diff --git a/packages/plugins/@hera/plugin-core/src/client/index.tsx b/packages/plugins/@hera/plugin-core/src/client/index.tsx index 541cc033a..179ab28f6 100644 --- a/packages/plugins/@hera/plugin-core/src/client/index.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/index.tsx @@ -1,12 +1,5 @@ import React from 'react'; -import { - Menu, - Plugin, - RemoteSchemaTemplateManagerProvider, - EditTitleField, - useCollection, - SchemaSettingsDateFormat, -} from '@nocobase/client'; +import { Menu, Plugin, RemoteSchemaTemplateManagerProvider, EditTitleField, useCollection } from '@nocobase/client'; import { remove } from 'lodash'; import { useFieldSchema } from '@formily/react'; import { isValid } from '@formily/shared'; @@ -49,7 +42,7 @@ import { Expression, SignatureInput, } from './components'; -import { AutoComplete, DatePicker, InternalPDFViewer, MenuDesigner } from './schema-components'; +import { AutoComplete, InternalPDFViewer, MenuDesigner } from './schema-components'; import { CreateSubmitActionInitializer, FilterFormItem, @@ -73,6 +66,11 @@ import { } from './schema-initializer/blocks/SheetBlockInitializer'; import AssociationCascader from './schema-components/association-cascader/AssociationCascader'; import { SchemaSettingsDatePickerType } from './schema-settings/SchemaSettingsDatePickerType'; +import { + SchemaSettingsDatePresets, + useCustomPresets, + useCustomPresets1, +} from './schema-settings/SchemaSettingsDatePresets'; export { usePDFViewerRef } from './schema-initializer'; export * from './components/custom-components/custom-components'; @@ -100,6 +98,9 @@ export class PluginCoreClient extends Plugin { this.schemaSettingsManager.addItem('fieldSettings:component:DatePicker', 'datePickerType', { Component: SchemaSettingsDatePickerType, }); + this.schemaSettingsManager.addItem('fieldSettings:component:DatePicker', 'datePresets', { + Component: SchemaSettingsDatePresets, + }); this.schemaSettingsManager.addItem('FormItemSettings', 'hera-divider', { type: 'divider', @@ -153,6 +154,8 @@ export class PluginCoreClient extends Plugin { useGetCustomAssociatedComponents, useGetCustomComponents, usePDFViewerPrintActionProps, + useCustomPresets1, + useCustomPresets, }); this.app.addComponents({ @@ -166,7 +169,6 @@ export class PluginCoreClient extends Plugin { CustomComponentDispatcher, CustomComponentStub, CustomField, - DatePicker, EditTitle, EditTitleField, Expression, diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/DatePicker.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/DatePicker.tsx deleted file mode 100644 index 6683df0bc..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/DatePicker.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { connect, mapProps, mapReadPretty } from '@formily/react'; -import { DatePicker as AntdDatePicker } from 'antd'; -import type { - DatePickerProps as AntdDatePickerProps, - RangePickerProps as AntdRangePickerProps, -} from 'antd/es/date-picker'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { ReadPretty } from './ReadPretty'; -import { getDateRanges, mapDatePicker, mapRangePicker } from './util'; -import dayjs from 'dayjs'; - -interface IDatePickerProps { - utc?: boolean; -} - -type ComposedDatePicker = React.FC & { - ReadPretty?: React.FC; - RangePicker?: React.FC; -}; - -const DatePickerContext = React.createContext({ utc: true }); - -export const useDatePickerContext = () => React.useContext(DatePickerContext); -export const DatePickerProvider = DatePickerContext.Provider; - -const InternalDatePicker: ComposedDatePicker = connect( - AntdDatePicker, - mapProps(mapDatePicker()), - mapReadPretty(ReadPretty.DatePicker), -); - -const InternalRangePicker = connect( - AntdDatePicker.RangePicker, - mapProps(mapRangePicker()), - mapReadPretty(ReadPretty.DateRangePicker), -); - -export const DatePicker = (props) => { - const { utc = true } = useDatePickerContext(); - const value = Array.isArray(props.value) ? props.value[0] : props.value; - props = { utc, ...props }; - return ; -}; - -DatePicker.ReadPretty = ReadPretty.DatePicker; - -DatePicker.RangePicker = function RangePicker(props) { - const { t } = useTranslation(); - const { utc = true } = useDatePickerContext(); - const rangesValue = getDateRanges(); - const presets = [ - { label: t('This year'), value: rangesValue.thisYear }, - ...Array(5) - .fill(0) - .map((_, i) => ({ label: dayjs().year() - i - 1 + '年', value: rangesValue.year(dayjs().year() - i - 1) })), - { label: t('This month'), value: rangesValue.thisMonth }, - { label: t('Last month'), value: rangesValue.lastMonth }, - ]; - props = { utc, presets, ...props }; - return ; -}; - -export default DatePicker; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/ReadPretty.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/ReadPretty.tsx deleted file mode 100644 index 64c8f5780..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/ReadPretty.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { usePrefixCls } from '@formily/antd-v5/esm/__builtins__'; -import { isArr } from '@formily/shared'; -import { getDefaultFormat, str2moment } from '@nocobase/utils/client'; -import type { - DatePickerProps as AntdDatePickerProps, - RangePickerProps as AntdRangePickerProps, -} from 'antd/es/date-picker'; -import cls from 'classnames'; -import dayjs from 'dayjs'; -import React from 'react'; - -type Composed = { - DatePicker: React.FC; - DateRangePicker: React.FC; -}; - -export const ReadPretty: Composed = () => null; - -ReadPretty.DatePicker = function DatePicker(props: any) { - const prefixCls = usePrefixCls('description-date-picker', props); - - if (!props.value) { - return
; - } - - const getLabels = () => { - const format = getDefaultFormat(props) as string; - const m = str2moment(props.value, props); - const labels = dayjs.isDayjs(m) ? m.format(format) : ''; - return isArr(labels) ? labels.join('~') : labels; - }; - return
{getLabels()}
; -}; - -ReadPretty.DateRangePicker = function DateRangePicker(props: any) { - const prefixCls = usePrefixCls('description-text', props); - const format = getDefaultFormat(props); - const getLabels = () => { - const m = str2moment(props.value, props); - if (!m) { - return ''; - } - const labels = m.map((m) => m.format(format)); - return isArr(labels) ? labels.join('~') : labels; - }; - return ( -
- {getLabels()} -
- ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/date-picker.test.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/date-picker.test.tsx deleted file mode 100644 index 6f9a07d02..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/date-picker.test.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import React from 'react'; -import { render, screen, sleep, userEvent, waitFor } from 'testUtils'; -import App1 from '../demos/demo1'; -import App11 from '../demos/demo11'; -import App2 from '../demos/demo2'; -import App3 from '../demos/demo3'; -import App4 from '../demos/demo4'; -import App5 from '../demos/demo5'; -import App6 from '../demos/demo6'; -import App7 from '../demos/demo7'; -import App8 from '../demos/demo8'; -import App9 from '../demos/demo9'; - -describe('DatePicker', () => { - it('basic', async () => { - const { container, getByText } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const input = container.querySelector('input') as HTMLElement; - - await userEvent.click(picker); - await userEvent.type(input, '2023/05/01 00:00:00'); - await userEvent.click(getByText('OK')); - - await waitFor(() => { - expect(input).toHaveValue('2023/05/01 00:00:00'); - // Read pretty - expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - - // TODO: 需要有个方法来固定测试环境的时区 - if (!process.env.GITHUB_ACTIONS) { - // Value - expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument(); - } - }); - }); - - it('GMT', async () => { - const { container, getByText } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const input = container.querySelector('input') as HTMLElement; - - await userEvent.click(picker); - // 清空默认值 - await userEvent.clear(input); - await userEvent.type(input, '2023/05/01 00:00:00'); - await userEvent.click(getByText('OK')); - - expect(input).toHaveValue('2023/05/01 00:00:00'); - // Read pretty - expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - // Value - expect(screen.getByText('2023-05-01T00:00:00.000Z')).toBeInTheDocument(); - }); - - it('non-UTC', async () => { - const { container } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const input = container.querySelector('input') as HTMLElement; - - await userEvent.click(picker); - await userEvent.type(input, '2023/05/01'); - - let selected; - await waitFor(() => { - selected = document.querySelector('.ant-picker-cell-selected') as HTMLElement; - expect(selected).toBeInTheDocument(); - }); - await userEvent.click(selected); - - expect(input).toHaveValue('2023/05/01'); - // Read pretty - expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - // Value - expect(screen.getByText('2023-05-01')).toBeInTheDocument(); - }); -}); - -describe('RangePicker', () => { - it('GMT', async () => { - const { container, getByPlaceholderText } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const startInput = getByPlaceholderText('Start date'); - const endInput = getByPlaceholderText('End date'); - - await userEvent.click(picker); - await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); - await userEvent.click(document.querySelector('[title="2023-05-02"]') as HTMLElement); - - await waitFor(() => expect(startInput).toHaveValue('2023-05-01')); - await waitFor(() => expect(endInput).toHaveValue('2023-05-02')); - // Read pretty - expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument(); - // Value - expect(screen.getByText('2023-05-01T00:00:00.000Z ~ 2023-05-02T23:59:59.999Z')).toBeInTheDocument(); - }); - - it('non-GMT', async () => { - const { container, getByPlaceholderText } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const startInput = getByPlaceholderText('Start date'); - const endInput = getByPlaceholderText('End date'); - - await userEvent.click(picker); - await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); - await userEvent.click(document.querySelector('[title="2023-05-02"]') as HTMLElement); - - await waitFor(() => { - expect(startInput).toHaveValue('2023-05-01'); - expect(endInput).toHaveValue('2023-05-02'); - // Read pretty - expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument(); - - if (!process.env.GITHUB_ACTIONS) { - // Value - expect(screen.getByText(/2023-04-30t16:00:00\.000z ~ 2023-05-02t15:59:59\.999z/i)).toBeInTheDocument(); - } - }); - }); - - it('non-UTC', async () => { - const { container, getByPlaceholderText } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const startInput = getByPlaceholderText('Start date'); - const endInput = getByPlaceholderText('End date'); - - await userEvent.click(picker); - await sleep(); - await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); - await userEvent.click(document.querySelector('[title="2023-05-02"]') as HTMLElement); - - await waitFor(() => expect(startInput).toHaveValue('2023-05-01')); - await waitFor(() => expect(endInput).toHaveValue('2023-05-02')); - - // Read pretty - await waitFor(() => - expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument(), - ); - - // Value - await waitFor(() => expect(screen.getByText('2023-05-01 ~ 2023-05-02')).toBeInTheDocument()); - }); - - it('showTime=false,gmt=true,utc=true', async () => { - const { container } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const input = container.querySelector('input') as HTMLElement; - - await userEvent.click(picker); - await userEvent.type(input, '2023/05/01'); - await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); - - await waitFor(() => { - expect(input).toHaveValue('2023/05/01'); - // Read pretty - expect(screen.getByText('2023/05/01', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - - // Value - expect(screen.getByText('2023-05-01T00:00:00.000Z')).toBeInTheDocument(); - }); - }); - - it('showTime=false,gmt=false,utc=true', async () => { - const { container } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const input = container.querySelector('input') as HTMLElement; - - await userEvent.click(picker); - await userEvent.type(input, '2023/05/01'); - await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); - - await waitFor(() => { - expect(input).toHaveValue('2023/05/01'); - // Read pretty - expect(screen.getByText('2023/05/01', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - - if (!process.env.GITHUB_ACTIONS) { - // Value - // 当 gmt 为 false 时是按照客户端本地时区进行计算的,但是这里的测试环境是 UTC+8,所以会有 8 小时的误差 - expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument(); - } - }); - }); - - it('showTime=false,gmt=true,utc=true & not input', async () => { - const currentDateString = new Date().toISOString().split('T')[0]; - const { container } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - - await userEvent.click(picker); - const btn = document.querySelector(`[title="${currentDateString}"]`); - expect(btn).toBeInTheDocument(); - await userEvent.click(btn as HTMLElement); - - await waitFor(() => { - // Read pretty - expect( - screen.getByText(currentDateString.replace(/-/g, '/'), { selector: '.ant-description-date-picker' }), - ).toBeInTheDocument(); - - // Value - expect(screen.getByText(`${currentDateString}T00:00:00.000Z`)).toBeInTheDocument(); - }); - }); - - // fix T-1506 - it('shortcut', async () => { - const { container } = render(); - - await sleep(); - - const picker = container.querySelector('.ant-picker') as HTMLElement; - const startInput = screen.getByPlaceholderText('Start date'); - const endInput = screen.getByPlaceholderText('End date'); - - await userEvent.click(picker); - - // shortcut: Today - await userEvent.click(screen.getByText(/today/i)); - await sleep(); - - // 因为 Today 快捷键的值是动态生成的,所以这里没有断言具体的值 - await waitFor(() => expect(startInput.getAttribute('value')).toBeTruthy()); - await waitFor(() => expect(endInput.getAttribute('value')).toBeTruthy()); - }); -}); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/getDateRanges.test.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/getDateRanges.test.ts deleted file mode 100644 index 41a0e5ae1..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/getDateRanges.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import dayjs from 'dayjs'; -import { getDateRanges } from '../util'; - -describe('getDateRanges', () => { - const dateRanges = getDateRanges(); - - it('today', () => { - const [start, end] = dateRanges.today(); - expect(start.toISOString()).toBe(dayjs().startOf('day').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('day').toISOString()); - }); - - test('yesterday', () => { - const [start, end] = dateRanges.yesterday(); - expect(dayjs(start).isSame(dayjs().subtract(1, 'day'), 'day')).toBe(true); - expect(dayjs(end).isSame(dayjs().subtract(1, 'day'), 'day')).toBe(true); - }); - - test('tomorrow', () => { - const [start, end] = dateRanges.tomorrow(); - expect(dayjs(start).isSame(dayjs().add(1, 'day'), 'day')).toBe(true); - expect(dayjs(end).isSame(dayjs().add(1, 'day'), 'day')).toBe(true); - }); - - it('lastWeek', () => { - const [start, end] = dateRanges.lastWeek(); - expect(start.toISOString()).toBe(dayjs().add(-1, 'week').startOf('isoWeek').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(-1, 'week').endOf('isoWeek').toISOString()); - }); - - it('thisWeek', () => { - const [start, end] = dateRanges.thisWeek(); - expect(start.toISOString()).toBe(dayjs().startOf('isoWeek').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('isoWeek').toISOString()); - }); - - it('nextWeek', () => { - const [start, end] = dateRanges.nextWeek(); - expect(start.toISOString()).toBe(dayjs().add(1, 'week').startOf('isoWeek').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(1, 'week').endOf('isoWeek').toISOString()); - }); - - it('lastMonth', () => { - const [start, end] = dateRanges.lastMonth(); - expect(start.toISOString()).toBe(dayjs().add(-1, 'month').startOf('month').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(-1, 'month').endOf('month').toISOString()); - }); - - it('thisMonth', () => { - const [start, end] = dateRanges.thisMonth(); - expect(start.toISOString()).toBe(dayjs().startOf('month').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('month').toISOString()); - }); - - it('nextMonth', () => { - const [start, end] = dateRanges.nextMonth(); - expect(start.toISOString()).toBe(dayjs().add(1, 'month').startOf('month').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(1, 'month').endOf('month').toISOString()); - }); - - it('lastQuarter', () => { - const [start, end] = dateRanges.lastQuarter(); - expect(start.toISOString()).toBe(dayjs().add(-1, 'quarter').startOf('quarter').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(-1, 'quarter').endOf('quarter').toISOString()); - }); - - it('thisQuarter', () => { - const [start, end] = dateRanges.thisQuarter(); - expect(start.toISOString()).toBe(dayjs().startOf('quarter').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('quarter').toISOString()); - }); - - it('nextQuarter', () => { - const [start, end] = dateRanges.nextQuarter(); - expect(start.toISOString()).toBe(dayjs().add(1, 'quarter').startOf('quarter').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(1, 'quarter').endOf('quarter').toISOString()); - }); - - it('lastYear', () => { - const [start, end] = dateRanges.lastYear(); - expect(start.toISOString()).toBe(dayjs().add(-1, 'year').startOf('year').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(-1, 'year').endOf('year').toISOString()); - }); - - it('thisYear', () => { - const [start, end] = dateRanges.thisYear(); - expect(start.toISOString()).toBe(dayjs().startOf('year').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('year').toISOString()); - }); - - it('nextYear', () => { - const [start, end] = dateRanges.nextYear(); - expect(start.toISOString()).toBe(dayjs().add(1, 'year').startOf('year').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(1, 'year').endOf('year').toISOString()); - }); - - it('last7Days', () => { - const [start, end] = dateRanges.last7Days(); - expect(start.toISOString()).toBe(dayjs().add(-6, 'days').startOf('days').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('days').toISOString()); - }); - - it('next7Days', () => { - const [start, end] = dateRanges.next7Days(); - expect(start.toISOString()).toBe(dayjs().add(1, 'day').startOf('day').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(7, 'days').endOf('days').toISOString()); - }); - - it('last30Days', () => { - const [start, end] = dateRanges.last30Days(); - expect(start.toISOString()).toBe(dayjs().add(-29, 'days').startOf('days').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('days').toISOString()); - }); - - it('next30Days', () => { - const [start, end] = dateRanges.next30Days(); - expect(start.toISOString()).toBe(dayjs().add(1, 'day').startOf('day').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(30, 'days').endOf('days').toISOString()); - }); - - it('last90Days', () => { - const [start, end] = dateRanges.last90Days(); - expect(start.toISOString()).toBe(dayjs().add(-89, 'days').startOf('days').toISOString()); - expect(end.toISOString()).toBe(dayjs().endOf('days').toISOString()); - }); - - it('next90Days', () => { - const [start, end] = dateRanges.next90Days(); - expect(start.toISOString()).toBe(dayjs().add(1, 'day').startOf('day').toISOString()); - expect(end.toISOString()).toBe(dayjs().add(90, 'days').endOf('days').toISOString()); - }); -}); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/mapDatePicker.test.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/mapDatePicker.test.ts deleted file mode 100644 index 8128382cf..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/mapDatePicker.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import dayjs from 'dayjs'; -import { mapDatePicker } from '../util'; -import { vi } from 'vitest'; - -describe('mapDatePicker', () => { - it('showTime is true and gmt is true', () => { - const props = { - value: '2022-02-22T22:22:22.000Z', - showTime: true, - gmt: true, - }; - const result = mapDatePicker()(props); - expect(result.value.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-02-22 22:22:22'); - }); - - it('showTime is true and gmt is false', () => { - const props = { - value: '2022-02-22T22:22:22.000Z', - showTime: true, - gmt: false, - }; - const result = mapDatePicker()(props); - expect(result.value.format('YYYY-MM-DD HH:mm:ss')).toBe( - dayjs('2022-02-22T22:22:22.000Z').format('YYYY-MM-DD HH:mm:ss'), - ); - }); - - it('showTime is false and gmt is true', () => { - const props = { - value: '2022-02-22T00:00:00.000Z', - showTime: false, - gmt: true, - }; - const result = mapDatePicker()(props); - expect(result.value.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-02-22 00:00:00'); - }); - - it('showTime is false and gmt is false', () => { - const props = { - value: '2022-02-22', - showTime: false, - gmt: false, - }; - const result = mapDatePicker()(props); - expect(result.value.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-02-22 00:00:00'); - }); - - it('should call onChange with correct value when showTime is true and gmt is true', () => { - const props = { - showTime: true, - gmt: true, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs.utc('2022-02-22 22:22:22')); - expect(props.onChange).toHaveBeenCalledWith('2022-02-22T22:22:22.000Z'); - }); - - it('should call onChange with correct value when showTime is true and gmt is false', () => { - const props = { - showTime: true, - gmt: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs('2022-02-22 22:22:22'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.toISOString()); - }); - - it('should call onChange with correct value when showTime is false and gmt is true', () => { - const props = { - showTime: false, - gmt: true, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs.utc('2022-02-22')); - expect(props.onChange).toHaveBeenCalledWith('2022-02-22T00:00:00.000Z'); - }); - - it('should call onChange with correct value when showTime is false and gmt is false', () => { - const props = { - showTime: false, - gmt: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs('2022-02-22'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.toISOString()); - }); - - it('should call onChange with correct value when picker is year and gmt is true', () => { - const props = { - picker: 'year', - gmt: true, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs.utc('2022-01-01T00:00:00.000Z')); - expect(props.onChange).toHaveBeenCalledWith('2022-01-01T00:00:00.000Z'); - }); - - it('should call onChange with correct value when picker is year and gmt is false', () => { - const props = { - picker: 'year', - gmt: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs('2022-02-01 00:00:00'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.startOf('year').toISOString()); - }); - - it('should call onChange with correct value when picker is month and gmt is true', () => { - const props = { - picker: 'month', - gmt: true, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs.utc('2022-02-22T00:00:00.000Z')); - expect(props.onChange).toHaveBeenCalledWith('2022-02-01T00:00:00.000Z'); - }); - - it('should call onChange with correct value when picker is month and gmt is false', () => { - const props = { - picker: 'month', - gmt: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs('2022-02-01 00:00:00'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.startOf('month').toISOString()); - }); - - it('should call onChange with correct value when picker is quarter and gmt is true', () => { - const props = { - picker: 'quarter', - gmt: true, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs.utc('2022-02-22T00:00:00.000Z')); - expect(props.onChange).toHaveBeenCalledWith('2022-01-01T00:00:00.000Z'); - }); - - it('should call onChange with correct value when picker is quarter and gmt is false', () => { - const props = { - picker: 'quarter', - gmt: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs('2022-02-01 00:00:00'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.startOf('quarter').toISOString()); - }); - - it('should call onChange with correct value when picker is week and gmt is true', () => { - const props = { - picker: 'week', - gmt: true, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs.utc('2022-02-21T00:00:00.000Z'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.startOf('week').add(1, 'day').toISOString()); - }); - - it('should call onChange with correct value when picker is week and gmt is false', () => { - const props = { - picker: 'week', - gmt: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - const m = dayjs('2022-02-21 00:00:00'); - result.onChange(m); - expect(props.onChange).toHaveBeenCalledWith(m.startOf('week').add(1, 'day').toISOString()); - }); - - it('should call onChange with correct value when utc is false', () => { - const props = { - showTime: true, - gmt: true, - utc: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs('2022-02-22 22:22:22')); - expect(props.onChange).toHaveBeenCalledWith('2022-02-22 22:22:22'); - }); - - it('should call onChange with correct value when picker is year and utc is false', () => { - const props = { - showTime: false, - gmt: true, - utc: false, - onChange: vi.fn(), - }; - const result = mapDatePicker()(props); - result.onChange(dayjs('2022-01-01 23:00:00')); - expect(props.onChange).toHaveBeenCalledWith('2022-01-01'); - }); - - it('utc is false and gmt is true', () => { - const props = { - value: '2022-01-01 23:00:00', - showTime: true, - gmt: true, - utc: false, - }; - const result = mapDatePicker()(props); - expect(result.value.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-01-01 23:00:00'); - }); -}); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/mapRangePicker.test.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/mapRangePicker.test.ts deleted file mode 100644 index dff35582f..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/mapRangePicker.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import dayjs from 'dayjs'; -import { mapRangePicker } from '../util'; - -describe('mapRangePicker', () => { - it('should work with showTime=false, gmt=true, utc=true', () => { - const props = { - showTime: false, - gmt: true, - utc: true, - onChange: vi.fn(), - }; - const { onChange } = mapRangePicker()(props); - const value = [dayjs.utc('2023-01-01T00:00:00.000Z'), dayjs.utc('2023-01-02T00:00:00.000Z')]; - onChange(value); - expect(props.onChange).toHaveBeenCalledWith(['2023-01-01T00:00:00.000Z', '2023-01-02T23:59:59.999Z']); - }); - - it('should work with showTime=true, gmt=true, utc=true', () => { - const props = { - showTime: true, - gmt: true, - utc: true, - onChange: vi.fn(), - }; - const { onChange } = mapRangePicker()(props); - const value = [dayjs.utc('2023-01-01T00:00:00.000Z'), dayjs.utc('2023-01-02T00:00:00.000Z')]; - onChange(value); - expect(props.onChange).toHaveBeenCalledWith(['2023-01-01T00:00:00.000Z', '2023-01-02T00:00:00.000Z']); - }); - - it('should work with showTime=false, gmt=true, utc=false', () => { - const props = { - showTime: false, - gmt: true, - utc: false, - onChange: vi.fn(), - }; - const { onChange } = mapRangePicker()(props); - const value = [dayjs.utc('2023-01-01T00:00:00.000Z'), dayjs.utc('2023-01-02T00:00:00.000Z')]; - onChange(value); - expect(props.onChange).toHaveBeenCalledWith(['2023-01-01', '2023-01-02']); - }); -}); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/util.test.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/util.test.ts deleted file mode 100644 index c48241b02..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/__tests__/util.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { str2moment } from '@nocobase/utils/client'; -import dayjs from 'dayjs'; -import { moment2str } from '../util'; - -describe('str2moment', () => { - describe('string value', () => { - test('gmt date', async () => { - const m = str2moment('2022-06-21T00:00:00.000Z', { gmt: true }); - expect(m.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-06-21 00:00:00'); - }); - - test('local date', async () => { - const m = str2moment('2022-06-21T00:00:00.000Z'); - expect(m.toISOString()).toBe('2022-06-21T00:00:00.000Z'); - }); - - test('value is null', async () => { - const m = str2moment(null); - expect(m).toBeNull(); - }); - - test('picker is month', async () => { - const m = str2moment('2022-06-01T00:00:00.000Z', { picker: 'month' }); - expect(m.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-06-01 00:00:00'); - }); - }); - - describe('array value', () => { - test('gmt date', async () => { - const arr = str2moment(['2022-06-21T00:00:00.000Z', '2022-06-21T00:00:00.000Z'], { gmt: true }); - for (const m of arr) { - expect(m.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-06-21 00:00:00'); - } - }); - - test('local date', async () => { - const arr = str2moment(['2022-06-21T00:00:00.000Z', '2022-06-21T00:00:00.000Z']); - for (const m of arr) { - expect(m.toISOString()).toBe('2022-06-21T00:00:00.000Z'); - } - }); - }); -}); - -describe('moment2str', () => { - test('gmt date', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { showTime: true, gmt: true }); - expect(str).toBe('2023-06-21T10:10:00.000Z'); - }); - - test('showTime is true, gmt is false', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { showTime: true, gmt: false }); - expect(str).toBe(m.toISOString()); - }); - - test('gmt is true', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { gmt: true }); - expect(str).toBe('2023-06-21T10:10:00.000Z'); - }); - - test('gmt is false', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { gmt: false }); - expect(str).toBe(dayjs('2023-06-21 10:10:00').toISOString()); - }); - - test('with time', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { showTime: true }); - expect(str).toBe(m.toISOString()); - }); - - test('picker is year, gmt is false', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'year', gmt: false }); - expect(str).toBe(dayjs('2023-01-01 00:00:00').toISOString()); - }); - - test('picker is year, gmt is true', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'year', gmt: true }); - expect(str).toBe('2023-01-01T00:00:00.000Z'); - }); - - test('picker is year', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'year' }); - expect(str).toBe('2023-01-01T00:00:00.000Z'); - }); - - test('picker is quarter, gmt is false', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'quarter', gmt: false }); - expect(str).toBe(dayjs('2023-04-01 00:00:00').toISOString()); - }); - - test('picker is quarter, gmt is true', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'quarter', gmt: true }); - expect(str).toBe('2023-04-01T00:00:00.000Z'); - }); - - test('picker is month, gmt is false', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'month', gmt: false }); - expect(str).toBe(dayjs('2023-06-01 00:00:00').toISOString()); - }); - - test('picker is month, gmt is true', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'month', gmt: true }); - expect(str).toBe('2023-06-01T00:00:00.000Z'); - }); - - test('picker is month', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'month' }); - expect(str).toBe('2023-06-01T00:00:00.000Z'); - }); - - test('picker is week, gmt is false', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'week', gmt: false }); - expect(str).toBe(dayjs('2023-06-19 00:00:00').toISOString()); - }); - - test('picker is week, gmt is true', () => { - const m = dayjs('2023-06-21 10:10:00'); - const str = moment2str(m, { picker: 'week', gmt: true }); - expect(str).toBe('2023-06-19T00:00:00.000Z'); - }); - - test('value is null', async () => { - const m = moment2str(null); - expect(m).toBeNull(); - }); -}); diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo1.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo1.tsx deleted file mode 100644 index 0e58433fe..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo1.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/** - * title: DatePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: true, - }, - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo10.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo10.tsx deleted file mode 100644 index aad364a19..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo10.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/** - * title: DatePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: false, - utc: true, - }, - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: false, - utc: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo11.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo11.tsx deleted file mode 100644 index 1cd60ac4d..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo11.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * title: DatePicker.RangePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - gmt: true, - }, - 'x-reactions': [ - { - target: 'read1', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - { - target: 'read2', - fulfill: { - state: { - value: '{{$self.value && $self.value.join(" ~ ")}}', - }, - }, - }, - ], - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - gmt: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo2.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo2.tsx deleted file mode 100644 index a32adcd20..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo2.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/** - * title: DatePicker (GMT) - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: true, - gmt: true, - }, - default: '2022-06-04T15:00:00.000Z', - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'string', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: true, - gmt: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo3.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo3.tsx deleted file mode 100644 index 82f0a1302..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo3.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/** - * title: DatePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - utc: false, - }, - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo4.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo4.tsx deleted file mode 100644 index e9709a3ee..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo4.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * title: DatePicker.RangePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import dayjs from 'dayjs'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - gmt: true, - defaultPickerValue: [dayjs('2023-05-01')], - }, - 'x-reactions': [ - { - target: 'read1', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - { - target: 'read2', - fulfill: { - state: { - value: '{{$self.value && $self.value.join(" ~ ")}}', - }, - }, - }, - ], - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - gmt: true, - defaultPickerValue: [dayjs('2023-05-01')], - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo5.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo5.tsx deleted file mode 100644 index e3db10a37..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo5.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/** - * title: DatePicker.RangePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import dayjs from 'dayjs'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - gmt: false, - defaultPickerValue: [dayjs('2023-05-01')], - }, - 'x-reactions': [ - { - target: 'read1', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - { - target: 'read2', - fulfill: { - state: { - value: '{{$self.value && $self.value.join(" ~ ")}}', - }, - }, - }, - ], - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - defaultPickerValue: [dayjs('2023-05-01')], - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo6.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo6.tsx deleted file mode 100644 index bb7a32572..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo6.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/** - * title: DatePicker.RangePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import dayjs from 'dayjs'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - utc: false, - defaultPickerValue: [dayjs('2023-05-01')], - }, - 'x-reactions': [ - { - target: 'read1', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - { - target: 'read2', - fulfill: { - state: { - value: '{{$self.value && $self.value.join(" ~ ")}}', - }, - }, - }, - ], - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker.RangePicker', - 'x-component-props': { - defaultPickerValue: [dayjs('2023-05-01')], - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo7.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo7.tsx deleted file mode 100644 index 56eaec182..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo7.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/** - * title: DatePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: true, - utc: true, - }, - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: true, - utc: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo8.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo8.tsx deleted file mode 100644 index aad364a19..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo8.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/** - * title: DatePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: false, - utc: true, - }, - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: false, - utc: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo9.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo9.tsx deleted file mode 100644 index 56eaec182..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/demos/demo9.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/** - * title: DatePicker - */ -import { FormItem } from '@formily/antd-v5'; -import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; -import React from 'react'; - -const schema = { - type: 'object', - properties: { - input: { - type: 'boolean', - title: `Editable`, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: true, - utc: true, - }, - 'x-reactions': { - target: '*(read1,read2)', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read1: { - type: 'boolean', - title: `Read pretty`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - dateFormat: 'YYYY/MM/DD', - showTime: false, - gmt: true, - utc: true, - }, - }, - read2: { - type: 'string', - title: `Value`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-component-props': {}, - }, - }, -}; - -export default () => { - return ( - - - - ); -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/index.md b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/index.md deleted file mode 100644 index 223f14afa..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/index.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -group: - title: Schema Components - order: 3 ---- - -# DatePicker - -## Examples - -### Basic - - - -### DatePicker (GMT) - - - -### DatePicker (showTime=false,gmt=true,utc=true) - - - - -### DatePicker (showTime=false,gmt=false,utc=true) - - - - -### DatePicker (non-UTC) - - - -### RangePicker (GMT) - - - -### RangePicker (non-GMT) - - - -### RangePicker (non-UTC) - - - - -## API - -基于 antd 的 [DatePicker](https://ant.design/components/date-picker/#API),新增了以下扩展属性,用于支持 NocoBase 的日期字段设置。 - -- `dateFormat` 设置日期格式 -- `timeFormat` 设置时间格式 diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/index.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/index.ts deleted file mode 100644 index a48b62e4d..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DatePicker'; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/util.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/util.ts deleted file mode 100644 index 50a2bfecc..000000000 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/date-picker/util.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { getDefaultFormat, str2moment, toGmt, toLocal } from '@nocobase/utils/client'; -import type { Dayjs } from 'dayjs'; -import dayjs from 'dayjs'; - -const toStringByPicker = (value, picker, timezone: 'gmt' | 'local') => { - if (!dayjs.isDayjs(value)) return value; - if (timezone === 'local') { - const offset = new Date().getTimezoneOffset(); - return dayjs(toStringByPicker(value, picker, 'gmt')) - .add(offset, 'minutes') - .toISOString(); - } - - if (picker === 'year') { - return value.format('YYYY') + '-01-01T00:00:00.000Z'; - } - if (picker === 'month') { - return value.format('YYYY-MM') + '-01T00:00:00.000Z'; - } - if (picker === 'quarter') { - return value.startOf('quarter').format('YYYY-MM') + '-01T00:00:00.000Z'; - } - if (picker === 'week') { - return value.startOf('week').add(1, 'day').format('YYYY-MM-DD') + 'T00:00:00.000Z'; - } - return value.format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z'; -}; - -const toGmtByPicker = (value: Dayjs, picker?: any) => { - if (!value || !dayjs.isDayjs(value)) { - return value; - } - return toStringByPicker(value, picker, 'gmt'); -}; - -const toLocalByPicker = (value: Dayjs, picker?: any) => { - if (!value || !dayjs.isDayjs(value)) { - return value; - } - return toStringByPicker(value, picker, 'local'); -}; - -export interface Moment2strOptions { - showTime?: boolean; - gmt?: boolean; - utc?: boolean; - picker?: 'year' | 'month' | 'week' | 'quarter'; -} - -export const moment2str = (value?: Dayjs | null, options: Moment2strOptions = {}) => { - const { showTime, gmt, picker, utc = true } = options; - if (!value) { - return value; - } - if (!utc) { - const format = showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'; - return value.format(format); - } - if (showTime) { - return gmt ? toGmt(value) : toLocal(value); - } - if (typeof gmt === 'boolean') { - return gmt ? toGmtByPicker(value, picker) : toLocalByPicker(value, picker); - } - return toGmtByPicker(value, picker); -}; - -export const mapDatePicker = function () { - return (props: any) => { - const format = getDefaultFormat(props) as any; - const onChange = props.onChange; - - return { - ...props, - format: format, - value: str2moment(props.value, props), - onChange: (value: Dayjs | null) => { - if (onChange) { - if (!props.showTime && value) { - value = value.startOf('day'); - } - onChange(moment2str(value, props)); - } - }, - }; - }; -}; - -export const mapRangePicker = function () { - return (props: any) => { - const format = getDefaultFormat(props) as any; - const onChange = props.onChange; - - return { - ...props, - format: format, - value: str2moment(props.value, props), - onChange: (value: Dayjs[]) => { - if (onChange) { - onChange( - value - ? [moment2str(getRangeStart(value[0], props), props), moment2str(getRangeEnd(value[1], props), props)] - : [], - ); - } - }, - } as any; - }; -}; - -function getRangeStart(value: Dayjs, options: Moment2strOptions) { - const { showTime } = options; - if (showTime) { - return value; - } - return value.startOf('day'); -} - -function getRangeEnd(value: Dayjs, options: Moment2strOptions) { - const { showTime } = options; - if (showTime) { - return value; - } - return value.endOf('day'); -} - -const getStart = (offset: any, unit: any) => { - return dayjs() - .add(offset, unit === 'isoWeek' ? 'week' : unit) - .startOf(unit); -}; - -const getEnd = (offset: any, unit: any) => { - return dayjs() - .add(offset, unit === 'isoWeek' ? 'week' : unit) - .endOf(unit); -}; - -export const getDateRanges = () => { - return { - now: () => dayjs().toISOString(), - today: () => [getStart(0, 'day'), getEnd(0, 'day')], - yesterday: () => [getStart(-1, 'day'), getEnd(-1, 'day')], - tomorrow: () => [getStart(1, 'day'), getEnd(1, 'day')], - thisWeek: () => [getStart(0, 'isoWeek'), getEnd(0, 'isoWeek')], - lastWeek: () => [getStart(-1, 'isoWeek'), getEnd(-1, 'isoWeek')], - nextWeek: () => [getStart(1, 'isoWeek'), getEnd(1, 'isoWeek')], - year: (year: number) => [getStart(year - dayjs().year(), 'year'), getEnd(year - dayjs().year(), 'year')], - thisIsoWeek: () => [getStart(0, 'isoWeek'), getEnd(0, 'isoWeek')], - lastIsoWeek: () => [getStart(-1, 'isoWeek'), getEnd(-1, 'isoWeek')], - nextIsoWeek: () => [getStart(1, 'isoWeek'), getEnd(1, 'isoWeek')], - thisMonth: () => [getStart(0, 'month'), getEnd(0, 'month')], - lastMonth: () => [getStart(-1, 'month'), getEnd(-1, 'month')], - nextMonth: () => [getStart(1, 'month'), getEnd(1, 'month')], - thisQuarter: () => [getStart(0, 'quarter'), getEnd(0, 'quarter')], - lastQuarter: () => [getStart(-1, 'quarter'), getEnd(-1, 'quarter')], - nextQuarter: () => [getStart(1, 'quarter'), getEnd(1, 'quarter')], - thisYear: () => [getStart(0, 'year'), getEnd(0, 'year')], - lastYear: () => [getStart(-1, 'year'), getEnd(-1, 'year')], - nextYear: () => [getStart(1, 'year'), getEnd(1, 'year')], - last7Days: () => [getStart(-6, 'days'), getEnd(0, 'days')], - next7Days: () => [getStart(1, 'day'), getEnd(7, 'days')], - last30Days: () => [getStart(-29, 'days'), getEnd(0, 'days')], - next30Days: () => [getStart(1, 'day'), getEnd(30, 'days')], - last90Days: () => [getStart(-89, 'days'), getEnd(0, 'days')], - next90Days: () => [getStart(1, 'day'), getEnd(90, 'days')], - }; -}; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/index.ts b/packages/plugins/@hera/plugin-core/src/client/schema-components/index.ts index 1547cd072..26ed01823 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/index.ts +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/index.ts @@ -1,5 +1,4 @@ export * from './auto-complete/AutoComplete'; export * from './blocks/GroupBlock'; export * from './blocks/PDFViewer'; -export * from './date-picker'; export * from './deprecated/ExtendedMenuDesigner'; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsDatePresets.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsDatePresets.tsx new file mode 100644 index 000000000..4735d5f5a --- /dev/null +++ b/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsDatePresets.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { ISchema, useFieldSchema } from '@formily/react'; +import { SchemaSettingsModalItem, useCollectionManager, useDesignable } from '@nocobase/client'; +import { tval, useTranslation } from '../locale'; +import { dayjs } from '@nocobase/utils/client'; + +export const useCustomPresets1 = () => { + const fieldSchema = useFieldSchema(); + const { t } = useTranslation(); + const rangesValue = getDateRanges(); + const presets = [ + { label: t('This year'), value: rangesValue.thisYear }, + ...Array(5) + .fill(0) + .map((_, i) => ({ label: dayjs().year() - i - 1 + t('year'), value: rangesValue.year(dayjs().year() - i - 1) })), + { label: t('This month'), value: rangesValue.thisMonth }, + { label: t('Last month'), value: rangesValue.lastMonth }, + ]; + if (fieldSchema.name === 'presets') { + return '{{ useCustomPresets1() }}'; + } + return presets; +}; + +export const useCustomPresets = (name: string) => { + return '{{ ' + name + '() }}'; +}; + +export const SchemaSettingsDatePresets: React.FC = () => { + const fieldSchema = useFieldSchema(); + const { dn } = useDesignable(); + const { t } = useTranslation(); + const cm = useCollectionManager(); + const collectionField = cm.getCollectionField(fieldSchema?.['x-collection-field']) || {}; + const defaultPresets = + fieldSchema?.['x-component-props']?.presets || collectionField?.uiSchema?.['x-component-props']?.presets || ''; + return ( + { + if (!data['presets']) { + // 强制清空来达到默认值的效果 + data['presets'] = undefined; + } + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + schema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props'] = { + ...(fieldSchema['x-component-props'] || {}), + ...data, + }; + schema['x-component-props'] = fieldSchema['x-component-props']; + // 这里有意不做直接刷新,直接刷新需要解析 {{ }} 对应的值,待完成后才可以做 + dn.emit('patch', { + schema, + }); + dn.refresh(); + }} + /> + ); +}; + +SchemaSettingsDatePresets.displayName = 'SchemaSettingsDatePresets'; + +const getDateRanges = () => { + return { + now: () => dayjs().toISOString(), + today: () => [getStart(0, 'day'), getEnd(0, 'day')], + yesterday: () => [getStart(-1, 'day'), getEnd(-1, 'day')], + tomorrow: () => [getStart(1, 'day'), getEnd(1, 'day')], + thisWeek: () => [getStart(0, 'isoWeek'), getEnd(0, 'isoWeek')], + lastWeek: () => [getStart(-1, 'isoWeek'), getEnd(-1, 'isoWeek')], + nextWeek: () => [getStart(1, 'isoWeek'), getEnd(1, 'isoWeek')], + year: (year: number) => [getStart(year - dayjs().year(), 'year'), getEnd(year - dayjs().year(), 'year')], + thisIsoWeek: () => [getStart(0, 'isoWeek'), getEnd(0, 'isoWeek')], + lastIsoWeek: () => [getStart(-1, 'isoWeek'), getEnd(-1, 'isoWeek')], + nextIsoWeek: () => [getStart(1, 'isoWeek'), getEnd(1, 'isoWeek')], + thisMonth: () => [getStart(0, 'month'), getEnd(0, 'month')], + lastMonth: () => [getStart(-1, 'month'), getEnd(-1, 'month')], + nextMonth: () => [getStart(1, 'month'), getEnd(1, 'month')], + thisQuarter: () => [getStart(0, 'quarter'), getEnd(0, 'quarter')], + lastQuarter: () => [getStart(-1, 'quarter'), getEnd(-1, 'quarter')], + nextQuarter: () => [getStart(1, 'quarter'), getEnd(1, 'quarter')], + thisYear: () => [getStart(0, 'year'), getEnd(0, 'year')], + lastYear: () => [getStart(-1, 'year'), getEnd(-1, 'year')], + nextYear: () => [getStart(1, 'year'), getEnd(1, 'year')], + last7Days: () => [getStart(-6, 'days'), getEnd(0, 'days')], + next7Days: () => [getStart(1, 'day'), getEnd(7, 'days')], + last30Days: () => [getStart(-29, 'days'), getEnd(0, 'days')], + next30Days: () => [getStart(1, 'day'), getEnd(30, 'days')], + last90Days: () => [getStart(-89, 'days'), getEnd(0, 'days')], + next90Days: () => [getStart(1, 'day'), getEnd(90, 'days')], + }; +}; + +const getStart = (offset: any, unit: any) => { + return dayjs() + .add(offset, unit === 'isoWeek' ? 'week' : unit) + .startOf(unit); +}; + +const getEnd = (offset: any, unit: any) => { + return dayjs() + .add(offset, unit === 'isoWeek' ? 'week' : unit) + .endOf(unit); +}; diff --git a/packages/plugins/@hera/plugin-core/src/locale/en-US.json b/packages/plugins/@hera/plugin-core/src/locale/en-US.json index ce94eb0c4..97e05d9f9 100644 --- a/packages/plugins/@hera/plugin-core/src/locale/en-US.json +++ b/packages/plugins/@hera/plugin-core/src/locale/en-US.json @@ -1,12 +1,14 @@ { "Add custom component": "Add custom component", "Association field": "Association field", + "Chuangxing presets": "Chuangxing presets", "Configure": "Configure", "Custom filter": "Custom filter", "Custom option label": "Custom option label", - "Group": "Group", "Group block": "Group block", + "Group": "Group", "HomePage Config": "HomePage Config", + "Last month": "Last month", "Mobile UI": "Mobile UI", "Navigate to new page": "Navigate to new page", "Page style": "Page Style", @@ -16,10 +18,14 @@ "Signature input": "Signature input", "Something went wrong. Please contact the developer to resolve the issue.": "Something went wrong. Please contact the developer to resolve the issue.", "System setting": "System setting", + "This month": "This month", + "This year": "This year", "classical": "classical", "component": "component", + "default": "default", "error": "error", "loading...": "loading...", "preview block": "preview block", - "tabs": "tabs" + "tabs": "tabs", + "year": "year" } diff --git a/packages/plugins/@hera/plugin-core/src/locale/zh-CN.json b/packages/plugins/@hera/plugin-core/src/locale/zh-CN.json index 35fd72c0e..85f007ae2 100644 --- a/packages/plugins/@hera/plugin-core/src/locale/zh-CN.json +++ b/packages/plugins/@hera/plugin-core/src/locale/zh-CN.json @@ -1,12 +1,14 @@ { "Add custom component": "添加自定义组件", "Association field": "关联字段", + "Chuangxing presets": "创兴预置", "Configure": "配置", "Custom filter": "自定义筛选", "Custom option label": "自定义选项标签", "Group": "汇总", "Group block": "汇总区块", "HomePage Config": "主页配置", + "Last month": "上月", "Mobile UI": "移动端页面", "Navigate to new page": "导航到新页面", "Page style": "页面版本", @@ -16,10 +18,14 @@ "Signature input": "手写签名", "Something went wrong. Please contact the developer to resolve the issue.": "系统内部错误,请联系开发者解决", "System setting": "系统设置", + "This month": "本月", + "This year": "今年", "classical": "经典版", "component": "组件", + "default": "默认", "error": "出错了", "loading...": "加载中...", "preview block": "预览区块", - "tabs": "多标签" + "tabs": "多标签", + "year": "年" }