From c00fa4cd7080ddb9efbe9d44e8456fd28b6d8aa3 Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Tue, 15 Feb 2022 15:54:24 +0800 Subject: [PATCH] chore: remove legacy DatetimePicker --- .../vant/src/datetime-picker/DatePicker.tsx | 338 --------------- .../src/datetime-picker/DatetimePicker.tsx | 49 --- packages/vant/src/datetime-picker/README.md | 367 ---------------- .../vant/src/datetime-picker/README.zh-CN.md | 398 ------------------ .../vant/src/datetime-picker/TimePicker.tsx | 184 -------- .../vant/src/datetime-picker/demo/index.vue | 153 ------- packages/vant/src/datetime-picker/index.ts | 13 - .../test/date-picker.legacy.ts | 220 ---------- .../test/datetime-picker.legacy.tsx | 116 ----- .../src/datetime-picker/test/demo.legacy.ts | 4 - .../src/datetime-picker/test/index.legacy.ts | 59 --- packages/vant/src/datetime-picker/types.ts | 27 -- packages/vant/src/datetime-picker/utils.ts | 78 ---- 13 files changed, 2006 deletions(-) delete mode 100644 packages/vant/src/datetime-picker/DatePicker.tsx delete mode 100644 packages/vant/src/datetime-picker/DatetimePicker.tsx delete mode 100644 packages/vant/src/datetime-picker/README.md delete mode 100644 packages/vant/src/datetime-picker/README.zh-CN.md delete mode 100644 packages/vant/src/datetime-picker/TimePicker.tsx delete mode 100644 packages/vant/src/datetime-picker/demo/index.vue delete mode 100644 packages/vant/src/datetime-picker/index.ts delete mode 100644 packages/vant/src/datetime-picker/test/date-picker.legacy.ts delete mode 100644 packages/vant/src/datetime-picker/test/datetime-picker.legacy.tsx delete mode 100644 packages/vant/src/datetime-picker/test/demo.legacy.ts delete mode 100644 packages/vant/src/datetime-picker/test/index.legacy.ts delete mode 100644 packages/vant/src/datetime-picker/types.ts delete mode 100644 packages/vant/src/datetime-picker/utils.ts diff --git a/packages/vant/src/datetime-picker/DatePicker.tsx b/packages/vant/src/datetime-picker/DatePicker.tsx deleted file mode 100644 index 426fd0ec6..000000000 --- a/packages/vant/src/datetime-picker/DatePicker.tsx +++ /dev/null @@ -1,338 +0,0 @@ -import { - ref, - watch, - computed, - nextTick, - onMounted, - defineComponent, - type PropType, -} from 'vue'; - -// Utils -import { - pick, - clamp, - extend, - isDate, - padZero, - makeStringProp, - createNamespace, -} from '../utils'; -import { - times, - sharedProps, - getTrueValue, - getMonthEndDay, - pickerInheritKeys, - proxyPickerMethods, -} from './utils'; - -// Composables -import { useExpose } from '../composables/use-expose'; - -// Components -import { Picker, PickerInstance } from '../picker'; - -// Types -import { DatetimePickerColumnType, DatetimePickerType } from './types'; - -const currentYear = new Date().getFullYear(); -const [name] = createNamespace('date-picker'); - -export default defineComponent({ - name, - - props: extend({}, sharedProps, { - type: makeStringProp('datetime'), - modelValue: Date, - columnsOrder: Array as PropType, - minDate: { - type: Date, - default: () => new Date(currentYear - 10, 0, 1), - validator: isDate, - }, - maxDate: { - type: Date, - default: () => new Date(currentYear + 10, 11, 31), - validator: isDate, - }, - }), - - emits: ['confirm', 'cancel', 'change', 'update:modelValue'], - - setup(props, { emit, slots }) { - const formatValue = (value?: Date) => { - if (isDate(value)) { - const timestamp = clamp( - value.getTime(), - props.minDate.getTime(), - props.maxDate.getTime() - ); - return new Date(timestamp); - } - - return undefined; - }; - - const picker = ref(); - const currentDate = ref(formatValue(props.modelValue)); - - const getBoundary = (type: 'max' | 'min', value: Date) => { - const boundary = props[`${type}Date` as const]; - const year = boundary.getFullYear(); - let month = 1; - let date = 1; - let hour = 0; - let minute = 0; - - if (type === 'max') { - month = 12; - date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1); - hour = 23; - minute = 59; - } - - if (value.getFullYear() === year) { - month = boundary.getMonth() + 1; - if (value.getMonth() + 1 === month) { - date = boundary.getDate(); - if (value.getDate() === date) { - hour = boundary.getHours(); - if (value.getHours() === hour) { - minute = boundary.getMinutes(); - } - } - } - } - - return { - [`${type}Year`]: year, - [`${type}Month`]: month, - [`${type}Date`]: date, - [`${type}Hour`]: hour, - [`${type}Minute`]: minute, - }; - }; - - const ranges = computed(() => { - const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = getBoundary( - 'max', - currentDate.value || props.minDate - ); - - const { minYear, minDate, minMonth, minHour, minMinute } = getBoundary( - 'min', - currentDate.value || props.minDate - ); - - let result: Array<{ type: DatetimePickerColumnType; range: number[] }> = [ - { - type: 'year', - range: [minYear, maxYear], - }, - { - type: 'month', - range: [minMonth, maxMonth], - }, - { - type: 'day', - range: [minDate, maxDate], - }, - { - type: 'hour', - range: [minHour, maxHour], - }, - { - type: 'minute', - range: [minMinute, maxMinute], - }, - ]; - - switch (props.type) { - case 'date': - result = result.slice(0, 3); - break; - case 'year-month': - result = result.slice(0, 2); - break; - case 'month-day': - result = result.slice(1, 3); - break; - case 'datehour': - result = result.slice(0, 4); - break; - } - - if (props.columnsOrder) { - const columnsOrder = props.columnsOrder.concat( - result.map((column) => column.type) - ); - result.sort( - (a, b) => columnsOrder.indexOf(a.type) - columnsOrder.indexOf(b.type) - ); - } - - return result; - }); - - const originColumns = computed(() => - ranges.value.map(({ type, range: rangeArr }) => { - let values = times(rangeArr[1] - rangeArr[0] + 1, (index) => - padZero(rangeArr[0] + index) - ); - - if (props.filter) { - values = props.filter(type, values); - } - - return { - type, - values, - }; - }) - ); - - const columns = computed(() => - originColumns.value.map((column) => ({ - values: column.values.map((value) => - props.formatter(column.type, value) - ), - })) - ); - - const updateColumnValue = () => { - const value = currentDate.value || props.minDate; - const { formatter } = props; - - const values = originColumns.value.map((column) => { - switch (column.type) { - case 'year': - return formatter('year', `${value.getFullYear()}`); - case 'month': - return formatter('month', padZero(value.getMonth() + 1)); - case 'day': - return formatter('day', padZero(value.getDate())); - case 'hour': - return formatter('hour', padZero(value.getHours())); - case 'minute': - return formatter('minute', padZero(value.getMinutes())); - default: - return ''; - } - }); - - nextTick(() => { - picker.value?.setValues(values); - }); - }; - - const updateInnerValue = () => { - const { type } = props; - const indexes = picker.value!.getIndexes(); - - const getValue = (type: DatetimePickerColumnType) => { - let index = 0; - originColumns.value.forEach((column, columnIndex) => { - if (type === column.type) { - index = columnIndex; - } - }); - const { values } = originColumns.value[index]; - return getTrueValue(values[indexes[index]]); - }; - - let year; - let month; - let day; - if (type === 'month-day') { - year = (currentDate.value || props.minDate).getFullYear(); - month = getValue('month'); - day = getValue('day'); - } else { - year = getValue('year'); - month = getValue('month'); - day = type === 'year-month' ? 1 : getValue('day'); - } - - const maxDay = getMonthEndDay(year, month); - day = day > maxDay ? maxDay : day; - - let hour = 0; - let minute = 0; - - if (type === 'datehour') { - hour = getValue('hour'); - } - - if (type === 'datetime') { - hour = getValue('hour'); - minute = getValue('minute'); - } - - const value = new Date(year, month - 1, day, hour, minute); - currentDate.value = formatValue(value); - }; - - const onConfirm = () => { - emit('update:modelValue', currentDate.value); - emit('confirm', currentDate.value); - }; - - const onCancel = () => emit('cancel'); - - const onChange = () => { - updateInnerValue(); - nextTick(() => { - nextTick(() => emit('change', currentDate.value)); - }); - }; - - onMounted(() => { - updateColumnValue(); - nextTick(updateInnerValue); - }); - - watch(columns, updateColumnValue); - - watch(currentDate, (value, oldValue) => - emit('update:modelValue', oldValue ? value : null) - ); - - watch(() => [props.filter, props.maxDate], updateInnerValue); - - watch( - () => props.minDate, - () => { - nextTick(updateInnerValue); - } - ); - - watch( - () => props.modelValue, - (value) => { - value = formatValue(value); - - if (value && value.valueOf() !== currentDate.value?.valueOf()) { - currentDate.value = value; - } - } - ); - - useExpose({ - getPicker: () => - picker.value && proxyPickerMethods(picker.value, updateInnerValue), - }); - - return () => ( - - ); - }, -}); diff --git a/packages/vant/src/datetime-picker/DatetimePicker.tsx b/packages/vant/src/datetime-picker/DatetimePicker.tsx deleted file mode 100644 index 257454267..000000000 --- a/packages/vant/src/datetime-picker/DatetimePicker.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { ref, defineComponent, type ExtractPropTypes } from 'vue'; -import { pick, extend, createNamespace } from '../utils'; -import { useExpose } from '../composables/use-expose'; -import TimePicker from './TimePicker'; -import DatePicker from './DatePicker'; -import { DatetimePickerInstance } from './types'; - -const [name, bem] = createNamespace('datetime-picker'); - -const timePickerPropKeys = Object.keys(TimePicker.props); -const datePickerPropKeys = Object.keys(DatePicker.props); -const datetimePickerProps = extend({}, TimePicker.props, DatePicker.props, { - modelValue: [String, Date], -}); - -export type DatetimePickerProps = ExtractPropTypes; - -export default defineComponent({ - name, - - props: datetimePickerProps, - - setup(props, { attrs, slots }) { - const root = ref(); - - useExpose({ - getPicker: () => root.value?.getPicker(), - }); - - return () => { - const isTimePicker = props.type === 'time'; - const Component = isTimePicker ? TimePicker : DatePicker; - const inheritProps = pick( - props, - isTimePicker ? timePickerPropKeys : datePickerPropKeys - ); - - return ( - - ); - }; - }, -}); diff --git a/packages/vant/src/datetime-picker/README.md b/packages/vant/src/datetime-picker/README.md deleted file mode 100644 index d95713ab6..000000000 --- a/packages/vant/src/datetime-picker/README.md +++ /dev/null @@ -1,367 +0,0 @@ -# DatetimePicker - -### Intro - -Used to select time, support date and time dimensions, usually used with the [Popup](#/en-US/popup) component. - -### Install - -Register component globally via `app.use`, refer to [Component Registration](#/en-US/advanced-usage#zu-jian-zhu-ce) for more registration ways. - -```js -import { createApp } from 'vue'; -import { DatetimePicker } from 'vant'; - -const app = createApp(); -app.use(DatetimePicker); -``` - -## Usage - -### Choose Date - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date(2021, 0, 17)); - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - currentDate, - }; - }, -}; -``` - -### Choose Year-Month - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - - const formatter = (type, val) => { - if (type === 'year') { - return `${val} Year`; - } - if (type === 'month') { - return `${val} Month`; - } - return val; - }; - - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - formatter, - currentDate, - }; - }, -}; -``` - -### Choose Month-Day - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - - const formatter = (type, val) => { - if (type === 'month') { - return `${val} Month`; - } - if (type === 'day') { - return `${val} Day`; - } - return val; - }; - - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - formatter, - currentDate, - }; - }, -}; -``` - -### Choose Time - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentTime = ref('12:00'); - return { currentTime }; - }, -}; -``` - -### Choose DateTime - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - currentDate, - }; - }, -}; -``` - -### Choose DateHour - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - currentDate, - }; - }, -}; -``` - -### Option Filter - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentTime = ref('12:00'); - - const filter = (type, options) => { - if (type === 'minute') { - return options.filter((option) => Number(option) % 5 === 0); - } - return options; - }; - - return { - filter, - currentTime, - }; - }, -}; -``` - -### Columns Order - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - - const formatter = (type, val) => { - if (type === 'year') { - return val + ' Year'; - } - if (type === 'month') { - return val + ' Month'; - } - if (type === 'day') { - return val + ' Day'; - } - return val; - }; - - return { - formatter, - currentDate, - }; - }, -}; -``` - -## API - -### Props - -| Attribute | Description | Type | Default | -| --- | --- | --- | --- | -| type | Can be set to `date` `time`
`year-month` `month-day` `datehour` | _string_ | `datetime` | -| title | Toolbar title | _string_ | `''` | -| confirm-button-text | Text of confirm button | _string_ | `Confirm` | -| cancel-button-text | Text of cancel button | _string_ | `Cancel` | -| show-toolbar | Whether to show toolbar | _boolean_ | `true` | -| loading | Whether to show loading prompt | _boolean_ | `false` | -| readonly | Whether to be readonly | _boolean_ | `false` | -| filter | Option filter | _(type: string, values: string[]) => string[]_ | - | -| formatter | Option text formatter | _(type: string, value: string) => string_ | - | -| columns-order | Array for ordering columns, where item can be set to
`year`, `month`, `day`, `hour` and `minute` | _string[]_ | - | -| option-height | Option height, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `44` | -| visible-option-num | Count of visible columns | _number \| string_ | `6` | -| swipe-duration | Duration of the momentum animation,unit `ms` | _number \| string_ | `1000` | - -### DatePicker Props - -Following props are supported when the type is date or datetime - -| Attribute | Description | Type | Default | -| --------- | ----------- | ------ | ------------------------------ | -| min-date | Min date | _Date_ | Ten years ago on January 1 | -| max-date | Max date | _Date_ | Ten years later on December 31 | - -### TimePicker Props - -Following props are supported when the type is time - -| Attribute | Description | Type | Default | -| ---------- | -------------------------- | ------------------ | ------- | -| 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` | -| max-minute | Max minute for `time` type | _number \| string_ | `59` | - -### Events - -| Event | Description | Arguments | -| ------- | ------------------------------------------ | -------------------- | -| change | Emitted when value changed | value: current value | -| confirm | Emitted when the confirm button is clicked | value: current value | -| cancel | Emitted when the cancel button is clicked | - | - -### Slots - -| Name | Description | SlotProps | -| -------------- | ---------------------------- | -------------------------- | -| default | Custom toolbar content | - | -| title | Custom title | - | -| confirm | Custom confirm button text | - | -| cancel | Custom cancel button text | - | -| option | Custom option content | _option: string \| object_ | -| columns-top | Custom content above columns | - | -| columns-bottom | Custom content below columns | - | - -### Methods - -Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get DatetimePicker instance and call instance methods. - -| Name | Description | Attribute | Return value | -| --------- | ------------------- | --------- | ------------ | -| getPicker | get Picker instance | - | - | - -### Types - -The component exports the following type definitions: - -```ts -import type { - DatetimePickerType, - DatetimePickerProps, - DatetimePickerInstance, -} from 'vant'; -``` - -`DatetimePickerInstance` is the type of component instance: - -```ts -import { ref } from 'vue'; -import type { DatetimePickerInstance } from 'vant'; - -const datetimePickerRef = ref(); - -datetimePickerRef.value?.getPicker(); -``` diff --git a/packages/vant/src/datetime-picker/README.zh-CN.md b/packages/vant/src/datetime-picker/README.zh-CN.md deleted file mode 100644 index ad99fae8f..000000000 --- a/packages/vant/src/datetime-picker/README.zh-CN.md +++ /dev/null @@ -1,398 +0,0 @@ -# DatetimePicker 时间选择 - -### 介绍 - -时间选择器,支持日期、年月、时分等维度,通常与[弹出层](#/zh-CN/popup)组件配合使用。 - -### 引入 - -通过以下方式来全局注册组件,更多注册方式请参考[组件注册](#/zh-CN/advanced-usage#zu-jian-zhu-ce)。 - -```js -import { createApp } from 'vue'; -import { DatetimePicker } from 'vant'; - -const app = createApp(); -app.use(DatetimePicker); -``` - -## 代码演示 - -### 选择年月日 - -DatetimePicker 通过 type 属性来定义需要选择的时间类型,type 为 `date` 表示选择年月日。通过 min-date 和 max-date 属性可以确定可选的时间范围。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date(2021, 0, 17)); - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - currentDate, - }; - }, -}; -``` - -### 选择年月 - -将 type 设置为 `year-month` 即可选择年份和月份。通过传入 `formatter` 函数,可以对选项文字进行格式化处理。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - - const formatter = (type, val) => { - if (type === 'year') { - return `${val}年`; - } - if (type === 'month') { - return `${val}月`; - } - return val; - }; - - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - formatter, - currentDate, - }; - }, -}; -``` - -### 选择月日 - -将 type 设置为 `month-day` 即可选择月份和日期。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - - const formatter = (type, val) => { - if (type === 'month') { - return `${val}月`; - } - if (type === 'day') { - return `${val}日`; - } - return val; - }; - - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - formatter, - currentDate, - }; - }, -}; -``` - -### 选择时间 - -将 type 设置为 `time` 即可选择时间(小时和分钟)。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentTime = ref('12:00'); - return { currentTime }; - }, -}; -``` - -### 选择完整时间 - -将 type 设置为 `datetime` 即可选择完整时间,包括年月日和小时、分钟。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - currentDate, - }; - }, -}; -``` - -### 选择年月日小时 - -将 type 设置为 `datehour` 即可选择日期和小时,包括年月日和小时。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - return { - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - currentDate, - }; - }, -}; -``` - -### 选项过滤器 - -通过传入 `filter` 函数,可以对选项数组进行过滤,实现自定义时间间隔。 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentTime = ref('12:00'); - - const filter = (type, options) => { - if (type === 'minute') { - return options.filter((option) => Number(option) % 5 === 0); - } - return options; - }; - - return { - filter, - currentTime, - }; - }, -}; -``` - -### 自定义列排序 - -```html - -``` - -```js -import { ref } from 'vue'; - -export default { - setup() { - const currentDate = ref(new Date()); - - const formatter = (type, val) => { - if (type === 'year') { - return val + '年'; - } - if (type === 'month') { - return val + '月'; - } - if (type === 'day') { - return val + '日'; - } - return val; - }; - - return { - formatter, - currentDate, - }; - }, -}; -``` - -## API - -### Props - -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| type | 时间类型,可选值为 `date` `time`
`year-month` `month-day` `datehour` | _string_ | `datetime` | -| title | 顶部栏标题 | _string_ | `''` | -| confirm-button-text | 确认按钮文字 | _string_ | `确认` | -| cancel-button-text | 取消按钮文字 | _string_ | `取消` | -| show-toolbar | 是否显示顶部栏 | _boolean_ | `true` | -| loading | 是否显示加载状态 | _boolean_ | `false` | -| readonly | 是否为只读状态,只读状态下无法切换选项 | _boolean_ | `false` | -| filter | 选项过滤函数 | _(type: string, values: string[]) => string[]_ | - | -| formatter | 选项格式化函数 | _(type: string, value: string) => string_ | - | -| columns-order | 自定义列排序数组, 子项可选值为
`year`、`month`、`day`、`hour`、`minute` | _string[]_ | - | -| option-height | 选项高度,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `44` | -| visible-option-num | 可见的选项个数 | _number \| string_ | `6` | -| swipe-duration | 快速滑动时惯性滚动的时长,单位`ms` | _number \| string_ | `1000` | - -### DatePicker Props - -当时间选择器类型为 date 或 datetime 时,支持以下 props: - -| 参数 | 说明 | 类型 | 默认值 | -| -------- | -------------------------- | ------ | ------ | -| min-date | 可选的最小时间,精确到分钟 | _Date_ | 十年前 | -| max-date | 可选的最大时间,精确到分钟 | _Date_ | 十年后 | - -### TimePicker Props - -当时间选择器类型为 time 时,支持以下 props: - -| 参数 | 说明 | 类型 | 默认值 | -| ---------- | -------------- | ------------------ | ------ | -| min-hour | 可选的最小小时 | _number \| string_ | `0` | -| max-hour | 可选的最大小时 | _number \| string_ | `23` | -| min-minute | 可选的最小分钟 | _number \| string_ | `0` | -| max-minute | 可选的最大分钟 | _number \| string_ | `59` | - -### Events - -| 事件名 | 说明 | 回调参数 | -| ------- | ------------------------ | --------------------- | -| change | 当值变化时触发的事件 | value: 当前选中的时间 | -| confirm | 点击完成按钮时触发的事件 | value: 当前选中的时间 | -| cancel | 点击取消按钮时触发的事件 | - | - -### Slots - -| 名称 | 说明 | 参数 | -| -------------- | ---------------------- | -------------------------- | -| default | 自定义整个顶部栏的内容 | - | -| title | 自定义标题内容 | - | -| confirm | 自定义确认按钮内容 | - | -| cancel | 自定义取消按钮内容 | - | -| option | 自定义选项内容 | _option: string \| object_ | -| columns-top | 自定义选项上方内容 | - | -| columns-bottom | 自定义选项下方内容 | - | - -### 方法 - -通过 ref 可以获取到 DatetimePicker 实例并调用实例方法,详见[组件实例方法](#/zh-CN/advanced-usage#zu-jian-shi-li-fang-fa)。 - -| 方法名 | 说明 | 参数 | 返回值 | -| --- | --- | --- | --- | -| getPicker | 获取 Picker 实例,用于调用 Picker 的[实例方法](#/zh-CN/picker#fang-fa) | - | - | - -### 类型定义 - -组件导出以下类型定义: - -```ts -import type { - DatetimePickerType, - DatetimePickerProps, - DatetimePickerInstance, -} from 'vant'; -``` - -`DatetimePickerInstance` 是组件实例的类型,用法如下: - -```ts -import { ref } from 'vue'; -import type { DatetimePickerInstance } from 'vant'; - -const datetimePickerRef = ref(); - -datetimePickerRef.value?.getPicker(); -``` - -## 常见问题 - -### 设置 min-date 或 max-date 后出现页面卡死的情况? - -请注意不要在模板中直接使用类似`min-date="new Date()"`的写法,这样会导致每次渲染组件时传入一个新的 Date 对象,而传入新的数据会触发下一次渲染,从而陷入死循环。 - -正确的做法是将`min-date`作为一个数据定义在`data`函数中。 - -### 在 iOS 系统上初始化组件失败? - -如果你遇到了在 iOS 上无法渲染组件的问题,请确认在创建 Date 对象时没有使用`new Date('2020-01-01')`这样的写法,iOS 不支持以中划线分隔的日期格式,正确写法是`new Date('2020/01/01')`。 - -对此问题的详细解释:[stackoverflow](https://stackoverflow.com/questions/13363673/javascript-date-is-invalid-on-ios)。 - -### 在桌面端无法操作组件? - -参见[桌面端适配](#/zh-CN/advanced-usage#zhuo-mian-duan-gua-pei)。 - -### 是否有年份或月份选择器? - -如果仅需要选择年份或者月份,建议直接使用 [Picker](#/zh-CN/picker) 组件。 diff --git a/packages/vant/src/datetime-picker/TimePicker.tsx b/packages/vant/src/datetime-picker/TimePicker.tsx deleted file mode 100644 index aac16aec1..000000000 --- a/packages/vant/src/datetime-picker/TimePicker.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { - ref, - watch, - computed, - nextTick, - onMounted, - defineComponent, -} from 'vue'; - -// Utils -import { - pick, - clamp, - extend, - padZero, - createNamespace, - makeNumericProp, -} from '../utils'; -import { - times, - sharedProps, - pickerInheritKeys, - proxyPickerMethods, -} from './utils'; - -// Composables -import { useExpose } from '../composables/use-expose'; - -// Components -import { Picker, PickerInstance } from '../picker'; - -const [name] = createNamespace('time-picker'); - -export default defineComponent({ - name, - - props: extend({}, sharedProps, { - minHour: makeNumericProp(0), - maxHour: makeNumericProp(23), - minMinute: makeNumericProp(0), - maxMinute: makeNumericProp(59), - modelValue: String, - }), - - emits: ['confirm', 'cancel', 'change', 'update:modelValue'], - - setup(props, { emit, slots }) { - const formatValue = (value?: string) => { - const { minHour, maxHour, maxMinute, minMinute } = props; - - if (!value) { - value = `${padZero(minHour)}:${padZero(minMinute)}`; - } - - let [hour, minute] = value.split(':'); - hour = padZero(clamp(+hour, +minHour, +maxHour)); - minute = padZero(clamp(+minute, +minMinute, +maxMinute)); - - return `${hour}:${minute}`; - }; - - const picker = ref(); - const currentDate = ref(formatValue(props.modelValue)); - - const ranges = computed(() => [ - { - type: 'hour', - range: [+props.minHour, +props.maxHour], - }, - { - type: 'minute', - range: [+props.minMinute, +props.maxMinute], - }, - ]); - - const originColumns = computed(() => - ranges.value.map(({ type, range: rangeArr }) => { - let values = times(rangeArr[1] - rangeArr[0] + 1, (index) => - padZero(rangeArr[0] + index) - ); - - if (props.filter) { - values = props.filter(type, values); - } - - return { - type, - values, - }; - }) - ); - - const columns = computed(() => - originColumns.value.map((column) => ({ - values: column.values.map((value) => - props.formatter(column.type, value) - ), - })) - ); - - const updateColumnValue = () => { - const pair = currentDate.value.split(':'); - const values = [ - props.formatter('hour', pair[0]), - props.formatter('minute', pair[1]), - ]; - - nextTick(() => { - picker.value?.setValues(values); - }); - }; - - const updateInnerValue = () => { - const [hourIndex, minuteIndex] = picker.value!.getIndexes(); - const [hourColumn, minuteColumn] = originColumns.value; - - const hour = hourColumn.values[hourIndex] || hourColumn.values[0]; - const minute = minuteColumn.values[minuteIndex] || minuteColumn.values[0]; - - currentDate.value = formatValue(`${hour}:${minute}`); - updateColumnValue(); - }; - - const onConfirm = () => emit('confirm', currentDate.value); - const onCancel = () => emit('cancel'); - - const onChange = () => { - updateInnerValue(); - nextTick(() => { - nextTick(() => emit('change', currentDate.value)); - }); - }; - - onMounted(() => { - updateColumnValue(); - nextTick(updateInnerValue); - }); - - watch(columns, updateColumnValue); - - watch( - () => [props.filter, props.maxHour, props.minMinute, props.maxMinute], - updateInnerValue - ); - - watch( - () => props.minHour, - () => { - nextTick(updateInnerValue); - } - ); - - watch(currentDate, (value) => emit('update:modelValue', value)); - - watch( - () => props.modelValue, - (value) => { - value = formatValue(value); - - if (value !== currentDate.value) { - currentDate.value = value; - updateColumnValue(); - } - } - ); - - useExpose({ - getPicker: () => - picker.value && proxyPickerMethods(picker.value, updateInnerValue), - }); - - return () => ( - - ); - }, -}); diff --git a/packages/vant/src/datetime-picker/demo/index.vue b/packages/vant/src/datetime-picker/demo/index.vue deleted file mode 100644 index 1b3dd17a1..000000000 --- a/packages/vant/src/datetime-picker/demo/index.vue +++ /dev/null @@ -1,153 +0,0 @@ - - - diff --git a/packages/vant/src/datetime-picker/index.ts b/packages/vant/src/datetime-picker/index.ts deleted file mode 100644 index 3263617aa..000000000 --- a/packages/vant/src/datetime-picker/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { withInstall } from '../utils'; -import _DatetimePicker, { DatetimePickerProps } from './DatetimePicker'; - -export const DatetimePicker = withInstall(_DatetimePicker); -export default DatetimePicker; -export type { DatetimePickerProps }; -export type { DatetimePickerType, DatetimePickerInstance } from './types'; - -declare module 'vue' { - export interface GlobalComponents { - VanDatetimePicker: typeof DatetimePicker; - } -} diff --git a/packages/vant/src/datetime-picker/test/date-picker.legacy.ts b/packages/vant/src/datetime-picker/test/date-picker.legacy.ts deleted file mode 100644 index 07419823a..000000000 --- a/packages/vant/src/datetime-picker/test/date-picker.legacy.ts +++ /dev/null @@ -1,220 +0,0 @@ -import DatePicker from '../DatePicker'; -import { mount, later, triggerDrag } from '../../../test'; - -function filter(type: string, options: string[]): string[] { - const mod = type === 'year' ? 10 : 5; - return options.filter((option: string) => Number(option) % mod === 0); -} - -function formatter(type: string, value: string): string { - return `${value} ${type}`; -} - -test('filter prop', async () => { - const wrapper = mount(DatePicker, { - props: { - filter, - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - modelValue: new Date(2020, 10, 1, 0, 0), - }, - }); - - expect(wrapper.html()).toMatchSnapshot(); -}); - -test('formatter prop', async () => { - const wrapper = mount(DatePicker, { - props: { - filter, - formatter, - minDate: new Date(2010, 0, 1), - maxDate: new Date(2025, 10, 1), - modelValue: new Date(2020, 10, 1, 0, 0), - }, - }); - - expect(wrapper.html()).toMatchSnapshot(); - - triggerDrag(wrapper.find('.van-picker-column'), 0, -100); - wrapper.find('.van-picker-column ul').trigger('transitionend'); - await later(); - - expect((wrapper.vm as Record).getPicker().getValues()).toEqual([ - '2020 year', - '05 month', - '05 day', - '00 hour', - '00 minute', - ]); -}); - -test('confirm event', () => { - const date = new Date(2020, 10, 1, 0, 0); - - const wrapper = mount(DatePicker, { - props: { - modelValue: date, - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - }, - }); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getFullYear()).toEqual(2020); - - triggerDrag(wrapper.find('.van-picker-column'), 0, -100); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getFullYear()).toEqual(2025); -}); - -test('year-month type', async () => { - const date = new Date(2020, 10, 1, 0, 0); - - const wrapper = mount(DatePicker, { - props: { - type: 'year-month', - modelValue: date, - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - }, - }); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getFullYear()).toEqual(2020); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getMonth()).toEqual(10); - - triggerDrag(wrapper.find('.van-picker-column'), 0, -100); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getFullYear()).toEqual(2025); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getMonth()).toEqual(0); - - triggerDrag(wrapper.findAll('.van-picker-column')[0], 0, -100); - await later(); - triggerDrag(wrapper.findAll('.van-picker-column')[1], 0, -100); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![2][0].getFullYear()).toEqual(2025); - expect(wrapper.emitted<[Date]>('confirm')![2][0].getMonth()).toEqual(10); -}); - -test('month-day type', async () => { - const date = new Date(2020, 10, 1, 0, 0); - - const wrapper = mount(DatePicker, { - props: { - type: 'month-day', - modelValue: date, - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - }, - }); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getMonth()).toEqual(10); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getDate()).toEqual(1); - - triggerDrag(wrapper.find('.van-picker-column'), 0, -300); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getMonth()).toEqual(11); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getDate()).toEqual(1); - - triggerDrag(wrapper.find('.van-picker-column'), 0, -300); - await later(); - triggerDrag(wrapper.findAll('.van-picker-column')[1], 0, -300); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![2][0].getMonth()).toEqual(11); - expect(wrapper.emitted<[Date]>('confirm')![2][0].getDate()).toEqual(31); -}); - -test('datehour type', () => { - const wrapper = mount(DatePicker, { - props: { - minDate: new Date(2010, 0, 1), - maxDate: new Date(2025, 10, 1), - modelValue: new Date(2020, 10, 1, 0, 0), - }, - }); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getHours()).toEqual(0); - - triggerDrag(wrapper.findAll('.van-picker-column')[3], 0, -300); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getHours()).toEqual(23); -}); - -test('cancel event', () => { - const wrapper = mount(DatePicker); - - wrapper.find('.van-picker__cancel').trigger('click'); - expect(wrapper.emitted('cancel')).toBeTruthy(); -}); - -test('max-date prop', () => { - const maxDate = new Date(2010, 5, 0, 0, 0); - const wrapper = mount(DatePicker, { - props: { - modelValue: new Date(2020, 10, 30, 30, 30), - maxDate, - }, - }); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(maxDate); -}); - -test('min-date prop', () => { - const minDate = new Date(2030, 0, 0, 0, 0); - const wrapper = mount(DatePicker, { - props: { - modelValue: new Date(2020, 0, 0, 0, 0), - minDate, - }, - }); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(minDate); -}); - -test('dynamic set value', async () => { - const wrapper = mount(DatePicker, { - props: { - modelValue: new Date(2019, 1, 1), - }, - }); - - await wrapper.setProps({ modelValue: new Date(2019, 1, 1) }); - wrapper.find('.van-picker__confirm').trigger('click'); - await wrapper.setProps({ modelValue: new Date(2025, 1, 1) }); - wrapper.find('.van-picker__confirm').trigger('click'); - - expect(wrapper.emitted<[Date]>('confirm')![0][0].getFullYear()).toEqual(2019); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getFullYear()).toEqual(2025); -}); - -test('use min-date with filter', async () => { - const minDate = new Date(2030, 0, 0, 0, 3); - const maxDate = new Date(2040, 0, 0, 0, 0); - - const wrapper = mount(DatePicker, { - props: { - minDate, - maxDate, - modelValue: new Date(2020, 0, 0, 0, 0), - filter(type: string, values: string[]) { - if (type === 'minute') { - return values.filter((value) => Number(value) % 30 === 0); - } - - return values; - }, - }, - }); - - await later(); - - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual( - new Date(2030, 0, 0, 0, 30) - ); -}); diff --git a/packages/vant/src/datetime-picker/test/datetime-picker.legacy.tsx b/packages/vant/src/datetime-picker/test/datetime-picker.legacy.tsx deleted file mode 100644 index 618e36f60..000000000 --- a/packages/vant/src/datetime-picker/test/datetime-picker.legacy.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { DatetimePicker } from '..'; -import { mount, later } from '../../../test'; -import { reactive } from 'vue'; -import { useExpose } from '../../composables/use-expose'; - -test('should emit confirm event after clicking the confirm button', () => { - const onConfirm = jest.fn(); - const wrapper = mount(DatetimePicker, { - props: { - onConfirm, - }, - }); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(onConfirm).toHaveBeenCalledTimes(1); -}); - -test('should emit cancel event after clicking the confirm button', () => { - const onCancel = jest.fn(); - const wrapper = mount(DatetimePicker, { - props: { - onCancel, - }, - }); - wrapper.find('.van-picker__cancel').trigger('click'); - expect(onCancel).toHaveBeenCalledTimes(1); -}); - -test('should allow to call getPicker method', () => { - const wrapper = mount(DatetimePicker); - - expect(wrapper.vm.getPicker()).toBeTruthy(); -}); - -test('should render title slot correctly', () => { - const wrapper = mount(DatetimePicker, { - props: { - showToolbar: true, - }, - slots: { - title: () => 'Custom title', - }, - }); - - expect(wrapper.find('.van-picker__toolbar').html()).toMatchSnapshot(); -}); - -test('should emit value correctly when dynamic change min-date', async () => { - const defaultValue = new Date(2020, 10, 2, 10, 30); - const wrapper = mount({ - emits: ['confirm'], - setup(_, { emit }) { - const state = reactive({ - date: defaultValue, - minDate: new Date(2010, 0, 1, 10, 30), - }); - - const onChange = () => { - state.minDate = state.date; - }; - - useExpose({ - onChange, - }); - - return () => ( - emit('confirm', value)} - /> - ); - }, - }); - - await later(); - (wrapper.vm as Record).onChange(); - await later(); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(defaultValue); -}); - -test('should update value correctly after calling setColumnIndex method', async () => { - const onConfirm = jest.fn(); - const defaultDate = new Date(2020, 0, 1); - const wrapper = mount(DatetimePicker, { - props: { - type: 'date', - minDate: defaultDate, - maxDate: new Date(2020, 0, 30), - modelValue: defaultDate, - onConfirm, - }, - }); - - wrapper.vm.getPicker().setColumnIndex(2, 14); - await wrapper.find('.van-picker__confirm').trigger('click'); - expect(onConfirm.mock.calls[0]).toEqual([new Date(2020, 0, 15)]); -}); - -test('should update value correctly after calling setColumnValue method', async () => { - const onConfirm = jest.fn(); - const defaultDate = new Date(2020, 0, 1); - const wrapper = mount(DatetimePicker, { - props: { - type: 'date', - minDate: defaultDate, - maxDate: new Date(2020, 0, 30), - modelValue: defaultDate, - onConfirm, - }, - }); - - wrapper.vm.getPicker().setColumnValue(2, '15'); - await wrapper.find('.van-picker__confirm').trigger('click'); - expect(onConfirm.mock.calls[0]).toEqual([new Date(2020, 0, 15)]); -}); diff --git a/packages/vant/src/datetime-picker/test/demo.legacy.ts b/packages/vant/src/datetime-picker/test/demo.legacy.ts deleted file mode 100644 index c0e0c95b9..000000000 --- a/packages/vant/src/datetime-picker/test/demo.legacy.ts +++ /dev/null @@ -1,4 +0,0 @@ -import Demo from '../demo/index.vue'; -import { snapshotDemo } from '../../../test/demo'; - -snapshotDemo(Demo); diff --git a/packages/vant/src/datetime-picker/test/index.legacy.ts b/packages/vant/src/datetime-picker/test/index.legacy.ts deleted file mode 100644 index 39b69af34..000000000 --- a/packages/vant/src/datetime-picker/test/index.legacy.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { later, mount, triggerDrag } from '../../../test'; -import DatePicker from '../DatePicker'; - -test('month-day type', async () => { - const date = new Date(2020, 10, 1, 0, 0); - - const wrapper = mount(DatePicker, { - props: { - type: 'month-day', - modelValue: date, - minDate: new Date(2020, 0, 1), - maxDate: new Date(2025, 10, 1), - }, - }); - - await later(); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getMonth()).toEqual(10); - expect(wrapper.emitted<[Date]>('confirm')![0][0].getDate()).toEqual(1); - - await later(); - triggerDrag(wrapper.find('.van-picker-column'), 0, -300); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getMonth()).toEqual(11); - expect(wrapper.emitted<[Date]>('confirm')![1][0].getDate()).toEqual(1); - - await later(); - triggerDrag(wrapper.findAll('.van-picker-column')[1], 0, -300); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![2][0].getMonth()).toEqual(11); - expect(wrapper.emitted<[Date]>('confirm')![2][0].getDate()).toEqual(31); -}); - -test('v-model', async () => { - const minDate = new Date(2030, 0, 0, 0, 3); - - const wrapper = mount(DatePicker, { - props: { - minDate, - }, - }); - - await later(); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(minDate); -}); - -test('value has an initial value', async () => { - const defaultValue = new Date(2020, 0, 0, 0, 0); - const wrapper = mount(DatePicker, { - propsData: { - modelValue: defaultValue, - }, - }); - - await later(); - wrapper.find('.van-picker__confirm').trigger('click'); - expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(defaultValue); -}); diff --git a/packages/vant/src/datetime-picker/types.ts b/packages/vant/src/datetime-picker/types.ts deleted file mode 100644 index 039956a2a..000000000 --- a/packages/vant/src/datetime-picker/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentPublicInstance } from 'vue'; -import type { PickerInstance } from '../picker'; -import { DatetimePickerProps } from './DatetimePicker'; - -export type DatetimePickerColumnType = - | 'year' - | 'month' - | 'day' - | 'hour' - | 'minute'; - -export type DatetimePickerType = - | 'date' - | 'time' - | 'datetime' - | 'datehour' - | 'month-day' - | 'year-month'; - -export type DatetimePickerExpose = { - getPicker: () => PickerInstance; -}; - -export type DatetimePickerInstance = ComponentPublicInstance< - DatetimePickerProps, - DatetimePickerExpose ->; diff --git a/packages/vant/src/datetime-picker/utils.ts b/packages/vant/src/datetime-picker/utils.ts deleted file mode 100644 index 7e3729b33..000000000 --- a/packages/vant/src/datetime-picker/utils.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { extend } from '../utils'; -import { pickerSharedProps } from '../picker/Picker'; -import type { PropType } from 'vue'; -import type { PickerInstance, PickerOption } from '../picker'; - -export const sharedProps = extend({}, pickerSharedProps, { - filter: Function as PropType< - (columnType: string, options: PickerOption[]) => PickerOption[] - >, - formatter: { - type: Function as PropType< - (type: string, option: PickerOption) => PickerOption - >, - default: (type: string, option: PickerOption) => option, - }, -}); - -export const pickerInheritKeys = Object.keys(pickerSharedProps) as Array< - keyof typeof pickerSharedProps ->; - -export function times(n: number, iteratee: (index: number) => T) { - if (n < 0) { - return []; - } - - const result: T[] = Array(n); - - let index = -1; - while (++index < n) { - result[index] = iteratee(index); - } - - return result; -} - -export function getTrueValue(value: string | undefined): number { - if (!value) { - return 0; - } - - while (Number.isNaN(parseInt(value, 10))) { - if (value.length > 1) { - value = value.slice(1); - } else { - return 0; - } - } - - return parseInt(value, 10); -} - -export const getMonthEndDay = (year: number, month: number): number => - 32 - new Date(year, month - 1, 32).getDate(); - -// https://github.com/youzan/vant/issues/10013 -export const proxyPickerMethods = ( - picker: PickerInstance, - callback: () => void -) => { - const methods = [ - 'setValues', - 'setIndexes', - 'setColumnIndex', - 'setColumnValue', - ]; - return new Proxy(picker, { - get: (target, prop: keyof PickerInstance) => { - if (methods.includes(prop)) { - return (...args: unknown[]) => { - target[prop](...args); - callback(); - }; - } - return target[prop]; - }, - }); -};