From fdcf9931bed3da6f1faedff99f3a6b8202d75691 Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Tue, 15 Feb 2022 17:44:53 +0800 Subject: [PATCH] refactor(TimePicker): modelValue now is string array --- packages/vant/src/date-picker/DatePicker.tsx | 16 +--- packages/vant/src/date-picker/utils.ts | 3 +- packages/vant/src/picker/Picker.tsx | 10 ++- packages/vant/src/picker/utils.ts | 10 --- packages/vant/src/time-picker/README.md | 11 +-- packages/vant/src/time-picker/README.zh-CN.md | 11 +-- packages/vant/src/time-picker/TimePicker.tsx | 86 +++++++++---------- packages/vant/src/time-picker/demo/index.vue | 8 +- .../vant/src/time-picker/test/index.spec.tsx | 22 ++--- 9 files changed, 79 insertions(+), 98 deletions(-) diff --git a/packages/vant/src/date-picker/DatePicker.tsx b/packages/vant/src/date-picker/DatePicker.tsx index cc8a613c2..04bec47f6 100644 --- a/packages/vant/src/date-picker/DatePicker.tsx +++ b/packages/vant/src/date-picker/DatePicker.tsx @@ -14,7 +14,6 @@ import { isDate, padZero, isSameValue, - makeArrayProp, createNamespace, } from '../utils'; import { times, sharedProps, getMonthEndDay, pickerInheritKeys } from './utils'; @@ -28,7 +27,6 @@ const [name] = createNamespace('date-picker'); export type DatePickerColumnType = 'year' | 'month' | 'day'; const datePickerProps = extend({}, sharedProps, { - modelValue: makeArrayProp(), columnsType: { type: Array as PropType, default: () => ['year', 'month', 'day'], @@ -136,17 +134,11 @@ export default defineComponent({ }) ); - watch( - currentValues, - (newValues) => { - if (isSameValue(newValues, props.modelValue)) { - emit('update:modelValue', newValues); - } - }, - { - deep: true, + watch(currentValues, (newValues) => { + if (isSameValue(newValues, props.modelValue)) { + emit('update:modelValue', newValues); } - ); + }); watch( () => props.modelValue, diff --git a/packages/vant/src/date-picker/utils.ts b/packages/vant/src/date-picker/utils.ts index 4f2d701b0..a14098a09 100644 --- a/packages/vant/src/date-picker/utils.ts +++ b/packages/vant/src/date-picker/utils.ts @@ -1,9 +1,10 @@ -import { extend } from '../utils'; +import { extend, makeArrayProp } from '../utils'; import { pickerSharedProps } from '../picker/Picker'; import type { PropType } from 'vue'; import type { PickerOption } from '../picker'; export const sharedProps = extend({}, pickerSharedProps, { + modelValue: makeArrayProp(), filter: Function as PropType< (columnType: string, options: PickerOption[]) => PickerOption[] >, diff --git a/packages/vant/src/picker/Picker.tsx b/packages/vant/src/picker/Picker.tsx index 9cf93df6a..86259a0f3 100644 --- a/packages/vant/src/picker/Picker.tsx +++ b/packages/vant/src/picker/Picker.tsx @@ -113,9 +113,11 @@ export default defineComponent({ ); const setValue = (index: number, value: string | number) => { - const newValues = selectedValues.value.slice(0); - newValues[index] = value; - selectedValues.value = newValues; + if (selectedValues.value[index] !== value) { + const newValues = selectedValues.value.slice(0); + newValues[index] = value; + selectedValues.value = newValues; + } }; const onChange = (value: number | string, columnIndex: number) => { @@ -277,7 +279,7 @@ export default defineComponent({ selectedValues, (newValues) => { if (!isSameValue(newValues, props.modelValue)) { - emit('update:modelValue', selectedValues.value); + emit('update:modelValue', newValues); } }, { immediate: true } diff --git a/packages/vant/src/picker/utils.ts b/packages/vant/src/picker/utils.ts index 579bc5eb1..58f304746 100644 --- a/packages/vant/src/picker/utils.ts +++ b/packages/vant/src/picker/utils.ts @@ -94,16 +94,6 @@ export function getElementTranslateY(element: Element) { return Number(translateY); } -export function isValuesEqual( - valuesA: Array, - valuesB: Array -) { - return ( - valuesA.length === valuesB.length && - valuesA.every((value, index) => value === valuesB[index]) - ); -} - export function assignDefaultFields( fields: PickerFieldNames | undefined ): Required { diff --git a/packages/vant/src/time-picker/README.md b/packages/vant/src/time-picker/README.md index 1006900d2..ddb8bbcb8 100644 --- a/packages/vant/src/time-picker/README.md +++ b/packages/vant/src/time-picker/README.md @@ -29,7 +29,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:00'); + const currentTime = ref(['12', '00']); return { currentTime }; }, }; @@ -53,7 +53,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:35'); + const currentTime = ref(['12', '35']); return { currentTime }; }, }; @@ -76,7 +76,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:00'); + const currentTime = ref(['12', '00']); const formatter = (type, option) => { if (type === 'hour') { option.text += 'h'; @@ -108,7 +108,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:00'); + const currentTime = ref(['12', '00']); const filter = (type, options) => { if (type === 'minute') { @@ -131,7 +131,8 @@ export default { | Attribute | Description | Type | Default | | --- | --- | --- | --- | -| v-model | Current time | _string_ | - | +| v-model | Current time | _string[]_ | - | +| columns-type | Columns type | _string[]_ | `['hour', 'minute']` | | min-hour | Min hour for `time` type | _number \| string_ | `0` | | max-hour | Max hour for `time` type | _number \| string_ | `23` | | min-minute | Max minute for `time` type | _number \| string_ | `0` | diff --git a/packages/vant/src/time-picker/README.zh-CN.md b/packages/vant/src/time-picker/README.zh-CN.md index 33e8f0db3..c025dac1e 100644 --- a/packages/vant/src/time-picker/README.zh-CN.md +++ b/packages/vant/src/time-picker/README.zh-CN.md @@ -29,7 +29,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:00'); + const currentTime = ref(['12', '00']); return { currentTime }; }, }; @@ -53,7 +53,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:35'); + const currentTime = ref(['12', '35']); return { currentTime }; }, }; @@ -76,7 +76,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:00'); + const currentTime = ref(['12', '00']); const formatter = (type, option) => { if (type === 'hour') { option.text += '时'; @@ -108,7 +108,7 @@ import { ref } from 'vue'; export default { setup() { - const currentTime = ref('12:00'); + const currentTime = ref(['12', '00']); const filter = (type, options) => { if (type === 'minute') { return options.filter((option) => Number(option) % 10 === 0); @@ -130,7 +130,8 @@ export default { | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| v-model | 当前选中的时间 | _string_ | - | +| v-model | 当前选中的时间 | _string[]_ | - | +| columns-type | 选项类型,由 `hour`、`minute` 和 `second` 组成的数组 | _string[]_ | `['hour', 'minute']` | | min-hour | 可选的最小小时 | _number \| string_ | `0` | | max-hour | 可选的最大小时 | _number \| string_ | `23` | | min-minute | 可选的最小分钟 | _number \| string_ | `0` | diff --git a/packages/vant/src/time-picker/TimePicker.tsx b/packages/vant/src/time-picker/TimePicker.tsx index 7086238cc..a24d24ba5 100644 --- a/packages/vant/src/time-picker/TimePicker.tsx +++ b/packages/vant/src/time-picker/TimePicker.tsx @@ -3,17 +3,18 @@ import { watch, computed, defineComponent, + type PropType, type ExtractPropTypes, } from 'vue'; // Utils import { pick, - clamp, extend, padZero, createNamespace, makeNumericProp, + isSameValue, } from '../utils'; import { times, sharedProps, pickerInheritKeys } from '../date-picker/utils'; @@ -22,12 +23,17 @@ import { Picker } from '../picker'; const [name] = createNamespace('time-picker'); +export type TimePickerColumnType = 'hour' | 'minute'; + const timePickerProps = extend({}, sharedProps, { minHour: makeNumericProp(0), maxHour: makeNumericProp(23), minMinute: makeNumericProp(0), maxMinute: makeNumericProp(59), - modelValue: String, + columnsType: { + type: Array as PropType, + default: () => ['hour', 'minute'], + }, }); export type TimePickerProps = ExtractPropTypes; @@ -40,66 +46,54 @@ export default defineComponent({ emits: ['confirm', 'cancel', 'change', 'update:modelValue'], setup(props, { emit, slots }) { - const formatValues = (value?: string) => { - const { minHour, maxHour, maxMinute, minMinute } = props; + const currentValues = ref(props.modelValue); - if (value) { - const [hour, minute] = value.split(':'); - return [ - padZero(clamp(+hour, +minHour, +maxHour)), - padZero(clamp(+minute, +minMinute, +maxMinute)), - ]; - } - - return [padZero(minHour), padZero(minMinute)]; + const genOptions = ( + min: number, + max: number, + type: TimePickerColumnType + ) => { + const options = times(max - min + 1, (index) => { + const value = padZero(min + index); + return props.formatter(type, { + text: value, + value, + }); + }); + return props.filter ? props.filter(type, options) : options; }; - const currentValues = ref(formatValues(props.modelValue)); - - const ranges = computed(() => [ - { - type: 'hour' as const, - range: [+props.minHour, +props.maxHour], - }, - { - type: 'minute' as const, - range: [+props.minMinute, +props.maxMinute], - }, - ]); - const columns = computed(() => - ranges.value.map(({ type, range }) => { - const options = times(range[1] - range[0] + 1, (index) => { - const value = padZero(range[0] + index); - return props.formatter(type, { - text: value, - value, - }); - }); - - if (props.filter) { - return props.filter(type, options); + props.columnsType.map((type) => { + switch (type) { + case 'hour': + return genOptions(+props.minHour, +props.maxHour, 'hour'); + case 'minute': + return genOptions(+props.minMinute, +props.maxMinute, 'minute'); + default: + throw new Error( + `[Vant] DatePicker: unsupported columns type: ${type}` + ); } - - return options; }) ); watch( currentValues, - (values) => { - const newValue = values.join(':'); - if (newValue !== props.modelValue) { - emit('update:modelValue', newValue); + (newValues) => { + if (!isSameValue(newValues, props.modelValue)) { + emit('update:modelValue', newValues); } }, - { deep: true, immediate: true } + { immediate: true } ); watch( () => props.modelValue, - (newValue) => { - currentValues.value = formatValues(newValue); + (newValues) => { + if (!isSameValue(newValues, currentValues.value)) { + currentValues.value = newValues; + } } ); diff --git a/packages/vant/src/time-picker/demo/index.vue b/packages/vant/src/time-picker/demo/index.vue index 87391a0c0..c26152500 100644 --- a/packages/vant/src/time-picker/demo/index.vue +++ b/packages/vant/src/time-picker/demo/index.vue @@ -23,10 +23,10 @@ const t = useTranslate({ }, }); -const baseTime = ref('12:00'); -const rangeTime = ref('12:35'); -const filterTime = ref('12:00'); -const formatterTime = ref('12:00'); +const baseTime = ref(['12', '00']); +const rangeTime = ref(['12', '35']); +const filterTime = ref(['12', ' 00']); +const formatterTime = ref(['12', '00']); const filter = (type: string, options: PickerOption[]) => { if (type === 'minute') { diff --git a/packages/vant/src/time-picker/test/index.spec.tsx b/packages/vant/src/time-picker/test/index.spec.tsx index e8a6b920e..2435b1ef1 100644 --- a/packages/vant/src/time-picker/test/index.spec.tsx +++ b/packages/vant/src/time-picker/test/index.spec.tsx @@ -17,28 +17,28 @@ test('should format initial value correctly', () => { }, }); - expect(onUpdate.mock.calls[0]).toEqual(['22:58']); + expect(onUpdate.mock.calls[0]).toEqual([['22', '58']]); }); test('should update modelValue correctly when using max-hour and max-minute prop', () => { const onUpdate = jest.fn(); mount(TimePicker, { props: { - modelValue: '23:59', + modelValue: ['23', '59'], maxHour: 2, maxMinute: 2, 'onUpdate:modelValue': onUpdate, }, }); - expect(onUpdate.mock.calls[0]).toEqual(['02:02']); + expect(onUpdate.mock.calls[0]).toEqual([['00', '00']]); }); test('should filter options when using filter prop', () => { const wrapper = mount(TimePicker, { props: { filter, - modelValue: '12:00', + modelValue: ['12', '00'], }, }); @@ -54,7 +54,7 @@ test('should format options correctly when using formatter prop', async () => { props: { filter, formatter, - modelValue: '12:00', + modelValue: ['12', '00'], }, }); @@ -64,7 +64,7 @@ test('should format options correctly when using formatter prop', async () => { test('should emit confirm event after clicking the confirm button', () => { const wrapper = mount(TimePicker, { props: { - modelValue: '12:00', + modelValue: ['12', '00'], }, }); @@ -92,9 +92,9 @@ test('should emit cancel event after clicking the cancel button', () => { test('should emit confirm event correctly after setting values', async () => { const wrapper = mount(TimePicker); - await wrapper.setProps({ modelValue: '00:00' }); + await wrapper.setProps({ modelValue: ['00', '00'] }); await wrapper.find('.van-picker__confirm').trigger('click'); - await wrapper.setProps({ modelValue: '22:30' }); + await wrapper.setProps({ modelValue: ['22', '30'] }); await wrapper.find('.van-picker__confirm').trigger('click'); expect(wrapper.emitted('confirm')).toEqual([ @@ -125,14 +125,14 @@ test('should emit confirm event correctly after setting range', async () => { props: { minHour: 0, minMinute: 0, - modelValue: '12:00', + modelValue: ['12', '00'], 'onUpdate:modelValue': onUpdate, }, }); await wrapper.setProps({ minHour: 20, minMinute: 30 }); await wrapper.find('.van-picker__confirm').trigger('click'); - expect(onUpdate.mock.calls[0]).toEqual(['20:30']); + expect(onUpdate.mock.calls[0]).toEqual([['20', '30']]); expect(wrapper.emitted('confirm')).toEqual([ [ { @@ -149,7 +149,7 @@ test('should emit confirm event correctly after setting range', async () => { test('should emit confirm event correctly after setting smaller max-hour and max-minute', async () => { const wrapper = mount(TimePicker, { props: { - modelValue: '23:59', + modelValue: ['23', '59'], }, });