feat: Picker组件优化性能

This commit is contained in:
xuqingkai 2023-08-21 20:25:03 +08:00
parent 09c75d4878
commit 24dd43f3a0
44 changed files with 1614 additions and 1234 deletions

View File

@ -222,7 +222,7 @@ function handleChange({ value }) {
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ----------------- | ---------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------- | --------------------- | -------- |
| v-model | 选中值,为 13 位时间戳或时间戳数组 | null / number / array | - | - | - |
| v-model | 选中值,为 13 位时间戳或时间戳数组 | null / number / array | - | - | - |
| type | 日期类型 | string | date / dates / datetime / week / month / daterange / datetimerange / weekrange / monthrange | date | - |
| min-date | 最小日期,为 13 位时间戳 | number | - | 当前日期往前推 6 个月 | - |
| max-date | 最大日期,为 13 位时间戳 | number | - | 当前日期往后推 6 个月 | - |
@ -239,15 +239,15 @@ function handleChange({ value }) {
## Events
| 事件名称 | 说明 | 参数 | 最低版本 |
| -------- | ---------------- | ------------------------ | -------- |
| 事件名称 | 说明 | 参数 | 最低版本 |
| -------- | ---------------- | ----------- | -------- |
| change | 绑定值变化时触发 | `{ value }` | - |
## Methods
| 方法名称 | 说明 | 参数 | 最低版本 |
| -------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | -------- |
| scrollIntoView | 使当前日期或者选中日期滚动到可视区域,并监听滚动,在面板从 隐藏状态(如 display: none 切换为展示状态时调用 | thresholds数字数组具体使用见 [Intersection Observer](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver) | - |
| 方法名称 | 说明 | 参数 | 最低版本 |
| -------------- | ------------------------------------------------------------------------------------------------------------ | ---- | -------- |
| scrollIntoView | 使当前日期或者选中日期滚动到可视区域,并监听滚动,在面板从 隐藏状态(如 display: none 切换为展示状态时调用 | - |
## 外部样式类

View File

@ -52,19 +52,20 @@
"upload:mp-dingtalk": "uni build -p mp-dingtalk && minici --platform dd"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-app-plus": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-components": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-h5": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-alipay": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-baidu": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-jd": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-lark": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-qq": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-app": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-app-plus": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-components": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-h5": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-alipay": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-baidu": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-jd": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-lark": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-qq": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-mp-xhs": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3081220230802001",
"vitepress": "^1.0.0-beta.6",
"vue": "^3.2.45",
"vue-i18n": "^9.1.9"
@ -73,10 +74,10 @@
"@commitlint/cli": "^17.4.4",
"@commitlint/config-conventional": "^17.4.4",
"@dcloudio/types": "^3.3.2",
"@dcloudio/uni-automator": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3080220230511001",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-automator": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3081220230802001",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3081220230802001",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3081220230802001",
"@types/node": "^18.14.6",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",

View File

@ -358,7 +358,7 @@
"mp-alipay": {
"allowsBounceVertical": "NO"
},
"navigationBarTitleText": "Picker 选择器视图"
"navigationBarTitleText": "Picker 选择器"
}
},
{

View File

@ -9,7 +9,7 @@
<wd-calendar label="日期时间选择" type="datetime" v-model="value4" />
<wd-calendar label="日期时间范围选择" type="datetimerange" v-model="value5" />
<wd-calendar label="周选择" type="week" v-model="value6" />
<wd-calendar label="月选择" type="month" v-model="value7" />
<wd-calendar label="月选择" type="month" :min-date="minDate" v-model="value7" />
<wd-calendar label="周范围选择" :first-day-of-week="1" type="weekrange" v-model="value8" />
<wd-calendar label="月范围选择" type="monthrange" v-model="value9" />
<wd-calendar label="日周月切换" :first-day-of-week="1" show-type-switch v-model="value10" />
@ -51,6 +51,8 @@ import { useToast } from '@/uni_modules/wot-design-uni'
import { dayjs } from '@/uni_modules/wot-design-uni'
import { ref } from 'vue'
const minDate = ref<number>(new Date(new Date().getFullYear() - 20, new Date().getMonth() - 6, new Date().getDate()).getTime())
const value1 = ref<number>(Date.now())
const value2 = ref<number[]>([Date.now() - 24 * 60 * 60 * 1000 * 3, Date.now()])
const value3 = ref<number[]>([])

View File

@ -23,7 +23,7 @@
</demo-block>
<demo-block title="自定义菜单选项" transparent>
<view class="custom-menu">
<wd-drop-menu style="flex: 1; min-width: 0">
<wd-drop-menu custom-style="flex: 1; min-width: 0">
<wd-drop-menu-item v-model="value4" :options="option1" @change="handleChange4" />
</wd-drop-menu>
<view style="flex: 1">

View File

@ -69,6 +69,7 @@ onReady(() => {
})
function handleResize(detail: Record<string, string | number>) {
console.log(detail)
const { height, width, top, right, bottom, left } = detail
lastHeight.value = sizeHeight.value
lastTop.value = sizeTop.value

View File

@ -42,6 +42,10 @@
}
}
:deep(.wd-action-sheet__popup){
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
}
@include b(action-sheet) {
background-color: $-color-white;
padding-bottom: 1px;

View File

@ -1,57 +1,59 @@
<template>
<wd-popup
custom-class="wd-action-sheet__popup"
:custom-style="`${(actions && actions.length) || (panels && panels.length) ? 'background: transparent;' : ''}`"
v-model="showPopup"
:duration="duration"
position="bottom"
:close-on-click-modal="closeOnClickModal"
:safe-area-inset-bottom="safeAreaInsetBottom"
:lazy-render="lazyRender"
@enter="handleOpen"
@close="close"
@after-enter="handleOpened"
@after-leave="handleClosed"
@clickmodal="handleClickModal"
:z-index="zIndex"
>
<view
class="wd-action-sheet"
:style="`${(actions && actions.length) || (panels && panels.length) ? 'margin: 0 10px 10px; border-radius: 16px;' : ''}`"
<view>
<wd-popup
custom-class="wd-action-sheet__popup"
:custom-style="`${(actions && actions.length) || (panels && panels.length) ? 'background: transparent;' : ''}`"
v-model="showPopup"
:duration="duration"
position="bottom"
:close-on-click-modal="closeOnClickModal"
:safe-area-inset-bottom="safeAreaInsetBottom"
:lazy-render="lazyRender"
@enter="handleOpen"
@close="close"
@after-enter="handleOpened"
@after-leave="handleClosed"
@clickmodal="handleClickModal"
:z-index="zIndex"
>
<view v-if="title" :class="`wd-action-sheet__header ${customHeaderClass}`">
{{ title }}
<wd-icon custom-class="wd-action-sheet__close" name="add" @click="close" />
</view>
<view class="wd-action-sheet__actions" v-if="actions && actions.length">
<button
v-for="(action, rowIndex) in actions"
:key="rowIndex"
:class="`wd-action-sheet__action ${action.disabled ? 'wd-action-sheet__action--disabled' : ''} ${
action.loading ? 'wd-action-sheet__action--loading' : ''
}`"
:style="`color: ${action.color}`"
@click="select(rowIndex, 'action')"
>
<wd-loading v-if="action.loading" size="20px" />
<view v-else class="wd-action-sheet__name">{{ action.name }}</view>
<view v-if="!action.loading && action.subname" class="wd-action-sheet__subname">{{ action.subname }}</view>
</button>
</view>
<view v-if="formatPanels && formatPanels.length">
<view v-for="(panel, rowIndex) in formatPanels" :key="rowIndex" class="wd-action-sheet__panels">
<view class="wd-action-sheet__panels-content">
<view v-for="(col, colIndex) in panel" :key="colIndex" class="wd-action-sheet__panel" @click="select(rowIndex, 'panels', colIndex)">
<image class="wd-action-sheet__panel-img" :src="(col as any).iconUrl" />
<view class="wd-action-sheet__panel-title">{{ (col as any).title }}</view>
<view
class="wd-action-sheet"
:style="`${(actions && actions.length) || (panels && panels.length) ? 'margin: 0 10px 10px; border-radius: 16px;' : ''}`"
>
<view v-if="title" :class="`wd-action-sheet__header ${customHeaderClass}`">
{{ title }}
<wd-icon custom-class="wd-action-sheet__close" name="add" @click="close" />
</view>
<view class="wd-action-sheet__actions" v-if="actions && actions.length">
<button
v-for="(action, rowIndex) in actions"
:key="rowIndex"
:class="`wd-action-sheet__action ${action.disabled ? 'wd-action-sheet__action--disabled' : ''} ${
action.loading ? 'wd-action-sheet__action--loading' : ''
}`"
:style="`color: ${action.color}`"
@click="select(rowIndex, 'action')"
>
<wd-loading v-if="action.loading" size="20px" />
<view v-else class="wd-action-sheet__name">{{ action.name }}</view>
<view v-if="!action.loading && action.subname" class="wd-action-sheet__subname">{{ action.subname }}</view>
</button>
</view>
<view v-if="formatPanels && formatPanels.length">
<view v-for="(panel, rowIndex) in formatPanels" :key="rowIndex" class="wd-action-sheet__panels">
<view class="wd-action-sheet__panels-content">
<view v-for="(col, colIndex) in panel" :key="colIndex" class="wd-action-sheet__panel" @click="select(rowIndex, 'panels', colIndex)">
<image class="wd-action-sheet__panel-img" :src="(col as any).iconUrl" />
<view class="wd-action-sheet__panel-title">{{ (col as any).title }}</view>
</view>
</view>
</view>
</view>
<slot />
<button v-if="cancelText" class="wd-action-sheet__cancel" @click="handleCancel">{{ cancelText }}</button>
</view>
<slot />
<button v-if="cancelText" class="wd-action-sheet__cancel" @click="handleCancel">{{ cancelText }}</button>
</view>
</wd-popup>
</wd-popup>
</view>
</template>
<script lang="ts">
export default {
@ -114,7 +116,7 @@ const props = withDefaults(defineProps<Props>(), {
closeOnClickModal: true,
duration: 200,
zIndex: 10,
lazyRender: false,
lazyRender: true,
safeAreaInsetBottom: true
})
const formatPanels = ref<Array<Panel> | Array<Array<Panel>>>([])

View File

@ -23,8 +23,10 @@
@include b(month) {
@include e(title) {
padding: 13px 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
height: 45px;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
}

View File

@ -82,7 +82,7 @@ const monthTitle = computed(() => {
}
})
const firstDayStyle = computed(() => {
return (index, date, firstDayOfWeek) => {
return (index: number, date: number, firstDayOfWeek: number) => {
return getFirstDayStyle(index, date, firstDayOfWeek)
}
})

View File

@ -8,16 +8,17 @@
</view>
<scroll-view
:class="`wd-month-panel__container ${!!timeType ? 'wd-month-panel__container--time' : ''}`"
:style="`height: ${!!timeType ? (panelHeight || 378) - 125 : panelHeight || 378}px`"
:style="`height: ${scrollHeight}px`"
scroll-y
:scroll-into-view="scrollIntoViewValue"
@scroll="monthScroll"
:scroll-top="scrollTop"
>
<view v-for="(item, index) in months(minDate, maxDate)" :key="index" :id="`month${index}`">
<month
class="month"
:type="type"
:date="item"
:data-date="item"
:date="item.date"
:data-date="item.date"
:value="value"
:min-date="minDate"
:max-date="maxDate"
@ -41,7 +42,6 @@
v-model="timeValue"
:columns="timeData"
:columns-height="125"
:show-picker="showPicker"
@change="handleTimeChange"
@pickstart="handlePickStart"
@pickend="handlePickEnd"
@ -64,8 +64,9 @@ export default {
<script lang="ts" setup>
import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue'
import { debounce, getType, isEqual } from '../../common/util'
import { compareMonth, formatMonthTitle, getMonths, getTimeData, getWeekLabel } from '../utils'
import { compareMonth, formatMonthTitle, getMonthEndDay, getMonths, getTimeData, getWeekLabel } from '../utils'
import Month from '../month/month.vue'
import { MonthInfo } from './type'
interface Props {
type: string
@ -84,35 +85,44 @@ interface Props {
// eslint-disable-next-line @typescript-eslint/ban-types
timeFilter?: Function
hideSecond: boolean
// picker
showPicker: boolean
}
const props = withDefaults(defineProps<Props>(), {
showPicker: true,
allowSameDay: false,
showPanelTitle: false,
hideSecond: false
})
const title = ref<string>('')
const scrollIntoViewValue = ref<string>('')
const scrollTop = ref<number>(0) //
const timeValue = ref<Array<string>>([])
const timeData = ref<Array<string | string[]>>([])
const timeType = ref<string>('') //
const innerValue = ref<string | number[]>('') //
let contentObserver: null | UniApp.IntersectionObserver = null
const instance = getCurrentInstance() as any
const weekLabel = computed(() => {
return (index: number) => {
return getWeekLabel(index)
return getWeekLabel(index - 1)
}
})
//
const scrollHeight = computed(() => {
const scrollHeight: number = timeType.value ? (props.panelHeight || 378) - 125 : props.panelHeight || 378
return scrollHeight
})
//
const months = computed(() => {
return (minDate, maxDate) => {
return getMonths(minDate, maxDate)
return (minDate: number, maxDate: number): MonthInfo[] => {
let months = getMonths(minDate, maxDate).map((month) => {
const offset = (7 + new Date(month).getDay() - props.firstDayOfWeek) % 7
const totalDay = getMonthEndDay(new Date(month).getFullYear(), new Date(month).getMonth() + 1)
return {
height: (offset + totalDay > 35 ? 64 * 6 : 64 * 5) + 45,
date: month
}
})
return months
}
})
@ -148,7 +158,6 @@ watch(
)
onMounted(() => {
initRect()
scrollIntoView()
})
@ -160,26 +169,6 @@ const handleChange = debounce((value) => {
})
}, 50)
function initRect(thresholds = [0, 0.7, 0.8, 0.9, 1]) {
if (!props.showPanelTitle) return
if (contentObserver != null) {
contentObserver.disconnect()
}
contentObserver = uni.createIntersectionObserver(instance, {
thresholds,
observeAll: true,
dataset: true
} as any)
contentObserver.relativeTo('.wd-month-panel__container')
contentObserver.observe('.month', (res: any) => {
if (res.boundingClientRect.top <= res.relativeRect.top) {
title.value = formatMonthTitle(res.dataset.date)
}
})
}
function scrollIntoView() {
setTimeout(() => {
let activeDate
@ -194,17 +183,18 @@ function scrollIntoView() {
activeDate = Date.now()
}
const months = getMonths(props.minDate, props.maxDate)
const monthsInfo = months.value(props.minDate, props.maxDate)
months.some((month, index) => {
if (compareMonth(month, activeDate) === 0) {
scrollIntoViewValue.value = ''
nextTick(() => {
scrollIntoViewValue.value = `month${index}`
})
return true
let top: number = 0
for (let index = 0; index < monthsInfo.length; index++) {
if (compareMonth(monthsInfo[index].date, activeDate) === 0) {
break
}
return false
top += monthsInfo[index] ? Number(monthsInfo[index].height) : 0
}
scrollTop.value = 0
nextTick(() => {
scrollTop.value = top
})
}, 50)
}
@ -275,10 +265,6 @@ function setTime(value, type) {
timeData.value = getTime(value, type) || []
timeValue.value = getTimeValue(value, type)
timeType.value = type
nextTick(() => {
initRect([0, 0.58, 0.69, 0.83, 1])
})
}
function handleDateChange({ value, type }) {
if (!isEqual(value, props.value)) {
@ -335,8 +321,31 @@ function handlePickEnd() {
emit('pickend')
}
const monthScroll = (event: { detail: { scrollTop: number } }) => {
const monthsInfo = months.value(props.minDate, props.maxDate)
if (monthsInfo.length <= 1) {
return
}
const scrollTop = Math.max(0, event.detail.scrollTop)
doSetSubtitle(scrollTop, monthsInfo)
}
/**
* 设置小标题
* scrollTop 滚动条位置
*/
function doSetSubtitle(scrollTop: number, monthsInfo: MonthInfo[]) {
let height: number = 0 //
for (let index = 0; index < monthsInfo.length; index++) {
height = height + monthsInfo[index].height
if (scrollTop < height + 45) {
title.value = formatMonthTitle(monthsInfo[index].date)
return
}
}
}
defineExpose({
initRect,
scrollIntoView
})
</script>

View File

@ -0,0 +1,7 @@
/**
*
*/
export interface MonthInfo {
date: number
height: number
}

View File

@ -112,15 +112,14 @@ export function getWeekLabel(index) {
* @param {timestamp} date
* @param {number} firstDayOfWeek
*/
export function getFirstDayStyle(index, date, firstDayOfWeek) {
export function getFirstDayStyle(index: number, date: number, firstDayOfWeek: number) {
if (firstDayOfWeek >= 7) {
firstDayOfWeek = firstDayOfWeek % 7
}
if (index !== 0) return ''
date = new Date(date)
const offset = (7 + date.getDay() - firstDayOfWeek) % 7
const offset = (7 + new Date(date).getDay() - firstDayOfWeek) % 7
return 'margin-left: ' + (100 / 7) * offset + '%'
}
@ -129,10 +128,8 @@ export function getFirstDayStyle(index, date, firstDayOfWeek) {
*
* @param {timestamp} date
*/
export function formatYearTitle(date) {
date = new Date(date)
const year = date.getFullYear()
export function formatYearTitle(date: number) {
const year = new Date(date).getFullYear()
return year + '年'
}
@ -160,7 +157,7 @@ export function getMonths(minDate, maxDate) {
* @param {timestamp} minDate
* @param {timestamp} maxDate
*/
export function getYears(minDate, maxDate) {
export function getYears(minDate: number, maxDate: number) {
const years: number[] = []
const year = new Date(minDate)
year.setMonth(0)

View File

@ -33,7 +33,6 @@
:panel-height="panelHeight"
:time-filter="timeFilter"
:hide-second="hideSecond"
:show-picker="showPicker"
@change="handleChange"
@pickstart="handlePickStart"
@pickend="handlePickEnd"
@ -43,7 +42,6 @@
<script lang="ts">
export default {
name: 'wd-calendar-view',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,
@ -92,8 +90,6 @@ interface Props {
timeFilter?: Function
// type 'datetime' 'datetimerange'
hideSecond: boolean
// picker
showPicker: boolean
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
@ -105,8 +101,7 @@ const props = withDefaults(defineProps<Props>(), {
showPanelTitle: true,
defaultTime: '00:00:00',
panelHeight: 378,
hideSecond: false,
showPicker: true
hideSecond: false
})
const formatDefauleTime = ref<number[]>([])
@ -128,9 +123,8 @@ watch(
const emit = defineEmits(['change', 'update:modelValue', 'pickstart', 'pickend'])
//
function scrollIntoView(thresholds) {
function scrollIntoView() {
const panel = getPanel()
panel.initRect && panel.initRect(thresholds)
panel.scrollIntoView && panel.scrollIntoView()
}

View File

@ -24,8 +24,10 @@
@include b(year) {
@include e(title) {
padding: 13px 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
height: 45px;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
}

View File

@ -0,0 +1,7 @@
/**
*
*/
export interface YearInfo {
date: number
height: number
}

View File

@ -1,18 +1,13 @@
<template>
<view class="wd-year-panel">
<view v-if="showPanelTitle" class="wd-year-panel__title">{{ title }}</view>
<scroll-view
class="wd-year-panel__container"
:style="`height: ${(panelHeight || 378) + (showPanelTitle ? 26 : 16)}px`"
scroll-y
:scroll-into-view="scrollIntoViewValue"
>
<scroll-view class="wd-year-panel__container" :style="`height: ${scrollHeight}px`" scroll-y @scroll="yearScroll" :scroll-top="scrollTop">
<view v-for="(item, index) in years(minDate, maxDate)" :key="index" :id="`year${index}`">
<year
class="year"
:type="type"
:date="item"
:data-date="item"
:date="item.date"
:data-date="item.date"
:value="value"
:min-date="minDate"
:max-date="maxDate"
@ -42,6 +37,7 @@ import { computed, getCurrentInstance, onMounted, ref, nextTick } from 'vue'
import { compareYear, formatYearTitle, getYears } from '../utils'
import { getType } from '../../common/util'
import Year from '../year/year.vue'
import { YearInfo } from './type'
interface Props {
type: string
@ -63,21 +59,34 @@ const props = withDefaults(defineProps<Props>(), {
})
const title = ref<string>('')
const scrollTop = ref<number>(0) //
const scrollIntoViewValue = ref<string>('')
let contentObserver: null | UniApp.IntersectionObserver = null
const instance = getCurrentInstance() as any
//
const scrollHeight = computed(() => {
const scrollHeight: number = (props.panelHeight || 378) + (props.showPanelTitle ? 26 : 16)
return scrollHeight
})
const years = computed(() => {
return (minDate, maxDate) => {
return getYears(minDate, maxDate)
return (minDate: number, maxDate: number): YearInfo[] => {
let years = getYears(minDate, maxDate).map((year) => {
return {
date: year,
height: 237
}
})
return years
}
})
const emit = defineEmits(['change'])
onMounted(() => {
initRect()
scrollIntoView()
})
@ -94,25 +103,6 @@ const requestAnimationFrame = (cb = () => void 0) => {
})
}
function initRect(thresholds = [0, 0.15, 0.7, 0.8, 0.9, 1]) {
if (!props.showPanelTitle) return
if (contentObserver != null) {
contentObserver.disconnect()
}
contentObserver = uni.createIntersectionObserver(instance, {
thresholds,
observeAll: true
})
contentObserver.relativeTo('.wd-year-panel__container')
contentObserver.observe('.year', (res: any) => {
if (res.boundingClientRect.top <= res.relativeRect.top) {
title.value = formatYearTitle(res.dataset.date)
}
})
}
function scrollIntoView() {
requestAnimationFrame().then(() => {
let activeDate
@ -127,21 +117,46 @@ function scrollIntoView() {
activeDate = Date.now()
}
const years = getYears(props.minDate, props.maxDate)
const yearsInfo = years.value(props.minDate, props.maxDate)
years.some((year, index) => {
if (compareYear(year, activeDate) === 0) {
scrollIntoViewValue.value = ''
nextTick(() => {
scrollIntoViewValue.value = `year${index}`
})
return true
let top: number = 0
for (let index = 0; index < yearsInfo.length; index++) {
if (compareYear(yearsInfo[index].date, activeDate) === 0) {
break
}
return false
top += yearsInfo[index] ? Number(yearsInfo[index].height) : 0
}
scrollTop.value = 0
nextTick(() => {
scrollTop.value = top
})
})
}
const yearScroll = (e: Event) => {
const yearsInfo = years.value(props.minDate, props.maxDate)
if (yearsInfo.length <= 1) {
return
}
const scrollTop = Math.max(0, (e.target as Element).scrollTop)
doSetSubtitle(scrollTop, yearsInfo)
}
/**
* 设置小标题
* scrollTop 滚动条位置
*/
function doSetSubtitle(scrollTop: number, yearsInfo: YearInfo[]) {
let height: number = 0 //
for (let index = 0; index < yearsInfo.length; index++) {
height = height + yearsInfo[index].height
if (scrollTop < height + 45) {
title.value = formatYearTitle(yearsInfo[index].date)
return
}
}
}
function handleDateChange({ value }) {
emit('change', {
value
@ -149,7 +164,6 @@ function handleDateChange({ value }) {
}
defineExpose({
initRect,
scrollIntoView
})
</script>

View File

@ -31,8 +31,6 @@
:safe-area-inset-bottom="safeAreaInsetBottom"
:z-index="zIndex"
@close="close"
@opened="setShowPicker(true)"
@closed="setShowPicker(false)"
>
<view class="wd-calendar__header">
<view v-if="!showTypeSwitch && shortcuts.length === 0" class="wd-calendar__title">{{ title || '选择日期' }}</view>
@ -86,7 +84,6 @@
:default-time="defaultTime"
:time-filter="timeFilter"
:hide-second="hideSecond"
:show-picker="showPicker"
:show-panel-title="!range(type)"
@change="handleChange"
/>
@ -101,7 +98,6 @@
<script lang="ts">
export default {
name: 'wd-calendar',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,
@ -274,7 +270,6 @@ const currentType = ref<CalendarType>('date')
const lastCurrentType = ref<CalendarType>('date')
const inited = ref<boolean>(false)
const rangeLabel = ref<Array<string>>([])
const showPicker = ref<boolean>(false)
const cell = useCell()
@ -342,10 +337,6 @@ const range = computed(() => {
const emit = defineEmits(['cancel', 'change', 'update:modelValue', 'confirm'])
const setShowPicker = debounce(function (show: boolean) {
showPicker.value = show
}, 100)
function scrollIntoView() {
calendarView.value && calendarView.value && calendarView.value.$.exposed.scrollIntoView()
}

View File

@ -6,7 +6,6 @@
<script lang="ts">
export default {
name: 'wd-checkbox-group',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

View File

@ -33,7 +33,6 @@
<script lang="ts">
export default {
name: 'wd-checkbox',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

View File

@ -80,7 +80,6 @@
<script lang="ts">
export default {
name: 'wd-col-picker',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,
@ -217,8 +216,6 @@ watch(
selectShowList.value = pickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, newSelectedList)[props.labelKey]
})
console.log(selectShowList.value, 'watch props.columns')
lastSelectList.value = newSelectedList
if (newSelectedList.length > 0) {
@ -372,8 +369,6 @@ function handleColChange(colIndex, item, index, callback?) {
selectShowList.value = pickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)[props.labelKey]
})
console.log(selectShowList.value, 'handleColChange')
callback()
}
},
@ -509,6 +504,11 @@ function handleAutoComplete() {
}
}
}
defineExpose({
close,
open
})
</script>
<style lang="scss" scoped>

View File

@ -0,0 +1,24 @@
export type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime'
/**
* @description 便 pickerView
* @param value
* @param type picker类型
* @return {Array} pickerValue
*/
export function getPickerValue(value, type) {
const values: number[] = []
const date = new Date(value)
if (type === 'time') {
const pair = value.split(':')
values.push(parseInt(pair[0]), parseInt(pair[1]))
} else {
values.push(date.getFullYear(), date.getMonth() + 1)
if (type === 'date') {
values.push(date.getDate())
} else if (type === 'datetime') {
values.push(date.getDate(), date.getHours(), date.getMinutes())
}
}
return values
}

View File

@ -9,7 +9,6 @@
:columnChange="columnChange"
:loading="loading"
:loading-color="loadingColor"
:show-picker="showPicker"
@change="onChange"
@pickstart="onPickStart"
@pickend="onPickEnd"
@ -19,7 +18,6 @@
<script lang="ts">
export default {
name: 'wd-datetime-picker-view',
behaviors: ['uni://form-field'],
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
@ -29,6 +27,7 @@ export default {
<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, onMounted, ref, watch } from 'vue'
import { debounce, getType, isDef, padZero, range } from '../common/util'
import { DateTimeType, getPickerValue } from './type'
//
/** @description 判断时间戳是否合法 */
@ -58,15 +57,11 @@ const getMonthEndDay = (year, month) => {
return 32 - new Date(year, month - 1, 32).getDate()
}
type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime'
interface Props {
customClass?: string
// type time Date
modelValue: string | number | Date
// PickerViewProps
// picker
showPicker: boolean
//
loading: boolean
loadingColor: string
@ -106,7 +101,6 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
customClass: '',
// PickerViewProps
showPicker: true,
//
loading: false,
loadingColor: '#4D80F0',
@ -131,7 +125,7 @@ const datePickerview = ref()
//
const innerValue = ref<null | number>(null)
// pickerViewcolumns
const columns = ref<Array<string | string[]>>([])
const columns = ref<Array<string | number>>([])
// pickerViewvalue
const pickerValue = ref<string | number | boolean | Array<string | number | boolean>>([])
// created hook
@ -432,29 +426,6 @@ function getBoundary(type, innerValue) {
}
}
/**
* @description 根据传入的值和类型获取当前的选项数组便于传入 pickerView
* @param value
* @param type picker类型
* @return {Array} pickerValue
*/
function getPickerValue(value, type) {
const values: number[] = []
const date = new Date(value)
if (type === 'time') {
const pair = value.split(':')
values.push(parseInt(pair[0]), parseInt(pair[1]))
} else {
values.push(date.getFullYear(), date.getMonth() + 1)
if (type === 'date') {
values.push(date.getDate())
} else if (type === 'datetime') {
values.push(date.getDate(), date.getHours(), date.getMinutes())
}
}
return values
}
/**
* @description 根据传入的value以及type初始化innerValue期间会使用format格式化数据
* @param value
@ -571,7 +542,7 @@ function onPickEnd() {
}
function getSelects() {
return datePickerview.value.getSelects()
return datePickerview.value && datePickerview.value.getSelects ? datePickerview.value.getSelects() : undefined
}
defineExpose({

View File

@ -33,14 +33,11 @@
<wd-popup
v-model="popupShow"
position="bottom"
:lazyRender="false"
:hide-when-close="true"
:hide-when-close="false"
:close-on-click-modal="closeOnClickModal"
:safe-area-inset-bottom="safeAreaInsetBottom"
:z-index="zIndex"
@close="onCancel"
@enter="setShowPicker(true)"
@leave="setShowPicker(false)"
custom-class="wd-picker__popup"
>
<!--toolBar-->
@ -89,7 +86,6 @@
:max-minute="maxMinute"
:min-minute="minMinute"
:start-symbol="true"
:show-picker="showPicker"
@change="onChangeStart"
@pickstart="onPickStart"
@pickend="onPickEnd"
@ -116,7 +112,6 @@
:max-minute="maxMinute"
:min-minute="minMinute"
:start-symbol="false"
:show-picker="showPicker"
@change="onChangeEnd"
@pickstart="onPickStart"
@pickend="onPickEnd"
@ -129,7 +124,6 @@
<script lang="ts">
export default {
name: 'wd-datetime-picker',
behaviors: ['uni://form-field'],
options: {
virtualHost: true,
addGlobalClass: true,
@ -140,14 +134,14 @@ export default {
<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, onMounted, ref, watch, nextTick } from 'vue'
import { debounce, deepClone, getType, isArray, isDef, isEqual, padZero } from '../common/util'
import { deepClone, getType, isArray, isDef, isEqual, padZero } from '../common/util'
import { useCell } from '../mixins/useCell'
type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime'
import { DateTimeType, getPickerValue } from '../wd-datetime-picker-view/type'
interface Props {
customClass?: string
customViewClass?: string
customLabelClass?: string
customValueClass?: string
customClass: string
customViewClass: string
customLabelClass: string
customValueClass: string
//
label?: string
//
@ -220,7 +214,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customViewClass: '',
customLabelVlass: '',
customLabelClass: '',
customValueClass: '',
//
placeholder: '请选择',
@ -273,7 +267,6 @@ const datetimePickerView1 = ref()
const showValue = ref<string | Date | Array<string | Date>>([])
const popupShow = ref<boolean>(false)
const showPicker = ref<boolean>(false)
const showStart = ref<boolean>(true)
const region = ref<boolean>(false)
const showTabLabel = ref<string[]>([])
@ -288,10 +281,6 @@ const { proxy } = getCurrentInstance() as any
const cell = useCell()
const setShowPicker = debounce(function (show: boolean) {
showPicker.value = show
}, 100)
watch(
() => props.modelValue,
(val, oldVal) => {
@ -377,7 +366,7 @@ watch(
watch(
() => props.defaultValue,
(val) => {
if (getType(val) === 'array') {
if (getType(val) === 'array' || region.value) {
innerValue.value = deepClone(getDefaultInnerValue(true))
endInnerValue.value = deepClone(getDefaultInnerValue(true, true))
} else {
@ -428,14 +417,11 @@ const customColumnFormatter = (picker) => {
})
})
}
console.log(mapColumns(columns))
return mapColumns(columns)
}
onBeforeMount(() => {
const { modelValue: value } = props
if (getType(value) === 'array') {
region.value = true
innerValue.value = deepClone(getDefaultInnerValue(true))
@ -446,9 +432,28 @@ onBeforeMount(() => {
})
onMounted(() => {
setShowValue()
setShowValue(false, false, true)
})
/**
* @description 根据传入的pickerpicker组件获取当前cell展示值
*/
function getSelects(picker: 'before' | 'after') {
let value = picker === 'before' ? innerValue.value : endInnerValue.value
let selected: number[] = []
if (value) {
selected = getPickerValue(value, props.type)
}
let selects = selected.map((value) => {
return {
[props.labelKey]: padZero(value),
[props.valueKey]: value
}
})
return selects
}
function noop() {}
function getDefaultInnerValue(isRegion?: boolean, isEnd?: boolean) {
@ -491,7 +496,7 @@ function showPopup() {
popupShow.value = true
innerValue.value = deepClone(getDefaultInnerValue())
}
setShowValue(true, false)
setShowValue(true, false, true)
}
/**
@ -531,6 +536,7 @@ function onChangeStart({ value }) {
*/
function onChangeEnd({ value }) {
endInnerValue.value = deepClone(value)
showTabLabel.value = [deepClone(showTabLabel.value[0]), setTabLabel(1)]
// emit('update:modelValue', [innerValue.value, value])
emit('change', {
@ -620,9 +626,13 @@ function setTabLabel(index: number = 0) {
if (region.value) {
let items = []
if (index === 0) {
items = (datetimePickerView.value && datetimePickerView.value.getSelects()) || []
items =
(datetimePickerView.value && datetimePickerView.value.getSelects && datetimePickerView.value.getSelects()) ||
(innerValue.value && getSelects('before'))
} else {
items = (datetimePickerView1.value && datetimePickerView1.value.getSelects()) || []
items =
(datetimePickerView1.value && datetimePickerView1.value.getSelects && datetimePickerView1.value.getSelects()) ||
(endInnerValue.value && getSelects('after'))
}
return defaultDisplayFormat(items, true)
}
@ -633,16 +643,25 @@ function setTabLabel(index: number = 0) {
* @param {Boolean} tab 是否修改tab展示值尽在区域选择情况下生效
* @param {Boolean} isConfirm 是否提交当前修改
*/
function setShowValue(tab = false, isConfirm = false) {
function setShowValue(tab: boolean = false, isConfirm: boolean = false, beforeMount: boolean = false) {
if (region.value) {
const items = (datetimePickerView.value && datetimePickerView.value.getSelects()) || []
const endItems = (datetimePickerView1.value && datetimePickerView1.value.getSelects()) || []
const items = beforeMount
? (innerValue.value && getSelects('before')) || []
: (datetimePickerView.value && datetimePickerView.value.getSelects && datetimePickerView.value.getSelects()) || []
const endItems = beforeMount
? (endInnerValue.value && getSelects('after')) || []
: (datetimePickerView1.value && datetimePickerView1.value.getSelects && datetimePickerView1.value.getSelects()) || []
showValue.value = tab
? showValue.value
: [props.modelValue[0] || isConfirm ? defaultDisplayFormat(items) : '', props.modelValue[1] || isConfirm ? defaultDisplayFormat(endItems) : '']
showTabLabel.value = [defaultDisplayFormat(items, true), defaultDisplayFormat(endItems, true)]
} else {
const items = (datetimePickerView.value && datetimePickerView.value.getSelects()) || []
const items = beforeMount
? (innerValue.value && getSelects('before')) || []
: (datetimePickerView.value && datetimePickerView.value.getSelects && datetimePickerView.value.getSelects()) || []
showValue.value = deepClone(props.modelValue || isConfirm ? defaultDisplayFormat(items) : '')
}
}

View File

@ -1,5 +1,5 @@
<template>
<view :class="`wd-drop-menu ${customClass}`" @click.stop="noop">
<view :style="customStyle" :class="`wd-drop-menu ${customClass}`" @click.stop="noop">
<view class="wd-drop-menu__list">
<view
v-for="(item, index) in titleList"
@ -34,7 +34,8 @@ import { getRect } from '../common/util'
type DropDirction = 'up' | 'down'
interface Props {
customClass?: string
customClass: string
customStyle: string
zIndex: number
direction: DropDirction
modal: boolean
@ -44,6 +45,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customStyle: '',
zIndex: 12,
direction: 'down',
modal: true,
@ -126,7 +128,7 @@ function fold(child?: any) {
const { top, bottom } = rect
if (props.direction === 'down') {
offset.value = bottom + 44
offset.value = bottom
// #ifdef H5
// H5沿
// H544px

View File

@ -26,7 +26,6 @@
<script lang="ts">
export default {
name: 'wd-input-number',
behaviors: ['uni://form-field'],
options: {
virtualHost: true,
addGlobalClass: true,

View File

@ -127,7 +127,6 @@
<script lang="ts">
export default {
name: 'wd-input',
behaviors: ['uni://form-field'],
options: {
virtualHost: true,
addGlobalClass: true,

View File

@ -21,6 +21,11 @@
}
}
:deep(.wd-message-box){
border-radius: $-message-box-radius;
overflow: hidden;
}
@include bdeep(message-box) {
border-radius: $-message-box-radius;
overflow: hidden;

View File

@ -1,49 +1,51 @@
<template>
<wd-popup
transition="zoom-in"
v-model="show"
:close-on-click-modal="closeOnClickModal"
:lazy-render="lazyRender"
custom-class="wd-message-box"
@clickmodal="toggleModal('modal')"
:z-index="zIndex"
:duration="200"
>
<view :class="rootClass">
<!--内容部分-->
<view :class="bodyClass">
<!--公共title-->
<view v-if="title" class="wd-message-box__title">
{{ title }}
<view>
<wd-popup
transition="zoom-in"
v-model="show"
:close-on-click-modal="closeOnClickModal"
:lazy-render="lazyRender"
custom-class="wd-message-box"
@clickmodal="toggleModal('modal')"
:z-index="zIndex"
:duration="200"
>
<view :class="rootClass">
<!--内容部分-->
<view :class="bodyClass">
<!--公共title-->
<view v-if="title" class="wd-message-box__title">
{{ title }}
</view>
<!--其它类型-->
<view class="wd-message-box__content">
<!--prompt类型-->
<block v-if="type === 'prompt'">
<!--输入框-->
<wd-input v-model="inputValue" :type="inputType" size="large" :placeholder="inputPlaceholder || '请输入'" @input="inputValChange" />
<!--错误提示-->
<view v-if="showErr" class="wd-message-box__input-error">
{{ inputError || '输入的数据不合法' }}
</view>
</block>
<!--使用插槽-->
<slot v-if="useSlot"></slot>
<!--使用文本-->
<block v-else>{{ msg }}</block>
</view>
</view>
<!--其它类型-->
<view class="wd-message-box__content">
<!--prompt类型-->
<block v-if="type === 'prompt'">
<!--输入框-->
<wd-input v-model="inputValue" :type="inputType" size="large" :placeholder="inputPlaceholder || '请输入'" @input="inputValChange" />
<!--错误提示-->
<view v-if="showErr" class="wd-message-box__input-error">
{{ inputError || '输入的数据不合法' }}
</view>
</block>
<!--使用插槽-->
<slot v-if="useSlot"></slot>
<!--使用文本-->
<block v-else>{{ msg }}</block>
<!--action按钮组合-->
<view :class="`wd-message-box__actions ${showCancelButton ? 'wd-message-box__flex' : 'wd-message-box__block'}`">
<wd-button type="info" block v-if="showCancelButton" custom-style="margin-right: 16px;" @click="toggleModal('cancel')">
{{ cancelButtonText || '取消' }}
</wd-button>
<wd-button block @click="toggleModal('confirm')">
{{ confirmButtonText || '确定' }}
</wd-button>
</view>
</view>
<!--action按钮组合-->
<view :class="`wd-message-box__actions ${showCancelButton ? 'wd-message-box__flex' : 'wd-message-box__block'}`">
<wd-button type="info" block v-if="showCancelButton" custom-style="margin-right: 16px;" @click="toggleModal('cancel')">
{{ cancelButtonText || '取消' }}
</wd-button>
<wd-button block @click="toggleModal('confirm')">
{{ confirmButtonText || '确定' }}
</wd-button>
</view>
</view>
</wd-popup>
</wd-popup>
</view>
</template>
<script lang="ts">
export default {

View File

@ -0,0 +1,81 @@
/*
* @Author: weisheng
* @Date: 2023-08-21 13:03:42
* @LastEditTime: 2023-08-21 17:24:57
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-picker-view\type.ts
*
*/
import { getType } from '../common/util'
export type ColumnItem = {
value?: string | number | boolean
label?: string
disabled?: boolean
}
/**
* @description props的value为array类型时提供format
* @param {Array<String|Number|Object>} array
* @param {string} valueKey valueKey
* @param {string} labelKey labelKey
*/
export function formatArray(array: Array<string | number | ColumnItem | Array<string | number | ColumnItem>>, valueKey: string, labelKey: string) {
let tempArray: Array<string | number | ColumnItem | Array<string | number | ColumnItem>> = array instanceof Array ? array : [array]
// 检测第一层的type
const firstLevelTypeList = new Set(array.map(getType))
/**
*
* 1.
* 2.object
* 3.
*/
if (firstLevelTypeList.size !== 1 && firstLevelTypeList.has('object')) {
// 原始值和引用类型不用混用
throw Error('The columns are correct')
}
/**
* array
* 便
*/
if (!(array[0] instanceof Array)) {
tempArray = [tempArray as Array<string | number | ColumnItem>]
}
// 经过上述处理都已经变成了二维数组再把每一项二维元素包装成object
const result: Array<Array<ColumnItem>> = (tempArray as Array<Array<string | number | ColumnItem>>).map((col) => {
return col.map((row) => {
const isObj = getType(row)
/* 针对原始值,包装成{valueKey,labelKey} */
if (isObj !== 'object') {
return {
[valueKey]: row,
[labelKey]: row
}
}
/**
* object的{valueKey,labelKey}
* labelKeyvalueKey代替
* valueKeylabelKey代替
* valueKey,labelKey都没有
*/
// eslint-disable-next-line no-prototype-builtins
if (!row.hasOwnProperty(valueKey) && !row.hasOwnProperty(labelKey)) {
// eslint-disable-next-line prettier/prettier
throw Error('Can\'t find valueKey and labelKey in columns')
}
// eslint-disable-next-line no-prototype-builtins
if (!row.hasOwnProperty(labelKey)) {
row[labelKey] = row[valueKey]
}
// eslint-disable-next-line no-prototype-builtins
if (!row.hasOwnProperty(valueKey)) {
row[valueKey] = row[labelKey]
}
return row as ColumnItem
})
})
return result
}

View File

@ -5,7 +5,6 @@
</view>
<view :style="`height: ${columnsHeight - 20}px;`">
<picker-view
v-if="showPicker"
mask-class="wd-picker-view__mask"
indicator-class="wd-picker-view__roller"
:indicator-style="`height: ${itemHeight}px;`"
@ -45,17 +44,10 @@ export default {
<script lang="ts" setup>
import { getCurrentInstance, ref, watch, nextTick } from 'vue'
import { deepClone, getType, isEqual, range } from '../common/util'
type ColumnItem = {
value?: string | number | boolean
label: string
disabled?: boolean
}
import { ColumnItem, formatArray } from './type'
interface Props {
customClass?: string
// picker
showPicker: boolean
//
loading: boolean
loadingColor: string
@ -68,7 +60,7 @@ interface Props {
//
modelValue: string | number | boolean | Array<string | number | boolean>
//
columns: Array<string | string[] | ColumnItem>
columns: Array<string | number | ColumnItem | Array<string | number | ColumnItem>>
//
// eslint-disable-next-line @typescript-eslint/ban-types
columnChange?: Function
@ -77,7 +69,6 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
customClass: '',
loading: false,
showPicker: true,
loadingColor: '#4D80F0',
columnsHeight: 217,
valueKey: 'value',
@ -86,7 +77,7 @@ const props = withDefaults(defineProps<Props>(), {
})
// render
const formatColumns = ref<Array<Record<string, any>[]>>([])
const formatColumns = ref<Array<Array<Record<string, any>>>>([])
const itemHeight = ref<number>(35)
const selectedIndex = ref<Array<number>>([]) //
const preSelectedIndex = ref<Array<number>>([])
@ -108,7 +99,7 @@ watch(
() => props.columns,
(newValue) => {
// propsformatColumnsvalueobserver
formatColumns.value = formatArray(newValue)
formatColumns.value = formatArray(newValue, props.valueKey, props.labelKey)
/**
* 每次改变都要重置选中项
* 1.选中每列的第一个
@ -172,7 +163,7 @@ function selectWithValue(value) {
if (type.indexOf(valueType) === -1) throw Error(`value must be one of ${type.toString()}`)
// propsformatColumns/ISSUE.md
if (formatColumns.value.length === 0) {
formatColumns.value = formatArray(props.columns)
formatColumns.value = formatArray(props.columns, props.valueKey, props.labelKey)
}
/**
* 1.单key转为Array<key>
@ -234,67 +225,6 @@ function selectWithIndex(columnIndex, rowIndex) {
return selectedIndex.value
}
/**
* @description 为props的value为array类型时提供format
* @param {Array<String|Number|Boolean|Object>} array
*/
function formatArray(array) {
array = array instanceof Array ? array : [array]
// type
const firstLevelTypeList = new Set(array.map(getType))
/**
* 存在三种类型的合法数据
* 1.数组是一维元素所有元素都是原始值
* 2.数组是一维元素所有元素都是object
* 3.数组是二维元素二维元素可以是任意内容
*/
if (firstLevelTypeList.size !== 1 && firstLevelTypeList.has('object')) {
//
throw Error('The columns are correct')
}
/**
* 数组的所有一维子元素都不是array说明是它是一个一维数组
* 所以需要把一维的转成二维这样方便统一处理
*/
if (!(array[0] instanceof Array)) {
array = [array]
}
// object
return array.map((col) =>
col.map((row) => {
const isObj = getType(row)
const { valueKey, labelKey } = props
/* 针对原始值,包装成{valueKey,labelKey} */
if (isObj !== 'object') {
return {
[valueKey]: row,
[labelKey]: row
}
}
/**
* 针对已经是object的修补成{valueKey,labelKey}
* 如果没有labelKey用valueKey代替
* 如果没有valueKey用labelKey代替
* valueKey,labelKey都没有直接抛错
*/
// eslint-disable-next-line no-prototype-builtins
if (!row.hasOwnProperty(valueKey) && !row.hasOwnProperty(labelKey)) {
// eslint-disable-next-line prettier/prettier
throw Error('Can\'t find valueKey and labelKey in columns')
}
// eslint-disable-next-line no-prototype-builtins
if (!row.hasOwnProperty(labelKey)) {
row[labelKey] = row[valueKey]
}
// eslint-disable-next-line no-prototype-builtins
if (!row.hasOwnProperty(valueKey)) {
row[valueKey] = row[labelKey]
}
return row
})
)
}
/**
* @description 滚动选中时更新选中的索引触发change事件
* @return {Number|Array<Number>}选中项的下标或者集合
@ -389,7 +319,6 @@ function getSelects() {
if (selects.length === 1) {
return selects[0]
}
return selects
}
@ -449,7 +378,7 @@ function setColumnData(columnIndex, data, jumpTo = 0) {
selectedIndex.value = selectWithIndex(columnIndex, jumpTo)
// formatArray
// ps v2.9.3flat
formatColumns.value[columnIndex] = formatArray(data).reduce((acc, val) => acc.concat(val), [])
formatColumns.value[columnIndex] = formatArray(data, props.valueKey, props.labelKey).reduce((acc, val) => acc.concat(val), [])
}
function getColumnsData() {

View File

@ -69,7 +69,6 @@
<script lang="ts">
export default {
name: 'wd-picker',
behaviors: ['uni://form-field'],
options: {
virtualHost: true,
addGlobalClass: true,
@ -80,8 +79,9 @@ export default {
<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, ref, watch, computed, onMounted } from 'vue'
import { deepClone, defaultDisplayFormat, getType } from '../common/util'
import { deepClone, defaultDisplayFormat, getType, isArray } from '../common/util'
import { useCell } from '../mixins/useCell'
import { ColumnItem, formatArray } from '../wd-picker-view/type'
interface Props {
customClass?: string
@ -126,9 +126,9 @@ interface Props {
// key
labelKey: string
//
modelValue: string | number | boolean | Array<string | number | boolean>
modelValue: string | number | Array<string | number>
//
columns: Array<string | string[]>
columns: Array<string | number | ColumnItem | Array<string | number | ColumnItem>>
//
// eslint-disable-next-line @typescript-eslint/ban-types
columnChange?: Function
@ -182,8 +182,8 @@ const popupShow = ref<boolean>(false)
//
const showValue = ref<string>('')
const pickerValue = ref<string | number | boolean | (string | number | boolean)[]>('')
const displayColumns = ref<Array<string | string[]>>([]) // pickerView columns
const resetColumns = ref<Array<string | string[]>>([]) // columns
const displayColumns = ref<Array<string | number | ColumnItem | Array<string | number | ColumnItem>>>([]) // pickerView columns
const resetColumns = ref<Array<string | number | ColumnItem | Array<string | number | ColumnItem>>>([]) // columns
const isPicking = ref<boolean>(false) // pickview
const hasConfirmed = ref<boolean>(false) //
@ -264,6 +264,7 @@ const { proxy } = getCurrentInstance() as any
const emit = defineEmits(['confirm', 'open', 'cancel', 'update:modelValue'])
onMounted(() => {
props.modelValue && setShowValue(getSelects(props.modelValue))
if (props.modelValue && pickerViewWd.value && pickerViewWd.value.getSelects) {
setShowValue(pickerViewWd.value!.getSelects())
}
@ -274,6 +275,51 @@ onBeforeMount(() => {
resetColumns.value = deepClone(props.columns)
})
/**
* @description 根据传入的valuepicker组件获取当前cell展示值
* @param {String|Number|Array<String|Number|Array<any>>}value
*/
function getSelects(value) {
const formatColumns = formatArray(props.columns, props.valueKey, props.labelKey)
if (props.columns.length === 0) return
// 使
if (value === '' || value === null || value === undefined || (getType(value) === 'array' && value.length === 0)) {
value = formatColumns.map((col) => {
return col[0][props.valueKey]
})
}
const valueType = getType(value)
const type = ['string', 'number', 'boolean', 'array']
if (type.indexOf(valueType) === -1) return []
/**
* 1.单key转为Array<key>
* 2.根据formatColumns的长度截取Array<String>保证下面的遍历不溢出
* 3.根据每列的key值找到选项中value为此key的下标并记录
*/
value = value instanceof Array ? value : [value]
value = value.slice(0, formatColumns.length)
if (value.length === 0) {
value = formatColumns.map(() => 0)
}
let selected: number[] = []
value.forEach((target, col) => {
let row = formatColumns[col].findIndex((row) => {
return row[props.valueKey].toString() === target.toString()
})
row = row === -1 ? 0 : row
selected.push(row)
})
const selects = selected.map((row, col) => formatColumns[col][row])
//
if (selects.length === 1) {
return selects[0]
}
return selects
}
//
function open() {
showPopup()

View File

@ -38,16 +38,18 @@ interface Props {
modal: boolean
zIndex: number
hideWhenClose: boolean
modalStyle?: string
modalStyle: string
safeAreaInsetBottom: boolean
modelValue: boolean
customStyle?: string
customStyle: string
lazyRender: boolean
customClass?: string
customClass: string
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customStyle: '',
modalStyle: '',
position: 'center',
closeOnClickModal: true,
modal: true,

View File

@ -6,7 +6,6 @@
<script lang="ts">
export default {
name: 'wd-radio-group',
behaviors: ['uni://form-field'],
options: {
virtualHost: true,
addGlobalClass: true,

View File

@ -21,7 +21,6 @@
<script lang="ts">
export default {
name: 'wd-rate',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

View File

@ -94,6 +94,13 @@ onMounted(() => {
width.value = lastWidth
//
onScrollHandler = () => {
const query = uni
.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(proxy)
// #endif
.select('.wd-resize__container')
.boundingClientRect()
query.exec(([res]) => {
// created
if (scrollEventCount.value++ === 0) {

View File

@ -50,7 +50,6 @@
<script lang="ts">
export default {
name: 'wd-search',
behaviors: ['uni://form-field'],
options: {
virtualHost: true,
addGlobalClass: true,

View File

@ -88,7 +88,6 @@
<script lang="ts">
export default {
name: 'wd-select-picker',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

View File

@ -44,7 +44,6 @@
<script lang="ts">
export default {
name: 'wd-slider',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

View File

@ -67,7 +67,9 @@
line-height: 1.1;
@include when(active) {
:deep(.wd-sort-button__icon-up, .wd-sort-button__icon-down) {
:deep(.wd-sort-button__icon-up),
:deep(.wd-sort-button__icon-down) {
transform: scale(calc((10 / 14)));
}
}

View File

@ -3,9 +3,9 @@
<view
:class="`wd-swipe-action ${customClass}`"
@click.stop="onClick()"
@touchstart="startDrag"
@touchmove="stopPropagation ? nothing : ''"
@touchmove.capture="onDrag"
@touchstart="startDrag"
@touchmove.prevent="onDrag"
@touchend="endDrag"
@touchcancel="endDrag"
>
@ -43,7 +43,7 @@ import { getRect } from '../common/util'
interface Props {
customClass?: string
// eslint-disable-next-line @typescript-eslint/ban-types
beforeClose: Function
beforeClose?: Function
disabled: boolean
modelValue: string
}
@ -51,8 +51,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
customStyle: '',
modelValue: 'close',
disabled: false,
beforeClose: (v) => v
disabled: false
})
const wrapperStyle = ref<string>('')
@ -280,7 +279,7 @@ function close(reason, position?: string) {
}
if (reason && position) {
props.beforeClose(reason, position)
props.beforeClose && props.beforeClose(reason, position)
}
swipeMove(0)

View File

@ -6,7 +6,6 @@
<script lang="ts">
export default {
name: 'wd-switch',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

View File

@ -42,7 +42,6 @@
<script lang="ts">
export default {
name: 'wd-upload',
behaviors: ['uni://form-field'],
options: {
addGlobalClass: true,
virtualHost: true,

1909
yarn.lock

File diff suppressed because it is too large Load Diff