Skip to content

Commit 686edcf

Browse files
committed
feat(date-picker): add fallbackViewDate prop to control panel view date and close #1951
1 parent 54a23eb commit 686edcf

7 files changed

Lines changed: 124 additions & 30 deletions

File tree

packages/varlet-ui/src/date-picker/DatePicker.vue

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ export default defineComponent({
280280
watch(
281281
() => props.modelValue,
282282
(value) => {
283-
if (!checkValue() || invalidFormatDate(value)) {
283+
if (!checkValue()) {
284284
return
285285
}
286286
@@ -298,7 +298,7 @@ export default defineComponent({
298298
299299
multipleInit(value, props.type)
300300
} else {
301-
dateInit(value as string)
301+
dateInit(getSingleDate(value as string | undefined))
302302
}
303303
},
304304
{ immediate: true },
@@ -537,15 +537,39 @@ export default defineComponent({
537537
return false
538538
}
539539
540+
function getFallbackDate() {
541+
if (props.fallbackViewDate) {
542+
const formatDate = dayjs(props.fallbackViewDate).format('YYYY-MM-D')
543+
544+
if (formatDate !== 'Invalid Date') {
545+
return props.fallbackViewDate
546+
}
547+
}
548+
549+
return dayjs().format('YYYY-MM-D')
550+
}
551+
552+
function getSingleDate(value: string | undefined) {
553+
if (value && !invalidFormatDate(dayjs(value).format('YYYY-MM-D'))) {
554+
return value
555+
}
556+
557+
return getFallbackDate()
558+
}
559+
560+
function getFirstValidDate(value: Array<string>, type: string) {
561+
const formatType = type === 'year' ? 'YYYY' : type === 'month' ? 'YYYY-MM' : 'YYYY-MM-D'
562+
563+
return value.find((choose) => !invalidFormatDate(dayjs(choose).format(formatType)))
564+
}
565+
540566
function rangeInit(value: Array<string>, type: string) {
541567
const rangeDate = type === 'year' ? chooseRangeYear : type === 'month' ? chooseRangeMonth : chooseRangeDay
542568
const formatType = type === 'year' ? 'YYYY' : type === 'month' ? 'YYYY-MM' : 'YYYY-MM-D'
543-
const formatDateList = value.map((choose) => dayjs(choose).format(formatType)).slice(0, 2)
544-
545-
const isValid = rangeDate.value.some((date) => invalidFormatDate(date))
546-
if (isValid) {
547-
return
548-
}
569+
const formatDateList = value
570+
.map((choose) => dayjs(choose).format(formatType))
571+
.filter((date) => !invalidFormatDate(date))
572+
.slice(0, 2)
549573
550574
rangeDate.value = formatDateList
551575
@@ -554,6 +578,8 @@ export default defineComponent({
554578
if (rangeDate.value.length === 2 && isChangeOrder) {
555579
rangeDate.value = [rangeDate.value[1], rangeDate.value[0]]
556580
}
581+
582+
previewInit(getFirstValidDate(value, type) ?? getFallbackDate())
557583
}
558584
559585
function multipleInit(value: Array<string>, type: string) {
@@ -563,10 +589,12 @@ export default defineComponent({
563589
// need uniq
564590
const formatDateList = Array.from(new Set(value.map((choose) => dayjs(choose).format(formatType))))
565591
rangeDate.value = formatDateList.filter((date) => date !== 'Invalid Date')
592+
593+
previewInit(getFirstValidDate(value, type) ?? getFallbackDate())
566594
}
567595
568596
function dateInit(value: string | undefined) {
569-
const handleValue = value ? dayjs(value) : dayjs()
597+
const handleValue = value ? dayjs(value) : dayjs(getFallbackDate())
570598
const formatDate = handleValue.format('YYYY-MM-D')
571599
572600
if (invalidFormatDate(formatDate)) {
@@ -580,6 +608,20 @@ export default defineComponent({
580608
chooseMonth.value = monthDes
581609
chooseYear.value = yearValue
582610
chooseDay.value = dayValue
611+
previewInit(formatDate)
612+
}
613+
614+
function previewInit(value: string | undefined) {
615+
const handleValue = value ? dayjs(value) : dayjs(getFallbackDate())
616+
const formatDate = handleValue.format('YYYY-MM-D')
617+
618+
if (invalidFormatDate(formatDate)) {
619+
return
620+
}
621+
622+
const [yearValue, monthValue] = formatDate.split('-')
623+
const monthDes: Month = MONTH_LIST.find((month) => month === monthValue) as Month
624+
583625
previewMonth.value = monthDes
584626
previewYear.value = yearValue
585627
}

packages/varlet-ui/src/date-picker/__tests__/index.spec.js

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import VarDatePicker from '../DatePicker'
88

99
mockScrollIntoView()
1010

11-
const [currentYear, currentMonth] = dayjs().format('YYYY-MM').split('-')
11+
const [currentYear] = dayjs().format('YYYY-MM').split('-')
1212

1313
test('datePicker plugin', () => {
1414
const app = createApp({}).use(DatePicker)
@@ -258,6 +258,67 @@ test('datePicker should update title year when month preview crosses year', asyn
258258
wrapper.unmount()
259259
})
260260

261+
test('datePicker fallbackViewDate should control initial panel when modelValue is empty', async () => {
262+
const wrapper = mount(VarDatePicker, {
263+
props: {
264+
fallbackViewDate: '2026-04-01',
265+
},
266+
})
267+
268+
await delay(0)
269+
expect(wrapper.find('.var-date-picker__title-year').text()).toBe('2026')
270+
expect(wrapper.find('.var-date-picker-header__value').text()).toBe('2026 四月')
271+
expect(wrapper.emitted()['update:modelValue']).toBeFalsy()
272+
273+
wrapper.unmount()
274+
})
275+
276+
test('datePicker fallbackViewDate should not override modelValue', async () => {
277+
const wrapper = mount(VarDatePicker, {
278+
props: {
279+
modelValue: '2021-05-19',
280+
fallbackViewDate: '2026-04-01',
281+
},
282+
})
283+
284+
await delay(0)
285+
expect(wrapper.find('.var-date-picker__title-year').text()).toBe('2021')
286+
expect(wrapper.find('.var-date-picker-header__value').text()).toBe('2021 五月')
287+
288+
wrapper.unmount()
289+
})
290+
291+
test('datePicker fallbackViewDate should control initial panel for empty multiple and range value', async () => {
292+
const multipleWrapper = mount(VarDatePicker, {
293+
props: {
294+
multiple: true,
295+
modelValue: [],
296+
fallbackViewDate: '2026-04-01',
297+
},
298+
})
299+
300+
await delay(0)
301+
expect(multipleWrapper.find('.var-date-picker__title-year').text()).toBe('2026')
302+
expect(multipleWrapper.find('.var-date-picker-header__value').text()).toBe('2026 四月')
303+
expect(multipleWrapper.emitted()['update:modelValue']).toBeFalsy()
304+
multipleWrapper.unmount()
305+
306+
const rangeWrapper = mount(VarDatePicker, {
307+
props: {
308+
range: true,
309+
modelValue: [],
310+
fallbackViewDate: '2026-04-01',
311+
},
312+
})
313+
314+
await delay(0)
315+
expect(rangeWrapper.find('.var-date-picker__title-year').text()).toBe('2026')
316+
expect(rangeWrapper.find('.var-date-picker-header__value').text()).toBe('2026 四月')
317+
expect(rangeWrapper.emitted()['update:modelValue']).toBeFalsy()
318+
319+
rangeWrapper.unmount()
320+
})
321+
261322
test('datePicker multiple', async () => {
262323
const template = `<var-date-picker multiple v-model="date" :type="type"/>`
263324

@@ -280,26 +341,17 @@ test('datePicker multiple', async () => {
280341
await elements[1].trigger('click')
281342
await elements[2].trigger('click')
282343

283-
expect(wrapper.vm.date).toEqual([
284-
'2021-05-19',
285-
`${currentYear}-${currentMonth}-01`,
286-
`${currentYear}-${currentMonth}-02`,
287-
`${currentYear}-${currentMonth}-03`,
288-
])
344+
expect(wrapper.vm.date).toEqual(['2021-05-19', '2021-05-01', '2021-05-02', '2021-05-03'])
289345

290346
await elements[0].trigger('click')
291-
expect(wrapper.vm.date).toEqual([
292-
'2021-05-19',
293-
`${currentYear}-${currentMonth}-02`,
294-
`${currentYear}-${currentMonth}-03`,
295-
])
347+
expect(wrapper.vm.date).toEqual(['2021-05-19', '2021-05-02', '2021-05-03'])
296348

297349
await wrapper.setData({ type: 'month', date: ['2021-05'] })
298350
await delay(0)
299351

300352
const btn = wrapper.find('ul').find('button')
301353
await btn.trigger('click')
302-
expect(wrapper.vm.date).toEqual(['2021-05', `${currentYear}-01`])
354+
expect(wrapper.vm.date).toEqual(['2021-05', '2021-01'])
303355

304356
await wrapper.setData({ type: 'year', date: ['2021'] })
305357
await delay(0)
@@ -336,7 +388,7 @@ test('datePicker range', async () => {
336388
await elements[0].trigger('click')
337389
await elements[2].trigger('click')
338390

339-
expect(wrapper.vm.date).toEqual([`${currentYear}-${currentMonth}-01`, `${currentYear}-${currentMonth}-03`])
391+
expect(wrapper.vm.date).toEqual(['2021-05-01', '2021-05-03'])
340392

341393
await wrapper.setData({ type: 'month', date: null })
342394
await wrapper.setData({ type: 'month', date: ['2021-05', '2021-06'] })
@@ -345,7 +397,7 @@ test('datePicker range', async () => {
345397
const lis = wrapper.find('.var-month-picker__content').find('ul').findAll('li').slice(0, 3)
346398
await lis[0].find('button').trigger('click')
347399
await lis[2].find('button').trigger('click')
348-
expect(wrapper.vm.date).toEqual([`${currentYear}-01`, `${currentYear}-03`])
400+
expect(wrapper.vm.date).toEqual(['2021-01', '2021-03'])
349401
expect(fn).toHaveBeenCalledTimes(1)
350402

351403
await wrapper.setData({ type: 'year', date: [currentYear, `${+currentYear + 2}`] })

packages/varlet-ui/src/date-picker/docs/en-US.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ const allowedDates = val => parseInt(val.split('-')[2], 10) % 2 === 1
108108
| `multiple` | Allow the selection of multiple dates | _boolean_ | `false` |
109109
| `range` | Allow the selection of date range | _boolean_ | `false` |
110110
| `touchable` | Allow switch panel by touch | _boolean_ | `true` |
111+
| `fallback-view-date` ***3.15.2*** | Controls the panel view fallback date when there is no usable selected value. It does not update `v-model`. | _string_ | `-` |
111112

112113
### Events
113114

packages/varlet-ui/src/date-picker/docs/zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ const allowedDates = val => parseInt(val.split('-')[2], 10) % 2 === 1
111111
| `multiple` | 是否支持选择多个日期 | _boolean_ | `false` |
112112
| `range` | 是否支持选择一个范围 | _boolean_ | `false` |
113113
| `touchable` | 是否支持拖动切换面板 | _boolean_ | `true` |
114+
| `fallback-view-date` ***3.15.2*** | 在没有可用选中值时,控制面板视图回退到的日期(不会写回 `v-model`| _string_ | `-` |
114115

115116
### 事件
116117

packages/varlet-ui/src/date-picker/example/index.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { t, use } from './locale'
66
const dates = reactive({
77
date: '2021-04',
88
date1: '2021-04-08',
9-
date2: ['', ''],
9+
date2: [],
1010
date3: ['2021-02-01', '2021-02-15'],
1111
date4: '2020-11-11',
1212
date5: '2021-05',
@@ -16,15 +16,11 @@ watchLang(use)
1616
onThemeChange()
1717
1818
const allowedDates = (date) => parseInt(date.split('-')[2], 10) % 2 === 1
19-
20-
function handlePreview(...args) {
21-
console.log('preview', args)
22-
}
2319
</script>
2420

2521
<template>
2622
<app-type>{{ t('basicUsage') }}</app-type>
27-
<var-date-picker v-model="dates.date1" @preview="handlePreview" />
23+
<var-date-picker v-model="dates.date1" />
2824

2925
<app-type>{{ t('monthPicker') }}</app-type>
3026
<var-date-picker v-model="dates.date" type="month" />

packages/varlet-ui/src/date-picker/props.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const props = {
5757
default: 'date',
5858
},
5959
hint: String,
60+
fallbackViewDate: String,
6061
allowedDates: Function as PropType<AllowedDates>,
6162
color: String,
6263
titleColor: String,

packages/varlet-ui/types/datePicker.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface DatePickerProps extends BasicAttributes {
2222
multiple?: boolean
2323
range?: boolean
2424
touchable?: boolean
25+
fallbackViewDate?: string
2526
onPreview?: ListenerProp<(year: number, month: number, day?: number) => void>
2627
onChange?: ListenerProp<(value: string | string[]) => void>
2728
'onUpdate:modelValue'?: ListenerProp<(value: string | string[]) => void>

0 commit comments

Comments
 (0)