fix: 🐛 修复 DateTimePicker 区域选择时边界值处理错误的问题

This commit is contained in:
不如摸鱼去 2025-03-20 13:20:02 +08:00
parent 34a48783f3
commit 230e09ff2a

View File

@ -7,15 +7,15 @@
>
<!--文案-->
<view class="wd-picker__field" @click="showPopup">
<slot v-if="useDefaultSlot"></slot>
<slot v-if="$slots.default"></slot>
<view v-else :class="['wd-picker__cell', customCellClass]">
<view
v-if="label || useLabelSlot"
v-if="label || $slots.label"
:class="`wd-picker__label ${customLabelClass} ${isRequired ? 'is-required' : ''}`"
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
>
<block v-if="label">{{ label }}</block>
<slot v-else name="label"></slot>
<slot v-if="$slots.label" name="label"></slot>
<block v-else>{{ label }}</block>
</view>
<view class="wd-picker__body">
<view class="wd-picker__value-wraper">
@ -319,33 +319,105 @@ const isRequired = computed(() => {
})
/**
* @description 自定义列项筛选规则对每列单项进行禁用校验最终返回传入PickerView的columns数组
* @param {Component} picker datetimePickerView对象
* @return {Array} columns
* @description 处理时间边界值判断
* @param isStart 是否是开始时间
* @param columnType 当前列类型
* @param value 当前值
* @param currentArray 当前完整选择的数组
* @param boundary 边界值数组
* @returns 是否超出边界
*/
function handleBoundaryValue(
isStart: boolean,
columnType: DatetimePickerViewColumnType,
value: number,
currentArray: number[],
boundary: number[]
): boolean {
const { type } = props
switch (type) {
case 'datetime': {
const [year, month, date, hour, minute] = boundary
if (columnType === 'year') {
return isStart ? value > year : value < year
}
if (columnType === 'month' && currentArray[0] === year) {
return isStart ? value > month : value < month
}
if (columnType === 'date' && currentArray[0] === year && currentArray[1] === month) {
return isStart ? value > date : value < date
}
if (columnType === 'hour' && currentArray[0] === year && currentArray[1] === month && currentArray[2] === date) {
return isStart ? value > hour : value < hour
}
if (columnType === 'minute' && currentArray[0] === year && currentArray[1] === month && currentArray[2] === date && currentArray[3] === hour) {
return isStart ? value > minute : value < minute
}
break
}
case 'year-month': {
const [year, month] = boundary
if (columnType === 'year') {
return isStart ? value > year : value < year
}
if (columnType === 'month' && currentArray[0] === year) {
return isStart ? value > month : value < month
}
break
}
case 'year': {
const [year] = boundary
if (columnType === 'year') {
return isStart ? value > year : value < year
}
break
}
case 'date': {
const [year, month, date] = boundary
if (columnType === 'year') {
return isStart ? value > year : value < year
}
if (columnType === 'month' && currentArray[0] === year) {
return isStart ? value > month : value < month
}
if (columnType === 'date' && currentArray[0] === year && currentArray[1] === month) {
return isStart ? value > date : value < date
}
break
}
case 'time': {
const [hour, minute] = boundary
if (columnType === 'hour') {
return isStart ? value > hour : value < hour
}
if (columnType === 'minute' && currentArray[0] === hour) {
return isStart ? value > minute : value < minute
}
break
}
}
return false
}
/**
* @description 自定义列项筛选规则
*/
const customColumnFormatter: DatetimePickerViewColumnFormatter = (picker) => {
if (!picker) {
return []
}
if (!picker) return []
const { type } = props
const { startSymbol, formatter } = picker
// pickervalueinnerValue
const start = picker.correctValue(innerValue.value)
const end = picker.correctValue(endInnerValue.value)
/**
* 如果是上方picekr 那么将下方picker的值作为下边界
* 如果是下方picekr 那么将上方picker的值作为上边界
*/
const currentValue = startSymbol ? picker.getPickerValue(start, type) : picker.getPickerValue(end, type)
const boundary = startSymbol ? picker.getPickerValue(end, type) : picker.getPickerValue(start, type)
// picekr
const columns = picker.getOriginColumns()
// index
return columns.map((column, cIndex) => {
return columns.map((column, _) => {
return column.values.map((value) => {
const disabled = columnDisabledRules(startSymbol, columns, cIndex, value, currentValue, boundary)
const disabled = handleBoundaryValue(startSymbol, column.type, value, currentValue, boundary)
return {
label: formatter ? formatter(column.type, padZero(value)) : padZero(value),
value,
@ -415,9 +487,6 @@ function close() {
onCancel()
}
/**
* @description 展示popup小程序有个bug在picker-view弹出时设置value会触发change事件而且会将picker-view的value多次触发change重置为第一项
*/
function showPopup() {
if (props.disabled || props.readonly) return
@ -450,15 +519,34 @@ function tabChange() {
* @description datetimePickerView change 事件
*/
function onChangeStart({ value }: { value: number | string }) {
innerValue.value = deepClone(value)
if (!datetimePickerView.value) return
if (region.value && !datetimePickerView1.value) return
if (region.value) {
showTabLabel.value = [setTabLabel(), deepClone(showTabLabel.value[1])]
emit('change', {
value: [value, endInnerValue.value]
//
const currentArray = datetimePickerView.value.getPickerValue(value, props.type)
const boundaryArray = datetimePickerView.value.getPickerValue(endInnerValue.value, props.type)
const columns = datetimePickerView.value.getOriginColumns()
//
const needsAdjust = columns.some((column, index) => {
return handleBoundaryValue(true, column.type, currentArray[index], currentArray, boundaryArray)
})
innerValue.value = deepClone(needsAdjust ? endInnerValue.value : value)
nextTick(() => {
showTabLabel.value = [setTabLabel(), deepClone(showTabLabel.value[1])]
emit('change', {
value: [innerValue.value, endInnerValue.value]
})
// picker
datetimePickerView.value && datetimePickerView.value.setColumns(datetimePickerView.value.updateColumns())
datetimePickerView1.value && datetimePickerView1.value.setColumns(datetimePickerView1.value.updateColumns())
})
datetimePickerView.value && datetimePickerView.value.setColumns(datetimePickerView.value.updateColumns())
datetimePickerView1.value && datetimePickerView1.value.setColumns(datetimePickerView1.value.updateColumns())
} else {
//
innerValue.value = deepClone(value)
emit('change', {
value: innerValue.value
})
@ -469,13 +557,28 @@ function onChangeStart({ value }: { value: number | string }) {
* @description 区域选择 下方 datetimePickerView change 事件
*/
function onChangeEnd({ value }: { value: number | string }) {
endInnerValue.value = deepClone(value)
showTabLabel.value = [deepClone(showTabLabel.value[0]), setTabLabel(1)]
emit('change', {
value: [innerValue.value, value]
if (!datetimePickerView.value || !datetimePickerView1.value) return
const currentArray = datetimePickerView1.value.getPickerValue(value, props.type)
const boundaryArray = datetimePickerView1.value.getPickerValue(innerValue.value, props.type)
const columns = datetimePickerView1.value.getOriginColumns()
//
const needsAdjust = columns.some((column, index) => {
return handleBoundaryValue(false, column.type, currentArray[index], currentArray, boundaryArray)
})
endInnerValue.value = deepClone(needsAdjust ? innerValue.value : value)
nextTick(() => {
showTabLabel.value = [deepClone(showTabLabel.value[0]), setTabLabel(1)]
emit('change', {
value: [innerValue.value, endInnerValue.value]
})
// picker
datetimePickerView.value && datetimePickerView.value.setColumns(datetimePickerView.value.updateColumns())
datetimePickerView1.value && datetimePickerView1.value.setColumns(datetimePickerView1.value.updateColumns())
})
datetimePickerView.value && datetimePickerView.value.setColumns(datetimePickerView.value.updateColumns())
datetimePickerView1.value && datetimePickerView1.value.setColumns(datetimePickerView1.value.updateColumns())
}
/**
@ -620,11 +723,6 @@ function defaultDisplayFormat(items: Record<string, any>[], tabLabel: boolean =
// 使formatterdefaultDisplayFormat
if (props.formatter) {
/**
* 不建议使用 this.picker.picker.getLabels() 拉取
* 在初始展示时需要使用模拟 nextTick 来等待内部 pickerView 渲染后labels才可得到format后的labels
* 但使用模拟nextTick会造成页面延迟展示问题对用户感知来讲不友好因此不适用该方法
*/
const typeMaps = {
year: ['year'],
datetime: ['year', 'month', 'date', 'hour', 'minute'],
@ -653,100 +751,6 @@ function defaultDisplayFormat(items: Record<string, any>[], tabLabel: boolean =
}
}
/**
* @description 区域选择time禁用规则根据传入的位置标志以及日期类型 返回该节点是否禁用
* @param {Boolean} isStart 时间段类型 truestart | falseend
* @param {Array} column 当前遍历到的列数组
* @param {Number} cindex 外层column的索引对应每一个类型
* @param {Number / String} value 遍历到的当前值
* @param {Array} currentValue 当前选中的值 this.pickerValue
* @param {Array} boundary 当前变量的限制值决定禁用的边界值
* @return {Boolean} disabled
*/
function columnDisabledRules(
isStart: boolean,
columns: {
type: DatetimePickerViewColumnType
values: number[]
}[],
cIndex: number,
value: number,
currentValue: number[],
boundary: number[]
) {
const { type } = props
// 0 1 2 3 4
// startPicker , endPicker , startPicker boundary min->boundary
// endPicker , startPicker , endPicker boundary boundary->max
const column = columns[cIndex]
// ranges[0][0] minYear ranges[0][1] maxYear
if (type === 'datetime') {
const year = boundary[0]
const month = boundary[1]
const date = boundary[2]
const hour = boundary[3]
const minute = boundary[4]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
if (column.type === 'month' && currentValue[0] === year) {
return isStart ? value > month : value < month
}
if (column.type === 'date' && currentValue[0] === year && currentValue[1] === month) {
return isStart ? value > date : value < date
}
if (column.type === 'hour' && currentValue[0] === year && currentValue[1] === month && currentValue[2] === date) {
return isStart ? value > hour : value < hour
}
if (column.type === 'minute' && currentValue[0] === year && currentValue[1] === month && currentValue[2] === date && currentValue[3] === hour) {
return isStart ? value > minute : value < minute
}
} else if (type === 'year-month') {
const year = boundary[0]
const month = boundary[1]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
if (column.type === 'month' && currentValue[0] === year) {
return isStart ? value > month : value < month
}
} else if (type === 'year') {
const year = boundary[0]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
} else if (type === 'date') {
const year = boundary[0]
const month = boundary[1]
const date = boundary[2]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
if (column.type === 'month' && currentValue[0] === year) {
return isStart ? value > month : value < month
}
if (column.type === 'date' && currentValue[0] === year && currentValue[1] === month) {
return isStart ? value > date : value < date
}
} else if (type === 'time') {
const hour = boundary[0]
const minute = boundary[1]
if (column.type === 'hour') {
return isStart ? value > hour : value < hour
}
if (column.type === 'minute' && currentValue[0] === hour) {
return isStart ? value > minute : value < minute
}
}
return false
}
function setLoading(loading: boolean) {
isLoading.value = loading
}