diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 64c8f1cf80..5443258a34 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -38,7 +38,7 @@ "react-i18next": "^11.15.1", "react-iframe": "~1.8.5", "react-image-lightbox": "^5.1.4", - "react-js-cron": "^1.4.0", + "react-js-cron": "^3.1.0", "react-quill": "^1.3.5", "react-router-dom": "^5.2.0", "react-to-print": "^2.14.7", diff --git a/packages/core/client/src/block-provider/CalendarBlockProvider.tsx b/packages/core/client/src/block-provider/CalendarBlockProvider.tsx index e2493b66d8..d94557b715 100644 --- a/packages/core/client/src/block-provider/CalendarBlockProvider.tsx +++ b/packages/core/client/src/block-provider/CalendarBlockProvider.tsx @@ -1,6 +1,7 @@ import { ArrayField } from '@formily/core'; import { useField } from '@formily/react'; import React, { createContext, useContext, useEffect } from 'react'; +import { useFixedSchema } from '../schema-component'; import { BlockProvider, useBlockRequestContext } from './BlockProvider'; export const CalendarBlockContext = createContext({}); @@ -9,6 +10,7 @@ const InternalCalendarBlockProvider = (props) => { const { fieldNames, showLunar } = props; const field = useField(); const { resource, service } = useBlockRequestContext(); + const fixedBlock = useFixedSchema() // if (service.loading) { // return ; // } @@ -20,6 +22,7 @@ const InternalCalendarBlockProvider = (props) => { resource, fieldNames, showLunar, + fixedBlock, }} > {props.children} @@ -50,5 +53,6 @@ export const useCalendarBlockProps = () => { return { fieldNames: ctx.fieldNames, showLunar: ctx.showLunar, + fixedBlock: ctx.fixedBlock }; }; diff --git a/packages/core/client/src/block-provider/KanbanBlockProvider.tsx b/packages/core/client/src/block-provider/KanbanBlockProvider.tsx index 3c274aa3c0..e08bd78672 100644 --- a/packages/core/client/src/block-provider/KanbanBlockProvider.tsx +++ b/packages/core/client/src/block-provider/KanbanBlockProvider.tsx @@ -24,8 +24,7 @@ const useGroupField = (props) => { const InternalKanbanBlockProvider = (props) => { const field = useField(); - const fieldSchema = useFieldSchema(); - useFixedSchema(); + const fixedBlock = useFixedSchema(); const { resource, service } = useBlockRequestContext(); const groupField = useGroupField(props); if (!groupField) { @@ -45,7 +44,7 @@ const InternalKanbanBlockProvider = (props) => { service, resource, groupField, - fixedBlock: fieldSchema?.['x-decorator-props']?.fixedBlock, + fixedBlock, }} > {props.children} diff --git a/packages/core/client/src/collection-manager/templates/calendar.tsx b/packages/core/client/src/collection-manager/templates/calendar.tsx index 1ff3bfa7b2..4ee7cf70d5 100644 --- a/packages/core/client/src/collection-manager/templates/calendar.tsx +++ b/packages/core/client/src/collection-manager/templates/calendar.tsx @@ -18,13 +18,26 @@ export const calendar: ICollectionTemplate = { type: 'string', uiSchema: { type: 'string', - title: '{{t("Cron")}}', - 'x-component': 'Select', + title: '{{t("Repeats")}}', + 'x-component': 'CronSet', + 'x-component-props': 'allowClear', enum: [ - { value: '0 0 * * *', label: '每天' }, - { value: '0 0 ? * 1', label: '每个星期一' }, - { value: '0 0 12 * * ?', label: '每天中午12点' }, - { value: '0 0 10,14,16 * * ?', label: '每天上午10点,下午2点,4点 ' }, + { + label: '{{t("Daily")}}', + value: '0 0 0 * * ?', + }, + { + label: '{{t("Weekly")}}', + value: 'every_week', + }, + { + label: '{{t("Monthly")}}', + value: 'every_month', + }, + { + label: '{{t("Yearly")}}', + value: 'every_year', + }, ], }, interface: 'select', diff --git a/packages/core/client/src/locale/en_US.ts b/packages/core/client/src/locale/en_US.ts index cf7383c18d..768902694f 100644 --- a/packages/core/client/src/locale/en_US.ts +++ b/packages/core/client/src/locale/en_US.ts @@ -270,6 +270,7 @@ export default { "Import": "Import", "Export": "Export", "Customize": "Customize", + "Custom": "Custom", "Function": "Function", "Popup form": "Popup form", "Flexible popup": "Flexible popup", @@ -345,6 +346,11 @@ export default { "Configure calendar": "Configure calendar", "Title field": "Title field", "Custom title": "Custom title", + "Daily": "Daily", + "Weekly": "Weekly", + "Monthly": "Monthly", + "Yearly": "Yearly", + "Repeats": "Repeats", "Show lunar": "Show lunar", "Start date field": "Start date field", "End date field": "End date field", diff --git a/packages/core/client/src/locale/zh_CN.ts b/packages/core/client/src/locale/zh_CN.ts index 78d61e5862..166ad19604 100644 --- a/packages/core/client/src/locale/zh_CN.ts +++ b/packages/core/client/src/locale/zh_CN.ts @@ -286,6 +286,7 @@ export default { "Import": "导入", "Export": "导出", "Customize": "自定义", + "Custom": "自定义", "Function": "Function", "Popup form": "Popup form", "Flexible popup": "Flexible popup", @@ -367,6 +368,11 @@ export default { "Allow linking to multiple records": "允许关联多条记录", "Allow uploading multiple files": "允许上传多个文件", + "Daily": "每天", + "Weekly": "每周", + "Monthly": "每月", + "Yearly": "每年", + "Repeats": "重复", "Configure calendar": "配置日历", "Title field": "标题字段", "Custom title": "自定义标题", diff --git a/packages/core/client/src/schema-component/antd/calendar/Calendar.Designer.tsx b/packages/core/client/src/schema-component/antd/calendar/Calendar.Designer.tsx index 32ac9a840e..6982a829ce 100644 --- a/packages/core/client/src/schema-component/antd/calendar/Calendar.Designer.tsx +++ b/packages/core/client/src/schema-component/antd/calendar/Calendar.Designer.tsx @@ -1,7 +1,7 @@ import { ISchema, useField, useFieldSchema } from '@formily/react'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useCompile, useDesignable } from '../..'; +import { useCompile, useDesignable, useFixedBlockDesignerSetting } from '../..'; import { useCalendarBlockContext } from '../../../block-provider'; import { useCollection, useCollectionManager } from '../../../collection-manager'; import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks'; @@ -21,6 +21,8 @@ export const CalendarDesigner = () => { const defaultFilter = fieldSchema?.['x-decorator-props']?.params?.filter || {}; const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; const defaultResource = fieldSchema?.['x-decorator-props']?.resource; + const fixedBlockDesignerSetting = useFixedBlockDesignerSetting(); + return ( @@ -60,6 +62,7 @@ export const CalendarDesigner = () => { dn.refresh(); }} /> + {fixedBlockDesignerSetting} { }; export const Calendar: any = observer((props: any) => { - const { dataSource, fieldNames, showLunar } = useProps(props); + const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props); const [date, setDate] = useState(new Date()); const [view, setView] = useState('month'); const events = useEvents(dataSource, fieldNames, date, view); @@ -211,7 +211,7 @@ export const Calendar: any = observer((props: any) => { }, [showLunar]); return ( -
+
& {}; - -const Input = (props: Exclude & { onChange: (value: string) => void }) => { +const Input = (props: Omit & { onChange: (value: string) => void }) => { const { onChange, ...rest } = props; return (
& { onChange: (value: strin const ReadPretty = (props) => { const api = useAPIClient(); const locale = api.auth.getLocale(); - return props.value ? ( - - {cronstrue.toString(props.value, { + const value = useMemo(() => { + try { + return cronstrue.toString(props.value, { locale, use24HourTimeFormat: true, - })} + }) + } catch { + console.error(`The '${props.value}' is not a valid cron expression`) + return props.value + } + }, [props.value]) + return props.value ? ( + + {value} ) : null; }; -export const Cron: ComposedCron = connect(Input, mapReadPretty(ReadPretty)); +export const Cron = connect(Input, mapReadPretty(ReadPretty)) as unknown as typeof Input & { + ReadPretty; +}; + +Cron.ReadPretty = ReadPretty; export default Cron; diff --git a/packages/core/client/src/schema-component/antd/cron/CronSet.tsx b/packages/core/client/src/schema-component/antd/cron/CronSet.tsx new file mode 100644 index 0000000000..e4870686d0 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/cron/CronSet.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { Select, SelectProps } from 'antd'; +import Cron from './Cron'; +import { connect, mapReadPretty, useFieldSchema } from '@formily/react'; +import { useCollection } from '../../../collection-manager'; +import { useCompile } from '../../hooks'; +import { EllipsisWithTooltip } from '../input'; + +interface CronSetProps extends SelectProps { + onChange: (v: string) => void; +} + +const CronSetInternal = (props: CronSetProps) => { + const { onChange, value } = props; + const { getField } = useCollection(); + const fieldSchema = useFieldSchema(); + const uiSchemaOptions = getField(fieldSchema?.name)?.uiSchema.enum; + const compile = useCompile(); + + const [customizeOption, setCustomizeOption] = useState({ + label: "{{t('Custom')}}", + value: '@daily', + }); + + const options = useMemo(() => { + return (props.options || []) + .concat((uiSchemaOptions as any[]) || []) + .concat([customizeOption]) + .map((item) => { + return { + ...item, + label: compile(item.label), + }; + }); + }, [props.options, customizeOption]); + + useEffect(() => { + if (typeof value === 'undefined' || value === null) { + return; + } + const item = options.find((o) => o.value === value); + if (!item) { + setCustomizeOption({ + ...customizeOption, + value, + }); + } + }, [options, value]); + + const isCustomize = useMemo(() => { + return value === customizeOption.value; + }, [value, customizeOption]); + + const onCronChange = (value, customize = false) => { + if (customize) { + setCustomizeOption({ + ...customizeOption, + value, + }); + } + onChange(value); + }; + + return ( +
+ + {isCustomize ? onCronChange(v, true)} allowedDropdowns={['period', 'week-days', 'months', 'month-days']} allowedPeriods={['year', 'month', 'week', 'day', ]} /> : null} +
+ ); +}; + +const ReadPretty = (props: CronSetProps) => { + const { value } = props; + const compile = useCompile(); + const fieldSchema = useFieldSchema(); + const { getField } = useCollection(); + const uiSchemaOptions = getField(fieldSchema?.name)?.uiSchema.enum; + + const options = useMemo(() => { + return (props.options || []).concat((uiSchemaOptions as any[]) || []); + }, [props.options]); + + const label = useMemo(() => { + return value && options?.find((o) => o.value === value)?.label; + }, [options, value]); + + return {value && (label ? compile(label) : )}; +}; + +export const CronSet = connect(CronSetInternal, mapReadPretty(ReadPretty)); diff --git a/packages/core/client/src/schema-component/antd/cron/index.tsx b/packages/core/client/src/schema-component/antd/cron/index.tsx index 3ce35cbf9b..f0e06e16f4 100644 --- a/packages/core/client/src/schema-component/antd/cron/index.tsx +++ b/packages/core/client/src/schema-component/antd/cron/index.tsx @@ -1 +1,2 @@ export * from './Cron'; +export * from './CronSet'; diff --git a/packages/core/client/src/schema-component/antd/cron/locale/index.ts b/packages/core/client/src/schema-component/antd/cron/locale/index.ts index 706f4d32e3..65d952524b 100644 --- a/packages/core/client/src/schema-component/antd/cron/locale/index.ts +++ b/packages/core/client/src/schema-component/antd/cron/locale/index.ts @@ -1,7 +1,5 @@ -import { DEFAULT_LOCALE_EN } from 'react-js-cron/dist/cjs/locale'; import zhCN from './zh-CN'; export default { 'zh-CN': zhCN, - 'en-US': DEFAULT_LOCALE_EN, }; diff --git a/packages/core/client/src/schema-component/antd/page/FixedBlock.tsx b/packages/core/client/src/schema-component/antd/page/FixedBlock.tsx index dfd79c367b..cca938a1b1 100644 --- a/packages/core/client/src/schema-component/antd/page/FixedBlock.tsx +++ b/packages/core/client/src/schema-component/antd/page/FixedBlock.tsx @@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next'; import { useDesignable } from '../../hooks'; import { useRecord } from '../../../record-provider'; import { useBlockTemplateContext } from '../../../schema-templates/BlockTemplate'; -import { uid } from '@formily/shared'; const FixedBlockContext = React.createContext({ setFixedSchema: (schema: Schema) => {}, @@ -40,6 +39,7 @@ export const useFixedSchema = () => { }, [], ); + return fieldSchema?.['x-decorator-props']?.fixedBlock }; export const useFixedBlock = () => { diff --git a/packages/plugins/client/src/cron.ts b/packages/plugins/client/src/cron.ts index 48de142ab3..0408dd03f1 100644 --- a/packages/plugins/client/src/cron.ts +++ b/packages/plugins/client/src/cron.ts @@ -12,5 +12,5 @@ export const getCronLocale = (lang: string) => { locale = require(file).cron?.[lang]; } catch (error) {} } - return locale || require('react-js-cron/dist/cjs/locale').DEFAULT_LOCALE_EN; + return locale; }; diff --git a/packages/plugins/sequence-field/package.json b/packages/plugins/sequence-field/package.json index e042acce8a..e99f3f6962 100644 --- a/packages/plugins/sequence-field/package.json +++ b/packages/plugins/sequence-field/package.json @@ -14,7 +14,7 @@ "@nocobase/utils": "0.9.1-alpha.2", "classnames": "^2.3.1", "cron-parser": "4.4.0", - "react-js-cron": "^1.4.0" + "react-js-cron": "^3.1.0" }, "devDependencies": { "@nocobase/test": "0.9.1-alpha.2" diff --git a/packages/plugins/sequence-field/src/client/sequence.tsx b/packages/plugins/sequence-field/src/client/sequence.tsx index 0a809ff565..597e17c2af 100644 --- a/packages/plugins/sequence-field/src/client/sequence.tsx +++ b/packages/plugins/sequence-field/src/client/sequence.tsx @@ -185,7 +185,7 @@ const RuleTypes = { ? ( ) diff --git a/packages/plugins/workflow/package.json b/packages/plugins/workflow/package.json index d0faedea18..cd6be0bbb6 100644 --- a/packages/plugins/workflow/package.json +++ b/packages/plugins/workflow/package.json @@ -19,7 +19,7 @@ "cron-parser": "4.4.0", "json-templates": "^4.2.0", "moment": "^2.29.2", - "react-js-cron": "^1.4.0" + "react-js-cron": "^3.1.0" }, "devDependencies": { "@nocobase/test": "0.9.1-alpha.2", diff --git a/yarn.lock b/yarn.lock index 86e2d03e18..e434e5c992 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21434,10 +21434,10 @@ react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-js-cron@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/react-js-cron/-/react-js-cron-1.4.0.tgz#58341434e3db00da11bc8009abb0ea6cd926cda7" - integrity sha512-gbx1NXMDJgiKBnn1ljTS/jIXBw+jAEDMFOW/kWeGXuLwV+HEDIfj2oIsgStGGzCjRgcjgM3UTtHxaWrBdo0lFQ== +react-js-cron@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/react-js-cron/-/react-js-cron-3.1.0.tgz#cd11a43cdfbededee0aaa14e35c026adda0ee1c7" + integrity sha512-UzKDvkr6tt91sbMTYdX7sranHxjjLDHOP+aOZbS9QbjlnoBvVjO5JaDOKC4kQBbaZLha3gdbuEtRXriE+3wSxQ== react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4"