From 5b064acf01273c08719c8990568a8bf279e7ab97 Mon Sep 17 00:00:00 2001 From: Kagol Date: Tue, 27 Feb 2024 17:22:12 +0800 Subject: [PATCH 01/10] chore: add comments to date-panel --- packages/vue/src/date-panel/src/pc.vue | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/vue/src/date-panel/src/pc.vue b/packages/vue/src/date-panel/src/pc.vue index 8375cf14e6..1f07347bbc 100644 --- a/packages/vue/src/date-panel/src/pc.vue +++ b/packages/vue/src/date-panel/src/pc.vue @@ -24,6 +24,8 @@ >
+ +
+
+
+ +
+
+ @@ -21,6 +22,7 @@ export default { dateTimeValue: '', weekValue: '', monthValue: '', + quarterValue: '', yearValue: '' } } diff --git a/packages/renderless/src/common/index.ts b/packages/renderless/src/common/index.ts index da57533cfb..8f0b5531c5 100644 --- a/packages/renderless/src/common/index.ts +++ b/packages/renderless/src/common/index.ts @@ -128,7 +128,7 @@ export const DATE = { } const TriggerTypes = - 'date,datetime,time,time-select,week,month,year,years,yearrange,daterange,monthrange,timerange,datetimerange,dates' + 'date,datetime,time,time-select,week,month,year,years,yearrange,daterange,monthrange,timerange,datetimerange,dates,quarter' export const DATEPICKER = { Day: 'day', @@ -184,6 +184,7 @@ export const DATEPICKER = { }, Time: 'time', TimeRange: 'timerange', + Quarter: 'quarter', IconTime: 'icon-time', IconDate: 'icon-Calendar', DateRange: 'daterange', diff --git a/packages/renderless/src/picker/index.ts b/packages/renderless/src/picker/index.ts index dbce4779ea..ee805be140 100644 --- a/packages/renderless/src/picker/index.ts +++ b/packages/renderless/src/picker/index.ts @@ -21,7 +21,16 @@ import globalTimezone from './timezone' const iso8601Reg = /^\d{4}-\d{2}-\d{2}(.)\d{2}:\d{2}:\d{2}(.+)$/ export const getPanel = - ({ DatePanel, DateRangePanel, MonthRangePanel, YearRangePanel, TimePanel, TimeRangePanel, TimeSelect }) => + ({ + DatePanel, + DateRangePanel, + MonthRangePanel, + YearRangePanel, + TimePanel, + TimeRangePanel, + QuarterPanel, + TimeSelect + }) => (type) => { if (type === DATEPICKER.DateRange || type === DATEPICKER.DateTimeRange) { return DateRangePanel @@ -35,6 +44,8 @@ export const getPanel = return TimePanel } else if (type === DATEPICKER.TimeSelect) { return TimeSelect + } else if (type === DATEPICKER.Quarter) { + return QuarterPanel } return DatePanel @@ -396,6 +407,13 @@ const getDatesOfTypeValueResolveMap = (api) => ({ } }) +const MONTH_QUARTER_MAP = { + 0: 1, + 3: 2, + 6: 3, + 9: 4 +} + export const typeValueResolveMap = ({ api, props, t }) => () => ({ @@ -413,7 +431,11 @@ export const typeValueResolveMap = years: getDatesOfTypeValueResolveMap(api), yearrange: getDatesOfTypeValueResolveMap(api), number: getNumberOfTypeValueResolveMap(), - dates: getDatesOfTypeValueResolveMap(api) + dates: getDatesOfTypeValueResolveMap(api), + quarter: { + formatter: (value) => `${value.getFullYear()}-Q${MONTH_QUARTER_MAP[value.getMonth()]}`, + parser: api.dateParser + } }) export const firstInputId = diff --git a/packages/renderless/src/quarter-panel/index.ts b/packages/renderless/src/quarter-panel/index.ts new file mode 100644 index 0000000000..e3f56e9f85 --- /dev/null +++ b/packages/renderless/src/quarter-panel/index.ts @@ -0,0 +1,13 @@ +const QUARTER_MAP = { + 0: 0, + 1: 3, + 2: 6, + 3: 9 +} + +export const handleQuarterTableClick = + ({ emit }) => + (event) => { + const currentDate = new Date(2024, QUARTER_MAP[event.target.cellIndex], 1) + emit('pick', currentDate) + } diff --git a/packages/renderless/src/quarter-panel/vue.ts b/packages/renderless/src/quarter-panel/vue.ts new file mode 100644 index 0000000000..41bb9b0661 --- /dev/null +++ b/packages/renderless/src/quarter-panel/vue.ts @@ -0,0 +1,19 @@ +import { handleQuarterTableClick } from './index' + +export const api = ['state', 'handleQuarterTableClick'] + +export const renderless = (props, { reactive }, { emit }) => { + const api = {} + + const state = reactive({ + visible: false, + rows: [{ text: 'Q1' }, { text: 'Q2' }, { text: 'Q3' }, { text: 'Q4' }] + }) + + Object.assign(api, { + state, + handleQuarterTableClick: handleQuarterTableClick({ emit }) + }) + + return api +} diff --git a/packages/theme/src/quarter-panel/index.less b/packages/theme/src/quarter-panel/index.less new file mode 100644 index 0000000000..9267a4db07 --- /dev/null +++ b/packages/theme/src/quarter-panel/index.less @@ -0,0 +1,33 @@ +@import '../custom.less'; +@import './vars.less'; + +@quarter-panel-prefix-cls: ~'@{css-prefix}quarter-panel'; + +.@{quarter-panel-prefix-cls} { + .component-css-vars-quarter-panel(); + + width: 288px; + padding: 12px; + background: #fff; + box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; + + &__header { + margin: 12px; + text-align: center; + padding-bottom: 12px; + border-bottom: 1px solid #dfe1e6; + } + + &__content { + + } + + &__table { + width: 100%; + text-align: center; + + td { + padding: 16px 0; + } + } +} \ No newline at end of file diff --git a/packages/theme/src/quarter-panel/vars.less b/packages/theme/src/quarter-panel/vars.less new file mode 100644 index 0000000000..4b9d0b8935 --- /dev/null +++ b/packages/theme/src/quarter-panel/vars.less @@ -0,0 +1 @@ +.component-css-vars-quarter-panel() {} diff --git a/packages/vue/src/picker/package.json b/packages/vue/src/picker/package.json index f059bf2a0f..33639355eb 100644 --- a/packages/vue/src/picker/package.json +++ b/packages/vue/src/picker/package.json @@ -22,6 +22,7 @@ "@opentiny/vue-date-panel": "workspace:~", "@opentiny/vue-date-range": "workspace:~", "@opentiny/vue-month-range": "workspace:~", + "@opentiny/vue-quarter-panel": "workspace:~", "@opentiny/vue-time": "workspace:~", "@opentiny/vue-time-range": "workspace:~", "@opentiny/vue-time-panel": "workspace:~", diff --git a/packages/vue/src/picker/src/pc.vue b/packages/vue/src/picker/src/pc.vue index 305c57c4e1..8b958c9ed4 100644 --- a/packages/vue/src/picker/src/pc.vue +++ b/packages/vue/src/picker/src/pc.vue @@ -162,6 +162,7 @@ import MonthRangePanel from '@opentiny/vue-month-range' import YearRangePanel from '@opentiny/vue-year-range' import TimePanel from '@opentiny/vue-time' import TimeRangePanel from '@opentiny/vue-time-range' +import QuarterPanel from '@opentiny/vue-quarter-panel' import TimeSelect from '@opentiny/vue-time-panel' import TinyTooltip from '@opentiny/vue-tooltip' import FilterBox from '@opentiny/vue-filter-box' @@ -193,6 +194,7 @@ export default defineComponent({ YearRangePanel, TimePanel, TimeRangePanel, + QuarterPanel, TimeSelect } }) diff --git a/packages/vue/src/quarter-panel/index.ts b/packages/vue/src/quarter-panel/index.ts new file mode 100644 index 0000000000..7bbbad6592 --- /dev/null +++ b/packages/vue/src/quarter-panel/index.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2022 - present TinyVue Authors. + * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ +import QuerterPanel from './src/pc.vue' +import { version } from './package.json' + +/* istanbul ignore next */ +QuerterPanel.install = function (Vue) { + Vue.component(QuerterPanel.name, QuerterPanel) +} + +QuerterPanel.version = version + +/* istanbul ignore next */ +if (process.env.BUILD_TARGET === 'runtime') { + if (typeof window !== 'undefined' && window.Vue) { + QuerterPanel.install(window.Vue) + } +} + +export default QuerterPanel diff --git a/packages/vue/src/quarter-panel/package.json b/packages/vue/src/quarter-panel/package.json new file mode 100644 index 0000000000..8327c0d73b --- /dev/null +++ b/packages/vue/src/quarter-panel/package.json @@ -0,0 +1,23 @@ +{ + "name": "@opentiny/vue-quarter-panel", + "version": "3.7.0", + "description": "", + "main": "lib/index.js", + "module": "index.ts", + "sideEffects": false, + "type": "module", + "devDependencies": { + "@opentiny-internal/vue-test-utils": "workspace:*", + "vitest": "^0.31.0" + }, + "scripts": { + "build": "pnpm -w build:ui $npm_package_name", + "//postversion": "pnpm build" + }, + "dependencies": { + "@opentiny/vue-common": "workspace:~", + "@opentiny/vue-icon": "workspace:~", + "@opentiny/vue-renderless": "workspace:~" + }, + "license": "MIT" +} diff --git a/packages/vue/src/quarter-panel/src/pc.vue b/packages/vue/src/quarter-panel/src/pc.vue new file mode 100644 index 0000000000..080be8ad94 --- /dev/null +++ b/packages/vue/src/quarter-panel/src/pc.vue @@ -0,0 +1,29 @@ + + + From aef57d94e2f1aa931fce2a581f65fc037695acd6 Mon Sep 17 00:00:00 2001 From: Kagol Date: Wed, 28 Feb 2024 16:37:39 +0800 Subject: [PATCH 03/10] feat(date-picker): quarter-panel support prevYear/nextYear/pickYear --- .../renderless/src/quarter-panel/index.ts | 45 +++++++++++- packages/renderless/src/quarter-panel/vue.ts | 32 +++++++-- packages/theme/src/quarter-panel/index.less | 5 ++ packages/vue/src/quarter-panel/src/pc.vue | 69 +++++++++++++++---- 4 files changed, 132 insertions(+), 19 deletions(-) diff --git a/packages/renderless/src/quarter-panel/index.ts b/packages/renderless/src/quarter-panel/index.ts index e3f56e9f85..b23caca6c5 100644 --- a/packages/renderless/src/quarter-panel/index.ts +++ b/packages/renderless/src/quarter-panel/index.ts @@ -1,3 +1,6 @@ +import { modifyDate, nextYear, prevYear } from '../common/deps/date-util' +import { DATEPICKER } from '../common' + const QUARTER_MAP = { 0: 0, 1: 3, @@ -6,8 +9,46 @@ const QUARTER_MAP = { } export const handleQuarterTableClick = - ({ emit }) => + ({ state, emit }) => (event) => { - const currentDate = new Date(2024, QUARTER_MAP[event.target.cellIndex], 1) + const currentDate = new Date(state.date.getFullYear(), QUARTER_MAP[event.target.cellIndex], 1) emit('pick', currentDate) } + +export const showYearPicker = + ({ state }) => + () => + (state.currentView = DATEPICKER.Year) + +export const cusPrevYear = + ({ state }) => + () => { + if (state.currentView === DATEPICKER.Year) { + state.startYear = state.startYear - DATEPICKER.PanelYearNum + } else { + state.date = prevYear(state.date) + } + } + +export const cusNextYear = + ({ state }) => + () => { + if (state.currentView === DATEPICKER.Year) { + state.startYear = state.startYear + DATEPICKER.PanelYearNum + } else { + state.date = nextYear(state.date) + } + } + +export const handleYearPick = + ({ state }) => + (value) => { + state.currentView = DATEPICKER.Quarter + state.date = modifyDate(state.date, value, state.date.getMonth(), state.date.getDate()) + } + +export const getYearLabel = + ({ state, t }) => + () => { + return state.date.getFullYear() + } diff --git a/packages/renderless/src/quarter-panel/vue.ts b/packages/renderless/src/quarter-panel/vue.ts index 41bb9b0661..d77110bc52 100644 --- a/packages/renderless/src/quarter-panel/vue.ts +++ b/packages/renderless/src/quarter-panel/vue.ts @@ -1,18 +1,42 @@ -import { handleQuarterTableClick } from './index' +import { DATEPICKER } from '../common' +import { + handleQuarterTableClick, + showYearPicker, + handleYearPick, + cusPrevYear, + cusNextYear, + getYearLabel +} from './index' -export const api = ['state', 'handleQuarterTableClick'] +export const api = [ + 'state', + 'handleQuarterTableClick', + 'showYearPicker', + 'handleYearPick', + 'cusPrevYear', + 'cusNextYear' +] -export const renderless = (props, { reactive }, { emit }) => { +export const renderless = (props, { reactive, computed }, { emit, t }) => { const api = {} const state = reactive({ + date: new Date(), visible: false, + currentView: DATEPICKER.Quarter, + yearLabel: computed(() => api.getYearLabel()), + startYear: Math.floor(new Date().getFullYear() / 10) * 10, rows: [{ text: 'Q1' }, { text: 'Q2' }, { text: 'Q3' }, { text: 'Q4' }] }) Object.assign(api, { state, - handleQuarterTableClick: handleQuarterTableClick({ emit }) + handleQuarterTableClick: handleQuarterTableClick({ state, emit }), + showYearPicker: showYearPicker({ state }), + handleYearPick: handleYearPick({ state }), + cusPrevYear: cusPrevYear({ state }), + cusNextYear: cusNextYear({ state }), + getYearLabel: getYearLabel({ state, t }) }) return api diff --git a/packages/theme/src/quarter-panel/index.less b/packages/theme/src/quarter-panel/index.less index 9267a4db07..35626275ce 100644 --- a/packages/theme/src/quarter-panel/index.less +++ b/packages/theme/src/quarter-panel/index.less @@ -11,6 +11,11 @@ background: #fff; box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; + table { + table-layout: fixed; + width: 100%; + } + &__header { margin: 12px; text-align: center; diff --git a/packages/vue/src/quarter-panel/src/pc.vue b/packages/vue/src/quarter-panel/src/pc.vue index 080be8ad94..c19063b96a 100644 --- a/packages/vue/src/quarter-panel/src/pc.vue +++ b/packages/vue/src/quarter-panel/src/pc.vue @@ -1,27 +1,70 @@ From 32e7a888c58c21a2491c091a3ee3d5acb8690a59 Mon Sep 17 00:00:00 2001 From: Kagol Date: Fri, 29 Mar 2024 17:35:40 +0800 Subject: [PATCH 09/10] fix(date-picker): [date-picker] fix e2e test failed --- .../sites/demos/pc/app/date-picker/default-value.spec.ts | 4 ++-- examples/sites/demos/pc/app/date-picker/events.spec.ts | 6 +++--- examples/sites/demos/pc/app/date-picker/format.spec.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/sites/demos/pc/app/date-picker/default-value.spec.ts b/examples/sites/demos/pc/app/date-picker/default-value.spec.ts index 933f484ce1..4ebd6d8919 100644 --- a/examples/sites/demos/pc/app/date-picker/default-value.spec.ts +++ b/examples/sites/demos/pc/app/date-picker/default-value.spec.ts @@ -15,11 +15,11 @@ test('[DatePicker] 测试选择器打开时默认时间设置', async ({ page }) await dateInputDefaultTime.press('Enter') await page.getByRole('textbox', { name: '2023-05-20 09:00:00' }).click() - await page.getByRole('cell', { name: '15' }).getByText('15').click() + await page.getByRole('cell', { name: '15' }).getByText('15').last().click() await expect(page.getByRole('textbox', { name: '选择时间' })).toHaveValue('09:00:00') await page.getByRole('textbox').nth(3).click() - await page.getByRole('cell', { name: '10' }).getByText('10').first().click() + await page.getByRole('cell', { name: '10' }).getByText('10').last().click() await page.getByRole('cell', { name: '10' }).getByText('10').nth(1).click() await expect(page.getByRole('textbox', { name: '开始时间' })).toHaveValue('09:00:00') await expect(page.getByRole('textbox', { name: '结束时间' })).toHaveValue('18:00:00') diff --git a/examples/sites/demos/pc/app/date-picker/events.spec.ts b/examples/sites/demos/pc/app/date-picker/events.spec.ts index 4ca92269a5..ce94829413 100644 --- a/examples/sites/demos/pc/app/date-picker/events.spec.ts +++ b/examples/sites/demos/pc/app/date-picker/events.spec.ts @@ -19,7 +19,7 @@ test('[DatePicker] 测试事件', async ({ page }) => { .filter({ hasText: /^blur:$/ }) .getByRole('textbox') .click() - await page.getByRole('cell', { name: '15' }).getByText('15').click() + await page.getByRole('cell', { name: '15' }).getByText('15').last().click() const blurEventMessageDom = page.locator('div').filter({ hasText: '触发 blur 事件' }).nth(1) await expect(blurEventMessageDom).toBeVisible() @@ -33,13 +33,13 @@ test('[DatePicker] 测试事件', async ({ page }) => { .filter({ hasText: /^change:$/ }) .getByRole('textbox') .click() - await page.getByRole('cell', { name: '15' }).getByText('15').click() + await page.getByRole('cell', { name: '15' }).getByText('15').last().click() const changeEventMessageDom = page.locator('div').filter({ hasText: '触发 change 事件,组件绑定值为:' }).nth(1) await expect(changeEventMessageDom).toBeVisible() // onPick 事件 await page.locator('div').filter({ hasText: /^-$/ }).click() - await page.getByRole('cell', { name: '10' }).getByText('10').first().click() + await page.getByRole('cell', { name: '10' }).getByText('10').last().click() const onPickEventMessageDom = page.locator('div').filter({ hasText: '触发 onPick 事件,开始日期为:' }).nth(1) await expect(onPickEventMessageDom).toBeVisible() }) diff --git a/examples/sites/demos/pc/app/date-picker/format.spec.ts b/examples/sites/demos/pc/app/date-picker/format.spec.ts index 7fedd6351b..b8ac19b68d 100644 --- a/examples/sites/demos/pc/app/date-picker/format.spec.ts +++ b/examples/sites/demos/pc/app/date-picker/format.spec.ts @@ -6,7 +6,7 @@ test('[DatePicker] 测试日期格式化', async ({ page }) => { // format: 日期输入框中显示的格式 await page.getByRole('textbox', { name: '2023 年 05 月 24 日' }).first().click() - await page.getByRole('cell', { name: '20' }).getByText('20').click() + await page.getByRole('cell', { name: '20' }).getByText('20').last().click() await expect(page.getByRole('textbox', { name: '2023 年 05 月 20 日' }).first()).toBeVisible() // time-format: 时间输入框中显示的格式 @@ -17,6 +17,6 @@ test('[DatePicker] 测试日期格式化', async ({ page }) => { // value-format: 选中值的格式 await page.locator('.tiny-date-editor input').nth(2).click() - await page.getByRole('cell', { name: '20' }).getByText('20').click() + await page.getByRole('cell', { name: '20' }).getByText('20').last().click() await expect(page.locator('.select-date')).toHaveText(`当前选中时间:${1589904000000}`) }) From a3ebe5d571b30584183558f83485fa0c0fcb1ced Mon Sep 17 00:00:00 2001 From: Kagol Date: Fri, 29 Mar 2024 18:11:16 +0800 Subject: [PATCH 10/10] chore: add node version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3c0ba1e59a..f6162ae974 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "pnpm": ">=6.35" }, "scripts": { - "preinstall": "npx only-allow pnpm", + "preinstall": "node -v && pnpm -v && npx only-allow pnpm", "postinstall": "pnpm build:internals", "prepare": "husky install", "bootstrap": "pnpm --filter=\"!./packages/dist/**\" install",