From 0fa2da707604b481d979d0d22ad6bebfdb01dc49 Mon Sep 17 00:00:00 2001 From: discreted66 <953831480@qq.com> Date: Tue, 21 Jan 2025 10:26:14 -0800 Subject: [PATCH] =?UTF-8?q?feat:[dateRange,datePicker]=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=97=B6=E9=97=B4=E5=8C=BA=E9=97=B4=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sites/demos/apis/date-panel.js | 306 ++++++++++++++++-- .../basic-usage-composition-api.vue | 12 +- .../pc/app/date-panel/basic-usage.spec.ts | 12 +- .../demos/pc/app/date-panel/basic-usage.vue | 13 +- .../pc/app/date-panel/custom-week.spec.ts | 12 +- .../custom-weeks-composition-api.vue | 23 +- .../demos/pc/app/date-panel/custom-weeks.vue | 23 +- .../disabled-date-composition-api.vue | 15 +- .../pc/app/date-panel/disabled-date.spec.ts | 25 +- .../demos/pc/app/date-panel/disabled-date.vue | 15 +- .../app/date-panel/event-composition-api.vue | 16 +- .../demos/pc/app/date-panel/event.spec.ts | 15 +- .../sites/demos/pc/app/date-panel/event.vue | 17 +- .../app/date-panel/format-composition-api.vue | 21 +- .../demos/pc/app/date-panel/format.spec.ts | 12 +- .../sites/demos/pc/app/date-panel/format.vue | 20 +- .../date-panel/readonly-composition-api.vue | 15 +- .../demos/pc/app/date-panel/readonly.spec.ts | 19 +- .../demos/pc/app/date-panel/readonly.vue | 15 +- .../date-panel/shortcuts-composition-api.vue | 40 ++- .../demos/pc/app/date-panel/shortcuts.spec.ts | 9 +- .../demos/pc/app/date-panel/shortcuts.vue | 40 ++- .../unlink-panels-composition-api.vue | 35 ++ .../pc/app/date-panel/unlink-panels.spec.ts | 18 ++ .../demos/pc/app/date-panel/unlink-panels.vue | 35 ++ .../pc/app/date-panel/webdoc/date-panel.js | 14 + .../demos/pc/app/date-picker/align.spec.ts | 3 + .../pc/app/date-picker/webdoc/date-picker.js | 4 +- packages/renderless/src/date-panel/vue.ts | 2 +- packages/renderless/src/date-range/index.ts | 47 ++- packages/renderless/src/date-range/vue.ts | 44 +-- packages/renderless/src/date-table/index.ts | 3 + packages/renderless/src/time/index.ts | 8 + .../date-range/__tests__/date-range.test.tsx | 17 + packages/vue/src/date-range/src/index.ts | 32 ++ .../vue/src/date-range/src/mobile-first.vue | 7 +- packages/vue/src/date-range/src/pc.vue | 37 ++- packages/vue/src/date-table/src/index.ts | 4 + packages/vue/src/date-table/src/pc.vue | 3 +- 39 files changed, 853 insertions(+), 155 deletions(-) create mode 100644 examples/sites/demos/pc/app/date-panel/unlink-panels-composition-api.vue create mode 100644 examples/sites/demos/pc/app/date-panel/unlink-panels.spec.ts create mode 100644 examples/sites/demos/pc/app/date-panel/unlink-panels.vue create mode 100644 packages/vue/src/date-range/__tests__/date-range.test.tsx diff --git a/examples/sites/demos/apis/date-panel.js b/examples/sites/demos/apis/date-panel.js index 6d24b00882..0e6ff6385d 100644 --- a/examples/sites/demos/apis/date-panel.js +++ b/examples/sites/demos/apis/date-panel.js @@ -28,7 +28,7 @@ export default { pcDemo: 'format' }, { - name: 'v-model/modelValue', + name: 'v-model / modelValue', type: 'date | string | number', defaultValue: '', desc: { @@ -60,6 +60,17 @@ export default { mode: ['pc'], pcDemo: 'readonly' }, + { + name: 'shortcuts', + type: 'Array', + defaultValue: '--', + desc: { + 'zh-CN': `设置快捷选项`, + 'en-US': 'Set shortcut options' + }, + mode: ['pc'], + pcDemo: 'shortcuts' + }, { name: 'show-week-number', type: 'boolean', @@ -185,37 +196,272 @@ export default { mode: ['pc'] } ] - } - ], - types: [ - { - name: 'IPickerOptions', - type: 'interface', - code: ` -interface IPickerOptions { - // 每周的第一天是星期几,默认值是7,也就是星期天 - firstDayOfWeek: number - // 实现部分禁用,此时只能选择一部分日期 - disabledDate: (time: Date) => boolean - // 选中日期后执行的回调,需要与 daterange 或 datetimerange 类型配合使用才生效 - onPick: (range: { minDate: Date, maxDate: Date }) => void - // 快捷选项 - shortcuts: { - text: string - onClick: (picker: { $emit: (type: string, date: Date) => void }) => void - type: 'startFrom' | 'EndAt' - startDate: Date - endDate: Date - }[] -} - ` }, { - name: 'IType', - type: 'type', - code: ` -type IType = 'date' | 'dates' | 'daterange' | 'datetime' | 'datetimerange' | 'week' | 'month' | 'monthrange' | 'quarter' | 'year' | 'years' | 'yearrange' - ` + name: 'date-range', + type: 'component', + props: [ + { + name: 'modelValue / v-model', + type: 'Array', + defaultValue: '', + desc: { + 'zh-CN': '绑定值', + 'en-US': 'Set the initial value of the calendar component. ;Bound Value' + }, + mode: ['pc'], + pcDemo: 'basic-usage' + }, + { + name: 'type', + type: 'daterange | datetimerange', + defaultValue: 'daterange', + desc: { + 'zh-CN': '时间区间类型', + 'en-US': 'Range type' + }, + mode: ['pc'], + pcDemo: 'basic-usage' + }, + { + name: 'disabled-date', + type: 'function', + defaultValue: '', + desc: { + 'zh-CN': '配置部分禁用', + 'en-US': 'Configuration section disabled.' + }, + mode: ['pc'], + pcDemo: 'shortcuts' + }, + { + name: 'format', + type: 'string', + defaultValue: "'yyyy-MM-dd'", + desc: { + 'zh-CN': '显示在输入框中的格式', + 'en-US': 'Display format in the text box' + }, + mode: ['pc'], + pcDemo: 'format' + }, + { + name: 'popper-class', + type: 'string', + defaultValue: '', + desc: { + 'zh-CN': '为 DateRange 下拉弹框添加的 class 类名', + 'en-US': 'Class name added for DateRange.' + }, + mode: ['pc'], + pcDemo: 'custom-suffix-icon' + }, + { + name: 'readonly', + type: 'boolean', + defaultValue: 'false', + desc: { + 'zh-CN': '设置日历组件是否只读', + 'en-US': 'Set whether the calendar component is read-only.' + }, + mode: ['pc'], + pcDemo: 'disabled' + }, + { + name: 'shortcuts', + type: 'Array', + defaultValue: '--', + desc: { + 'zh-CN': `设置快捷选项`, + 'en-US': 'Set shortcut options' + }, + mode: ['pc'], + pcDemo: 'shortcuts' + }, + { + name: 'show-week-number', + type: 'boolean', + defaultValue: 'false', + desc: { + 'zh-CN': '是否展示周次序号', + 'en-US': 'Class name added to the DatePicker drop-down list box' + }, + mode: ['pc'], + pcDemo: 'custom-weeks' + }, + { + name: 'unlink-panels', + type: 'boolean', + defaultValue: 'false', + desc: { + 'zh-CN': '在范围选择器里取消两个日期面板之间的联动', + 'en-US': 'Unlink the two date panels in the range selector' + }, + mode: ['pc'], + pcDemo: 'unlink-panels' + } + ], + events: [ + { + name: 'select-change', + type: '(value: Date) => void', + desc: { + 'zh-CN': '用户确认选定的值时触发', + 'en-US': + 'This event is triggered when the user confirms the selected value. change (arg1) {//arg1 is the changed date or date set of datepicker}' + }, + mode: ['pc'], + pcDemo: 'events' + } + ], + format: [ + { + name: 'a', + desc: { + 'zh-CN': 'am/pm', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'A', + desc: { + 'zh-CN': 'AM/PM', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'd', + desc: { + 'zh-CN': '日,不补0', + 'en-US': '' + }, + mode: ['pc'] + }, + { + name: 'dd', + desc: { + 'zh-CN': '日', + 'en-US': '' + }, + mode: ['pc'] + }, + { + name: 'h', + desc: { + 'zh-CN': '小时,12小时制,需要和 A 或 a 一起使用,不补0', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'H', + desc: { + 'zh-CN': '小时,24小时制,不补0', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'hh', + desc: { + 'zh-CN': '小时,12小时制,需要和 A 或 a 一起使用', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'HH', + desc: { + 'zh-CN': '小时,24小时制', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'm', + desc: { + 'zh-CN': '分钟,不补0', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'M', + desc: { + 'zh-CN': '月,不补0', + 'en-US': '' + }, + mode: ['pc'] + }, + { + name: 'mm', + desc: { + 'zh-CN': '分钟', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'MM', + desc: { + 'zh-CN': '月', + 'en-US': '' + }, + mode: ['pc'] + }, + { + name: 's', + desc: { + 'zh-CN': '秒,不补0', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'ss', + desc: { + 'zh-CN': '秒', + 'en-US': '' + }, + mode: ['pc'], + pcDemo: '' + }, + { + name: 'W', + desc: { + 'zh-CN': '周,不补0', + 'en-US': '' + }, + mode: ['pc'] + }, + { + name: 'WW', + desc: { + 'zh-CN': '周', + 'en-US': '' + }, + mode: ['pc'] + }, + { + name: 'yyyy', + desc: { + 'zh-CN': '年', + 'en-US': '' + }, + mode: ['pc'] + } + ] } ] } diff --git a/examples/sites/demos/pc/app/date-panel/basic-usage-composition-api.vue b/examples/sites/demos/pc/app/date-panel/basic-usage-composition-api.vue index bbee604870..ea75fabf15 100644 --- a/examples/sites/demos/pc/app/date-panel/basic-usage-composition-api.vue +++ b/examples/sites/demos/pc/app/date-panel/basic-usage-composition-api.vue @@ -1,15 +1,18 @@ diff --git a/examples/sites/demos/pc/app/date-panel/basic-usage.spec.ts b/examples/sites/demos/pc/app/date-panel/basic-usage.spec.ts index 1ad8ba7291..5ee4603bf1 100644 --- a/examples/sites/demos/pc/app/date-panel/basic-usage.spec.ts +++ b/examples/sites/demos/pc/app/date-panel/basic-usage.spec.ts @@ -14,9 +14,15 @@ test('[DatePanel] 测试月份/年份/日期选择', async ({ page }) => { // 选择月份展示日期面板 await page.getByRole('cell', { name: '一月', exact: true }).click() - await expect(page.getByRole('cell', { name: '15' }).locator('div')).toBeVisible() + await expect(page.getByRole('cell', { name: '15' }).locator('div').first()).toBeVisible() // 选择日期 - await page.getByRole('cell', { name: '15' }).locator('div').click() - await expect(page.getByText('-01-15')).toBeVisible() + await page.getByText('16').first().click() + await expect(page.getByText('-01-16')).toBeVisible() + + // dateRange + await page.locator('.tiny-date-range-picker__header > button:nth-child(2)').first().click() + await page.locator('div').filter({ hasText: /^19$/ }).nth(1).click() + await page.getByRole('cell', { name: '28' }).nth(3).click() + await expect(page.getByText('[ "2024-12-19", "2025-01-28" ]')).toBeVisible() }) diff --git a/examples/sites/demos/pc/app/date-panel/basic-usage.vue b/examples/sites/demos/pc/app/date-panel/basic-usage.vue index 57e8551553..2f7f13ad8e 100644 --- a/examples/sites/demos/pc/app/date-panel/basic-usage.vue +++ b/examples/sites/demos/pc/app/date-panel/basic-usage.vue @@ -1,24 +1,29 @@ diff --git a/examples/sites/demos/pc/app/date-panel/custom-week.spec.ts b/examples/sites/demos/pc/app/date-panel/custom-week.spec.ts index 5815ce83f7..9bec5f183a 100644 --- a/examples/sites/demos/pc/app/date-panel/custom-week.spec.ts +++ b/examples/sites/demos/pc/app/date-panel/custom-week.spec.ts @@ -4,6 +4,7 @@ test('[DatePanel] 测试周次序号', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('date-panel#custom-weeks') + // datePanel // 选择年份月份日期 await page.getByRole('button', { name: '2025 年' }).click() await page.getByText('2026').click() @@ -15,6 +16,15 @@ test('[DatePanel] 测试周次序号', async ({ page }) => { await expect(page.getByText('17w')).toBeVisible() // 选择日期 - await page.getByText('23', { exact: true }).click() + await page.getByText('23', { exact: true }).first().click() await expect(page.getByText('-04-23')).toBeVisible() + await expect(page.locator('.value')).toHaveText('2026-04-23') + + // dateRange + await page.locator('div:nth-child(2) > .tiny-date-range-picker__header > button:nth-child(2)').click() + await expect(page.getByText('12w')).toBeVisible() + + await page.getByText('13').nth(1).click() + await page.getByText('17').nth(2).click() + await expect(page.locator('.value1')).toHaveText('[ "2025-02-13", "2025-02-17" ]') }) diff --git a/examples/sites/demos/pc/app/date-panel/custom-weeks-composition-api.vue b/examples/sites/demos/pc/app/date-panel/custom-weeks-composition-api.vue index 00b92b6e49..0cb8ea02f0 100644 --- a/examples/sites/demos/pc/app/date-panel/custom-weeks-composition-api.vue +++ b/examples/sites/demos/pc/app/date-panel/custom-weeks-composition-api.vue @@ -1,21 +1,30 @@ diff --git a/examples/sites/demos/pc/app/date-panel/event.spec.ts b/examples/sites/demos/pc/app/date-panel/event.spec.ts index ed60e8f57c..b035aecbe9 100644 --- a/examples/sites/demos/pc/app/date-panel/event.spec.ts +++ b/examples/sites/demos/pc/app/date-panel/event.spec.ts @@ -4,13 +4,16 @@ test('[DatePanel] 测试事件', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('date-panel#event') + // datePanel await page.getByRole('button', { name: '2025 年' }).click() await expect(page.getByRole('cell', { name: '2025' })).toBeVisible() + await page.getByText('2024').click() + await page.getByText('七月').click() + await page.locator('#event').getByText('17').first().click() + await expect(page.getByText('触发 面板选中 事件,组件绑定值为:2024-07-')).toBeVisible() - await page.getByText('2025').click() - await expect(page.getByRole('cell', { name: '一月', exact: true })).toBeVisible() - - await page.getByText('六月').click() - await page.getByRole('cell', { name: '11' }).locator('div').click() - await expect(page.getByText('触发 面板选中 事件,组件绑定值为:2025-06-')).toBeVisible() + // dateRange + await page.locator('#event').getByText('9', { exact: true }).nth(2).click() + await page.locator('#event').getByText('16').nth(2).click() + await expect(page.getByText('触发 区间面板选中 事件,组件绑定值为:2025-01-')).toBeVisible() }) diff --git a/examples/sites/demos/pc/app/date-panel/event.vue b/examples/sites/demos/pc/app/date-panel/event.vue index 17bb1e8076..3336f521da 100644 --- a/examples/sites/demos/pc/app/date-panel/event.vue +++ b/examples/sites/demos/pc/app/date-panel/event.vue @@ -1,27 +1,36 @@ diff --git a/examples/sites/demos/pc/app/date-panel/format-composition-api.vue b/examples/sites/demos/pc/app/date-panel/format-composition-api.vue index 158759a0d5..ae0afee3a9 100644 --- a/examples/sites/demos/pc/app/date-panel/format-composition-api.vue +++ b/examples/sites/demos/pc/app/date-panel/format-composition-api.vue @@ -1,25 +1,28 @@ diff --git a/examples/sites/demos/pc/app/date-panel/format.spec.ts b/examples/sites/demos/pc/app/date-panel/format.spec.ts index 0cdfa6ec28..7c5c193364 100644 --- a/examples/sites/demos/pc/app/date-panel/format.spec.ts +++ b/examples/sites/demos/pc/app/date-panel/format.spec.ts @@ -3,10 +3,12 @@ import { test, expect } from '@playwright/test' test('[DatePanel] 测试格式化', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('date-panel#format') + // datePanel + await page.locator('#format').getByText('23', { exact: true }).first().click() + await expect(page.locator('.value')).toHaveText('2025/01/23') - await page.locator('#format').getByText('15').first().click() - await expect(page.getByText('-01-15')).toBeVisible() - - await page.locator('#format').getByText('8', { exact: true }).nth(2).click() - await expect(page.getByText('/01/08')).toBeVisible() + // dateRange + await page.locator('#format').getByText('28').nth(1).click() + await page.locator('#format').getByText('12', { exact: true }).nth(2).click() + await expect(page.locator('.value1')).toHaveText('[ "2025/01/28", "2025/02/12" ]') }) diff --git a/examples/sites/demos/pc/app/date-panel/format.vue b/examples/sites/demos/pc/app/date-panel/format.vue index 09de8b6a05..ae0afee3a9 100644 --- a/examples/sites/demos/pc/app/date-panel/format.vue +++ b/examples/sites/demos/pc/app/date-panel/format.vue @@ -1,22 +1,28 @@ diff --git a/examples/sites/demos/pc/app/date-panel/readonly-composition-api.vue b/examples/sites/demos/pc/app/date-panel/readonly-composition-api.vue index eb39e1e244..3e2fa48d77 100644 --- a/examples/sites/demos/pc/app/date-panel/readonly-composition-api.vue +++ b/examples/sites/demos/pc/app/date-panel/readonly-composition-api.vue @@ -1,19 +1,28 @@ diff --git a/examples/sites/demos/pc/app/date-panel/readonly.spec.ts b/examples/sites/demos/pc/app/date-panel/readonly.spec.ts index 9c11e161f2..3cbe921f1d 100644 --- a/examples/sites/demos/pc/app/date-panel/readonly.spec.ts +++ b/examples/sites/demos/pc/app/date-panel/readonly.spec.ts @@ -3,12 +3,17 @@ import { test, expect } from '@playwright/test' test('[DatePanel] 测试只读', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('date-panel#readonly') - await page.getByText('21', { exact: true }).click() - await expect(page.getByText('-01-05')).toBeVisible() - await page.getByLabel('上个月').click() - await page.getByText('18').click() - await page.getByLabel('下个月').click() - await expect(page.getByText('-01-05')).toBeVisible() - await expect(page.getByText('5', { exact: true }).first()).toHaveCSS('background-color', 'rgb(20, 118, 255)') + // datePanel + await page.locator('#readonly').getByLabel('上个月').click() + await page.locator('#readonly').getByText('19').first().click() + await page.locator('#readonly').getByLabel('下个月').click() + await page.locator('#readonly div').filter({ hasText: /^21$/ }).first().click() + await page.locator('#readonly div').filter({ hasText: /^23$/ }).first().click() + await expect(page.locator('.value')).toHaveText('2025-01-15') + + // dateRange + await page.locator('#readonly').getByText('14').nth(1).click() + await page.locator('#readonly').getByText('25', { exact: true }).nth(2).click() + await expect(page.locator('.value1')).toHaveText('[ "2025-01-15", "2025-02-15" ]') }) diff --git a/examples/sites/demos/pc/app/date-panel/readonly.vue b/examples/sites/demos/pc/app/date-panel/readonly.vue index eb39e1e244..3e2fa48d77 100644 --- a/examples/sites/demos/pc/app/date-panel/readonly.vue +++ b/examples/sites/demos/pc/app/date-panel/readonly.vue @@ -1,19 +1,28 @@ diff --git a/examples/sites/demos/pc/app/date-panel/shortcuts-composition-api.vue b/examples/sites/demos/pc/app/date-panel/shortcuts-composition-api.vue index 537724e69d..00e29dee3e 100644 --- a/examples/sites/demos/pc/app/date-panel/shortcuts-composition-api.vue +++ b/examples/sites/demos/pc/app/date-panel/shortcuts-composition-api.vue @@ -2,14 +2,17 @@
{{ value }}
+
{{ value1 }}
+
diff --git a/examples/sites/demos/pc/app/date-panel/shortcuts.spec.ts b/examples/sites/demos/pc/app/date-panel/shortcuts.spec.ts index 3374ab778d..926c27b994 100644 --- a/examples/sites/demos/pc/app/date-panel/shortcuts.spec.ts +++ b/examples/sites/demos/pc/app/date-panel/shortcuts.spec.ts @@ -4,10 +4,15 @@ test('[DatePanel] 测试快捷选项', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('date-panel#shortcuts') - const valueDom = page.locator('.value') + // datePanel await page.getByRole('button', { name: '今天' }).click() await page.getByRole('button', { name: '昨天' }).click() await page.getByRole('button', { name: '一周前' }).click() + await expect(page.locator('.value')).not.toHaveText('') - await expect(valueDom).not.toHaveText('') + // dateRange + await page.getByRole('button', { name: '最近三个月' }).click() + await page.getByRole('button', { name: '最近一个月' }).click() + await page.getByRole('button', { name: '最近一周' }).click() + await expect(page.locator('.value1')).not.toHaveText('') }) diff --git a/examples/sites/demos/pc/app/date-panel/shortcuts.vue b/examples/sites/demos/pc/app/date-panel/shortcuts.vue index 537724e69d..00e29dee3e 100644 --- a/examples/sites/demos/pc/app/date-panel/shortcuts.vue +++ b/examples/sites/demos/pc/app/date-panel/shortcuts.vue @@ -2,14 +2,17 @@
{{ value }}
+
{{ value1 }}
+
diff --git a/examples/sites/demos/pc/app/date-panel/unlink-panels-composition-api.vue b/examples/sites/demos/pc/app/date-panel/unlink-panels-composition-api.vue new file mode 100644 index 0000000000..b06740c9dd --- /dev/null +++ b/examples/sites/demos/pc/app/date-panel/unlink-panels-composition-api.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/examples/sites/demos/pc/app/date-panel/unlink-panels.spec.ts b/examples/sites/demos/pc/app/date-panel/unlink-panels.spec.ts new file mode 100644 index 0000000000..579b96b7ca --- /dev/null +++ b/examples/sites/demos/pc/app/date-panel/unlink-panels.spec.ts @@ -0,0 +1,18 @@ +import { expect, test } from '@playwright/test' + +test('[DatePanel] 测试范围选择取消面板联动', async ({ page }) => { + page.on('pageerror', (exception) => expect(exception).toBeNull()) + await page.goto('date-panel#unlink-panels') + await page.locator('.settings-btn').first().click() + await page.locator('label').filter({ hasText: '单示例' }).click() + + await page.locator('.tiny-picker-panel__icon-btn').first().click() + await page.locator('div').filter({ hasText: /^10$/ }).first().click() + await page.getByText('19').nth(1).click() + await expect(page.locator('.value')).toHaveText('默认启用面板联动:[ "2024-01-10", "2024-02-19" ]') + + await page.locator('div:nth-child(2) > .tiny-date-range-picker__header > button:nth-child(3)').click() + await page.getByText('14').nth(2).click() + await page.getByText('12', { exact: true }).nth(3).click() + await expect(page.locator('.value1')).toHaveText('关闭面板联动:[ "2025-01-14", "2026-02-12" ]') +}) diff --git a/examples/sites/demos/pc/app/date-panel/unlink-panels.vue b/examples/sites/demos/pc/app/date-panel/unlink-panels.vue new file mode 100644 index 0000000000..519be926b0 --- /dev/null +++ b/examples/sites/demos/pc/app/date-panel/unlink-panels.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/examples/sites/demos/pc/app/date-panel/webdoc/date-panel.js b/examples/sites/demos/pc/app/date-panel/webdoc/date-panel.js index 1b1d0dff51..4a66864aed 100644 --- a/examples/sites/demos/pc/app/date-panel/webdoc/date-panel.js +++ b/examples/sites/demos/pc/app/date-panel/webdoc/date-panel.js @@ -77,6 +77,20 @@ export default { }, codeFiles: ['custom-weeks.vue'] }, + { + demoId: 'unlink-panels', + name: { + 'zh-CN': '面板联动', + 'en-US': 'Panel linkage' + }, + desc: { + 'zh-CN': + '

范围选择时,默认情况下,在开始日期面板中单击上一月或上一年箭头图标时,结束日期面板中日期也联动切换到上一月或上一年。在结束日期面板中切换下一月或下一年时,开始日期面板也随之联动。但若配置 unlink-panels 属性为 true,面板之间就不再联动,切换年月时只对当前面板生效。

\n', + 'en-US': + '

When selecting a range, by default, when clicking the previous month or year arrow icon in the start date panel, the date in the end date panel will also switch to the previous month or year. When switching to the next month or year in the end date panel, the start date panel will also be linked accordingly. But if theunlink panelsattribute is set to true, the panels will no longer be linked and will only be effective for the current panel when switching year and month.

\n' + }, + codeFiles: ['unlink-panels.vue'] + }, { demoId: 'event', name: { diff --git a/examples/sites/demos/pc/app/date-picker/align.spec.ts b/examples/sites/demos/pc/app/date-picker/align.spec.ts index f3cdfac156..f422aae265 100644 --- a/examples/sites/demos/pc/app/date-picker/align.spec.ts +++ b/examples/sites/demos/pc/app/date-picker/align.spec.ts @@ -1,5 +1,8 @@ import { expect, test } from '@playwright/test' +test.use({ + viewport: { width: 1920, height: 1080 } +}) test('[DatePicker] 测试对齐方式', async ({ page }) => { page.on('pageerror', (exception) => expect(exception).toBeNull()) await page.goto('date-picker#align') diff --git a/examples/sites/demos/pc/app/date-picker/webdoc/date-picker.js b/examples/sites/demos/pc/app/date-picker/webdoc/date-picker.js index 102bfdcf81..231b290fc3 100644 --- a/examples/sites/demos/pc/app/date-picker/webdoc/date-picker.js +++ b/examples/sites/demos/pc/app/date-picker/webdoc/date-picker.js @@ -208,13 +208,13 @@ export default { demoId: 'unlink-panels', name: { 'zh-CN': '面板联动', - 'en-US': 'Default time setting when the selector is enabled' + 'en-US': 'Panel linkage' }, desc: { 'zh-CN': '

范围选择时,默认情况下,在开始日期面板中单击上一月或上一年箭头图标时,结束日期面板中日期也联动切换到上一月或上一年。在结束日期面板中切换下一月或下一年时,开始日期面板也随之联动。但若配置 unlink-panels 属性为 true,面板之间就不再联动,切换年月时只对当前面板生效。

\n', 'en-US': - '

default-value property specifies the date that is selected by default when the date picker panel opens.

\n' + '

When selecting a range, by default, when clicking the previous month or year arrow icon in the start date panel, the date in the end date panel will also switch to the previous month or year. When switching to the next month or year in the end date panel, the start date panel will also be linked accordingly. But if theunlink panelsattribute is set to true, the panels will no longer be linked and will only be effective for the current panel when switching year and month.

\n' }, codeFiles: ['unlink-panels.vue'] }, diff --git a/packages/renderless/src/date-panel/vue.ts b/packages/renderless/src/date-panel/vue.ts index e54062b479..cf56a189e8 100644 --- a/packages/renderless/src/date-panel/vue.ts +++ b/packages/renderless/src/date-panel/vue.ts @@ -100,7 +100,7 @@ const initState = ({ reactive, computed, api, i18n, designConfig, props }) => { shortcuts: props.shortcuts || [], visible: false, currentView: DATEPICKER.Date, - disabledDate: props.disabledDate || '', + disabledDate: props.disabledDate || null, cellClassName: '', selectableRange: [], firstDayOfWeek: props.firstDayOfWeek || 7, diff --git a/packages/renderless/src/date-range/index.ts b/packages/renderless/src/date-range/index.ts index e148c02b3b..de259ecb9e 100644 --- a/packages/renderless/src/date-range/index.ts +++ b/packages/renderless/src/date-range/index.ts @@ -17,12 +17,14 @@ import { formatDate, modifyDate, modifyTime, + toDate, nextYear, prevYear, nextMonth, prevMonth, modifyWithTimeString } from '../common/deps/date-util' +import { DATEPICKER } from './../common/index' export const calcDefaultValue = (defaultVal) => { if (Array.isArray(defaultVal)) { @@ -213,7 +215,10 @@ export const handleClear = emit('pick', null) } -export const handleChangeRange = (state) => (val) => { +export const handleChangeRange = (state, props) => (val) => { + if (props.readonly) { + return + } state.minDate = val.minDate state.maxDate = val.maxDate state.rangeState = val.rangeState @@ -366,8 +371,11 @@ export const handleTimeChange = } export const handleRangePick = - ({ api, state, t }) => + ({ api, state, props, t }) => (val, close = true) => { + if (props.readonly) { + return + } const defaultTime = state.defaultTime || [] let minDateVal = val.minDate let maxDateVal = val.maxDate @@ -404,7 +412,7 @@ export const handleRangePick = api.handleConfirm() } -export const handleShortcutClick = (state, api) => (shortcut) => { +export const handleShortcutClick = (state, api, props) => (shortcut) => { if (shortcut.type) { state.singleSelect = true state.shortcutType = shortcut.type @@ -424,7 +432,15 @@ export const handleShortcutClick = (state, api) => (shortcut) => { if (shortcut.onClick) { const picker = { $emit: (type, [start, end]) => { - api.doPick(start, end) + // 面板直接使用快捷选项 + if (props.shortcuts?.length) { + state.value = [start, end] + state.leftDate = start + state.rightDate = end + api.handleRangePick({ minDate: start, maxDate: end }) + } else { + api.doPick(start, end) + } } } @@ -539,10 +555,15 @@ export const rightPrevMonth = (state.rightDate = prevMonth(state.rightDate)) export const handleConfirm = - ({ api, emit, state }) => + ({ api, emit, state, props, t }) => (visible = false) => { if (api.isValidValue([state.minDate, state.maxDate])) { emit('pick', [state.minDate, state.maxDate], visible) + const defaultFormat = props.type === 'daterange' ? DATEPICKER.DateFormats.date : DATEPICKER.DateFormats.datetime + const start = formatDate(state.minDate, props.format || defaultFormat, t) + const end = formatDate(state.maxDate, props.format || defaultFormat, t) + emit('update:modelValue', [start, end]) + emit('select-change', [start, end]) } } @@ -596,3 +617,19 @@ export const watchPickerVisible = state.maxRangeDate = constants.endDate } } + +export const watchModelValue = + ({ state }) => + (val) => { + const newVal = toDate(val?.[0]) + const newVal1 = toDate(val?.[1]) + if (newVal && newVal1) { + const start = modifyDate(newVal, newVal.getFullYear(), newVal.getMonth(), newVal.getUTCDate()) + const end = modifyDate(newVal1, newVal1.getFullYear(), newVal1.getMonth(), newVal1.getUTCDate()) + state.value = [start, end] + state.minDate = start + state.maxDate = end + state.leftDate = start + state.rightDate = end + } + } diff --git a/packages/renderless/src/date-range/vue.ts b/packages/renderless/src/date-range/vue.ts index 3cce39cec5..f59adef6ef 100644 --- a/packages/renderless/src/date-range/vue.ts +++ b/packages/renderless/src/date-range/vue.ts @@ -50,7 +50,8 @@ import { computerBtnDisabled, computerLabel, computerEnableYearArrow, - watchPickerVisible + watchPickerVisible, + watchModelValue } from './index' import { nextMonth, extractDateFormat, extractTimeFormat } from '../common/deps/date-util' @@ -77,14 +78,15 @@ export const api = [ 'handleMinTimeClose', 'handleDateChange', 'handleMaxTimeClose', - 'isValidValue' + 'isValidValue', + 'watchModelValue' ] -const initState = ({ reactive, computed, api, constants, designConfig }) => { +const initState = ({ reactive, computed, api, constants, designConfig, props }) => { const state = reactive({ popperElm: null, - popperClass: '', - value: [], + popperClass: props.popperClass || '', + value: props.modelValue || [], defaultValue: null, defaultTime: null, minDate: '', @@ -97,16 +99,16 @@ const initState = ({ reactive, computed, api, constants, designConfig }) => { leftDate: new Date(), rightDate: nextMonth(new Date()), rangeState: { endDate: null, selecting: false, row: null, column: null }, - showTime: false, - format: '', + showTime: props.type === 'datetimerange' || false, + format: props.format || '', arrowControl: false, - unlinkPanels: false, + unlinkPanels: props.unlinkPanels || false, firstDayOfWeek: 7, minTimePickerVisible: false, maxTimePickerVisible: false, - shortcuts: '', + shortcuts: props.shortcuts || '', visible: '', - disabledDate: '', + disabledDate: props.disabledDate || null, cellClassName: '', dateUserInput: { min: null, max: null }, timeUserInput: { min: null, max: null }, @@ -138,7 +140,8 @@ const initState = ({ reactive, computed, api, constants, designConfig }) => { return state } -const initWatch = ({ watch, state, api }) => { +const initWatch = ({ watch, state, api, props }) => { + watch(() => props.modelValue, api.watchModelValue, { immediate: true }) watch(() => state.minDate, api.watchMinDate) watch(() => state.maxDate, api.watchMaxDate) watch(() => state.minTimePickerVisible, api.watchMinTimePickerVisible) @@ -149,14 +152,14 @@ const initWatch = ({ watch, state, api }) => { watch(() => state.visible, api.watchPickerVisible) } -const initApi = ({ api, state, t, vm, nextTick, emit, constants }) => { +const initApi = ({ api, state, t, vm, nextTick, emit, constants, props }) => { Object.assign(api, { t, state, computerLabel: computerLabel({ state, t }), resetView: resetView({ state }), watchMaxDate: watchMaxDate({ state, vm }), - handleChangeRange: handleChangeRange(state), + handleChangeRange: handleChangeRange(state, props), handleMaxTimeClose: handleMaxTimeClose(state), handleMinTimeClose: handleMinTimeClose(state), isValidValue: isValidValue({ state }), @@ -187,12 +190,13 @@ const initApi = ({ api, state, t, vm, nextTick, emit, constants }) => { watchDefault: watchDefault({ state }), handleClear: handleClear({ emit, state }), setTimeFormat: setTimeFormat({ nextTick, vm, state }), - handleConfirm: handleConfirm({ api, emit, state }), - handleRangePick: handleRangePick({ api, state, t }), - handleShortcutClick: handleShortcutClick(state, api), + handleConfirm: handleConfirm({ api, emit, state, props, t }), + handleRangePick: handleRangePick({ api, state, props, t }), + handleShortcutClick: handleShortcutClick(state, api, props), computerBtnDisabled: computerBtnDisabled({ state, api }), computerEnableYearArrow: computerEnableYearArrow(state), - watchPickerVisible: watchPickerVisible({ state, constants }) + watchPickerVisible: watchPickerVisible({ state, constants }), + watchModelValue: watchModelValue({ state, api }) }) } @@ -203,10 +207,10 @@ export const renderless = ( ) => { const api = {} const emit = props.emitter ? props.emitter.emit : $emit - const state = initState({ reactive, computed, api, constants, designConfig }) + const state = initState({ reactive, computed, api, constants, designConfig, props }) - initApi({ api, state, t, vm, nextTick, emit, constants }) - initWatch({ watch, state, api }) + initApi({ api, state, t, vm, nextTick, emit, constants, props }) + initWatch({ watch, state, api, props }) return api } diff --git a/packages/renderless/src/date-table/index.ts b/packages/renderless/src/date-table/index.ts index ab7bede468..468cc017f1 100644 --- a/packages/renderless/src/date-table/index.ts +++ b/packages/renderless/src/date-table/index.ts @@ -448,6 +448,9 @@ const getTarget = (event) => { export const handleClick = ({ api, emit, props, state }) => (event) => { + if (props.readonly) { + return + } let target = getTarget(event) if (target.tagName !== 'TD') { diff --git a/packages/renderless/src/time/index.ts b/packages/renderless/src/time/index.ts index c9a326a8cc..5842a86a62 100644 --- a/packages/renderless/src/time/index.ts +++ b/packages/renderless/src/time/index.ts @@ -40,6 +40,14 @@ export const watchVisible = state.oldValue = state.value vm.$refs.spinner.emitSelectRange('hours') api.adjustSpinners() + + // 若超出浏览器视窗则修改时间面板位置 + const element = vm.$el + const { left, width } = element.getBoundingClientRect() + if (left + width > document.documentElement.clientWidth) { + element.style.left = 'unset' + element.style.right = 0 + } }) } else { state.needInitAdjust = true diff --git a/packages/vue/src/date-range/__tests__/date-range.test.tsx b/packages/vue/src/date-range/__tests__/date-range.test.tsx new file mode 100644 index 0000000000..6309652ecd --- /dev/null +++ b/packages/vue/src/date-range/__tests__/date-range.test.tsx @@ -0,0 +1,17 @@ +import { mountPcMode } from '@opentiny-internal/vue-test-utils' +import { describe, expect, test } from 'vitest' +import DateRange from '@opentiny/vue-date-range' +import { nextTick } from 'vue' + +describe('PC Mode', () => { + const mount = mountPcMode + + test('value & format', async () => { + let value = ['2025-01-15', '2025-02-15'] + let format = 'yyyy-MM-dd' + const wrapper = mount(() => ) + + await nextTick() + expect(wrapper.find('.tiny-picker-panel').exists()).toBe(true) + }) +}) diff --git a/packages/vue/src/date-range/src/index.ts b/packages/vue/src/date-range/src/index.ts index 050ab16909..a3cc69b6b3 100644 --- a/packages/vue/src/date-range/src/index.ts +++ b/packages/vue/src/date-range/src/index.ts @@ -30,6 +30,38 @@ export default defineComponent({ timeEditable: { type: Boolean, default: true + }, + type: { + type: String, + default: () => 'daterange' + }, + modelValue: { + type: Array, + default: () => [] + }, + format: { + type: String, + default: '' + }, + readonly: { + type: Boolean, + default: false + }, + shortcuts: { + type: Array, + default: () => [] + }, + disabledDate: { + type: Function, + default: null + }, + popperClass: { + type: String, + default: '' + }, + unlinkPanels: { + type: Boolean, + default: false } }, setup(props, context) { diff --git a/packages/vue/src/date-range/src/mobile-first.vue b/packages/vue/src/date-range/src/mobile-first.vue index 8b300faca4..9c2d4641c1 100644 --- a/packages/vue/src/date-range/src/mobile-first.vue +++ b/packages/vue/src/date-range/src/mobile-first.vue @@ -8,7 +8,12 @@ >
-
+
@@ -211,13 +212,14 @@ :show-week-number="showWeekNumber" :format-weeks="formatWeeks" @pick="handleRangePick" + :readonly="readonly" >
-