From 082e27ff10d08a038deef373d193d4b9a077235f Mon Sep 17 00:00:00 2001 From: Junyi Date: Fri, 10 Jun 2022 19:23:26 +0800 Subject: [PATCH] Feat(plugin workflow): cron field for schedule trigger configuration (#495) * feat(plugin-workflow): add cron field component * refactor(plugin-workflow): break schedule trigger into component files and add locale --- packages/core/client/package.json | 3 +- .../client/src/workflow/triggers/index.tsx | 7 +- .../client/src/workflow/triggers/schedule.tsx | 499 ------------------ .../triggers/schedule/DateFieldsSelect.tsx | 25 + .../triggers/schedule/EndsByField.tsx | 35 ++ .../workflow/triggers/schedule/OnField.tsx | 47 ++ .../triggers/schedule/RepeatField.tsx | 127 +++++ .../triggers/schedule/ScheduleConfig.tsx | 221 ++++++++ .../src/workflow/triggers/schedule/index.tsx | 53 ++ .../triggers/schedule/locale/Cron.zh-CN.ts | 79 +++ yarn.lock | 25 +- 11 files changed, 616 insertions(+), 505 deletions(-) delete mode 100644 packages/core/client/src/workflow/triggers/schedule.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/DateFieldsSelect.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/EndsByField.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/OnField.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/RepeatField.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/ScheduleConfig.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/index.tsx create mode 100644 packages/core/client/src/workflow/triggers/schedule/locale/Cron.zh-CN.ts diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 79f865abf..5ea0baac5 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -31,12 +31,13 @@ "mathjs": "^10.6.0", "react-beautiful-dnd": "^13.1.0", "react-big-calendar": "^0.38.7", + "react-contenteditable": "^3.3.6", "react-drag-listview": "^0.1.9", "react-helmet": "^6.1.0", "react-i18next": "^11.15.1", "react-image-lightbox": "^5.1.4", + "react-js-cron": "^1.4.0", "react-quill": "^1.3.5", - "react-contenteditable": "^3.3.6", "react-router-dom": "^5.2.0", "slate": "^0.76.1", "slate-history": "^0.66.0", diff --git a/packages/core/client/src/workflow/triggers/index.tsx b/packages/core/client/src/workflow/triggers/index.tsx index bec7a13f0..4908af0b0 100644 --- a/packages/core/client/src/workflow/triggers/index.tsx +++ b/packages/core/client/src/workflow/triggers/index.tsx @@ -5,11 +5,12 @@ import { Registry } from "@nocobase/utils"; import { useTranslation } from "react-i18next"; import { message, Tag } from "antd"; -import { SchemaComponent, useActionContext, useAPIClient, useCompile, useRecord, useRequest, useResourceActionContext } from '../../'; -import collection from './collection'; +import { SchemaComponent, useActionContext, useAPIClient, useCompile, useResourceActionContext } from '../../'; + import { nodeCardClass, nodeMetaClass } from "../style"; -import schedule from "./schedule"; import { useFlowContext } from "../WorkflowCanvas"; +import collection from './collection'; +import schedule from "./schedule/"; function useUpdateConfigAction() { diff --git a/packages/core/client/src/workflow/triggers/schedule.tsx b/packages/core/client/src/workflow/triggers/schedule.tsx deleted file mode 100644 index 81b973003..000000000 --- a/packages/core/client/src/workflow/triggers/schedule.tsx +++ /dev/null @@ -1,499 +0,0 @@ -import React, { useState } from 'react'; -import { InputNumber, Select } from 'antd'; -import { observer, useForm, useFormEffects } from '@formily/react'; - -import { useCollectionDataSource, useCollectionManager } from '../../collection-manager'; -import { SchemaComponent, useCompile, DatePicker } from '../../schema-component'; - -import { useFlowContext } from '../WorkflowCanvas'; -import { BaseTypeSet } from '../calculators'; -import { collection } from '../schemas/collection'; -import { useTranslation } from 'react-i18next'; -import { onFieldValueChange } from '@formily/core'; -import { css } from '@emotion/css'; - -const DateFieldsSelect: React.FC = observer((props) => { - const compile = useCompile(); - const { getCollectionFields } = useCollectionManager(); - const { values } = useForm(); - const fields = getCollectionFields(values?.config?.collection); - - return ( - - ); -}); - -function OnField({ value, onChange }) { - const { t } = useTranslation(); - const [dir, setDir] = useState(value.offset ? value.offset / Math.abs(value.offset) : 0); - - return ( -
- onChange({ ...value, field })} /> - {value.field - ? ( - - ) - : null} - {dir - ? ( - <> - onChange({ ...value, offset: v * dir })}/> - - - ) - : null} -
- ); -} - -function EndsByField({ value, onChange }) { - const { t } = useTranslation(); - const [type, setType] = useState(typeof value === 'object' && !(value instanceof Date) ? 'field' : 'date'); - return ( -
- - {type === 'field' - ? ( - - ) - : ( - - ) - } -
- ); -} - -function parseCronRule(cron: string) { - const rules = cron.split(/\s+/).slice(1).map(v => v.split('/')); - let index = rules.findIndex(rule => rule[0] === '*'); - if (index === -1) { - return { - mode: 0 - } - } - // fix days of week - if (index === 3 && rules[4][0] === '*') { - index = 4; - } - return { - mode: index + 1, - step: rules[index][1] ?? 1 - }; -} - -const CronUnits = [ - { value: 1, option: 'By minute', unitText: 'Minutes' }, - { value: 2, option: 'By hour', unitText: 'Hours' }, - { value: 3, option: 'By date', unitText: 'Days', conflict: true, startFrom: 1 }, - { value: 4, option: 'By month', unitText: 'Months', startFrom: 1 }, - { value: 5, option: 'By day of week', unitText: 'Days', conflict: true }, -]; - -const RepeatOptions = [ - { value: 'none', text: 'No repeat' }, - { value: 60_000, text: 'By minute', unitText: 'Minutes' }, - { value: 3600_000, text: 'By hour', unitText: 'Hours' }, - { value: 86400_000, text: 'By day', unitText: 'Days' }, - { value: 604800_000, text: 'By week', unitText: 'Weeks' }, - // { value: 18144_000_000, text: 'By 30 days' }, - { value: 'cron', text: 'Advance', disabled: true } -]; - -function getNumberOption(v) { - const opts = RepeatOptions.filter(option => typeof option.value === 'number').reverse() as any[]; - return opts.find(item => !(v % item.value)); -} - -function getRepeatTypeValue(v) { - switch (typeof v) { - case 'number': - const option = getNumberOption(v); - return option ? option.value : 'none'; - case 'string': - return 'cron'; - default: - break; - } - return 'none'; -} - -function getChangedCron({ mode, step }) { - const m = mode - 1; - const left = [0, ...Array(m).fill(null).map((_, i) => { - if (CronUnits[m].conflict && CronUnits[i].conflict) { - return '?'; - } - return i === 3 ? '*' : CronUnits[i].startFrom ?? 0; - })].join(' '); - const right = Array(5 - mode).fill(null).map((_, i) => { - if (CronUnits[m].conflict && CronUnits[mode + i].conflict || mode === 4) { - return '?'; - } - return '*'; - }).join(' '); - return `${left} ${!step || step == 1 ? '*' : `*/${step}`}${right ? ` ${right}` : ''}`; -} - -function CronField({ value, onChange }) { - const { t } = useTranslation(); - const cron = parseCronRule(value); - const unit = CronUnits[cron.mode - 1]; - - return ( - - onChange(getChangedCron({ step: v, mode: cron.mode }))} - min={1} - addonBefore={t('Every')} - addonAfter={t(unit.unitText)} - /> - ); -} - -function CommonRepeatField({ value, onChange }) { - const { t } = useTranslation(); - const option = getNumberOption(value); - - return ( - onChange(v * option.value)} - min={1} - addonBefore={t('Every')} - addonAfter={t(option.unitText)} - /> - ); -} - -function RepeatField({ value = null, onChange }) { - const { t } = useTranslation(); - const typeValue = getRepeatTypeValue(value); - function onTypeChange(v) { - if (v === 'none') { - onChange(null); - return; - } - if (v === 'cron') { - onChange('0 * * * * *'); - return; - } - onChange(v); - } - - return ( -
- - {typeof typeValue === 'number' - ? - : null} - {typeValue === 'cron' - ? - : null} -
- ); -} - -const ModeFieldsets = { - 0: { - startsOn: { - type: 'datetime', - name: 'startsOn', - title: '{{t("Starts on")}}', - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - showTime: true - }, - required: true - }, - repeat: { - type: 'string', - name: 'repeat', - title: '{{t("Repeat mode")}}', - 'x-decorator': 'FormItem', - 'x-component': 'RepeatField', - 'x-reactions': [ - { - target: 'config.endsOn', - fulfill: { - state: { - visible: '{{!!$self.value}}', - }, - } - }, - { - target: 'config.limit', - fulfill: { - state: { - visible: '{{!!$self.value}}', - }, - } - } - ] - }, - endsOn: { - type: 'datetime', - name: 'endsOn', - title: '{{t("Ends on")}}', - 'x-decorator': 'FormItem', - 'x-component': 'DatePicker', - 'x-component-props': { - showTime: true - } - }, - limit: { - type: 'number', - name: 'limit', - title: '{{t("Repeat limit")}}', - 'x-decorator': 'FormItem', - 'x-component': 'InputNumber', - 'x-component-props': { - placeholder: '{{t("No limit")}}', - min: 0 - } - } - }, - 1: { - collection: { - ...collection, - 'x-reactions': [ - ...collection['x-reactions'], - { - // only full path works - target: 'config.startsOn', - fulfill: { - state: { - visible: '{{!!$self.value}}', - }, - } - } - ] - }, - startsOn: { - type: 'object', - title: '{{t("Starts on")}}', - 'x-decorator': 'FormItem', - 'x-component': 'OnField', - 'x-reactions': [ - { - target: 'config.repeat', - fulfill: { - state: { - visible: '{{!!$self.value}}', - }, - } - } - ], - required: true - }, - repeat: { - type: 'string', - name: 'repeat', - title: '{{t("Repeat mode")}}', - 'x-decorator': 'FormItem', - 'x-component': 'RepeatField', - 'x-reactions': [ - { - target: 'config.endsOn', - fulfill: { - state: { - visible: '{{!!$self.value}}', - }, - } - }, - { - target: 'config.limit', - fulfill: { - state: { - visible: '{{!!$self.value}}', - }, - } - } - ] - }, - endsOn: { - type: 'object', - title: '{{t("Ends on")}}', - 'x-decorator': 'FormItem', - 'x-component': 'EndsByField' - }, - limit: { - type: 'number', - name: 'limit', - title: '{{t("Repeat limit")}}', - 'x-decorator': 'FormItem', - 'x-component': 'InputNumber', - 'x-component-props': { - placeholder: '{{t("No limit")}}', - min: 0 - } - } - } -}; - -const ScheduleConfig = () => { - const { values = {}, clearFormGraph } = useForm(); - const { config = {} } = values; - const [mode, setMode] = useState(config.mode); - useFormEffects(() => { - onFieldValueChange('config.mode', (field) => { - setMode(field.value); - clearFormGraph('config.collection'); - clearFormGraph('config.startsOn'); - clearFormGraph('config.repeat'); - clearFormGraph('config.endsOn'); - clearFormGraph('config.limit'); - }) - }); - - return ( - <> - - - - ); -}; - -export default { - title: '{{t("Schedule event")}}', - type: 'schedule', - fieldset: { - config: { - type: 'object', - name: 'config', - 'x-component': 'ScheduleConfig', - 'x-component-props': { - } - } - }, - scope: { - useCollectionDataSource - }, - components: { - // FieldsSelect - ScheduleConfig - }, - getter({ type, options, onChange }) { - const { t } = useTranslation(); - const compile = useCompile(); - const { collections = [] } = useCollectionManager(); - const { workflow } = useFlowContext(); - const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] }; - - return ( - - ); - } -}; diff --git a/packages/core/client/src/workflow/triggers/schedule/DateFieldsSelect.tsx b/packages/core/client/src/workflow/triggers/schedule/DateFieldsSelect.tsx new file mode 100644 index 000000000..3e33e4d98 --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/DateFieldsSelect.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { observer, useForm } from "@formily/react"; +import { Select } from "antd"; + +import { useCollectionManager, useCompile } from "@nocobase/client"; + +export const DateFieldsSelect: React.FC = observer((props) => { + const compile = useCompile(); + const { getCollectionFields } = useCollectionManager(); + const { values } = useForm(); + const fields = getCollectionFields(values?.config?.collection); + + return ( + + ); +}); diff --git a/packages/core/client/src/workflow/triggers/schedule/EndsByField.tsx b/packages/core/client/src/workflow/triggers/schedule/EndsByField.tsx new file mode 100644 index 000000000..554ac3f12 --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/EndsByField.tsx @@ -0,0 +1,35 @@ +import { css } from "@emotion/css"; +import { DatePicker, Select } from "antd"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { OnField } from "./OnField"; + + + +export function EndsByField({ value, onChange }) { + const { t } = useTranslation(); + const [type, setType] = useState(typeof value === 'object' && !(value instanceof Date) ? 'field' : 'date'); + return ( +
+ + {type === 'field' + ? ( + + ) + : ( + + ) + } +
+ ); +} diff --git a/packages/core/client/src/workflow/triggers/schedule/OnField.tsx b/packages/core/client/src/workflow/triggers/schedule/OnField.tsx new file mode 100644 index 000000000..f461f34bf --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/OnField.tsx @@ -0,0 +1,47 @@ +import React, { useState } from "react"; +import { css } from "@emotion/css"; +import { InputNumber, Select } from "antd"; +import { useTranslation } from "react-i18next"; + +import { DateFieldsSelect } from "./DateFieldsSelect"; + + + +export function OnField({ value, onChange }) { + const { t } = useTranslation(); + const [dir, setDir] = useState(value.offset ? value.offset / Math.abs(value.offset) : 0); + + return ( +
+ onChange({ ...value, field })} /> + {value.field + ? ( + + ) + : null} + {dir + ? ( + <> + onChange({ ...value, offset: v * dir })}/> + + + ) + : null} +
+ ); +} diff --git a/packages/core/client/src/workflow/triggers/schedule/RepeatField.tsx b/packages/core/client/src/workflow/triggers/schedule/RepeatField.tsx new file mode 100644 index 000000000..f8a0da4d6 --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/RepeatField.tsx @@ -0,0 +1,127 @@ +import React from "react"; +import { css } from "@emotion/css"; +import { InputNumber, Select } from "antd"; +import { useTranslation } from "react-i18next"; +import { Cron } from 'react-js-cron'; + +import CronZhCN from './locale/Cron.zh-CN'; + +const languages = { + 'zh-CN': CronZhCN, +}; + + +const RepeatOptions = [ + { value: 'none', text: 'No repeat' }, + { value: 60_000, text: 'By minute', unitText: 'Minutes' }, + { value: 3600_000, text: 'By hour', unitText: 'Hours' }, + { value: 86400_000, text: 'By day', unitText: 'Days' }, + { value: 604800_000, text: 'By week', unitText: 'Weeks' }, + // { value: 18144_000_000, text: 'By 30 days' }, + { value: 'cron', text: 'Advance' } +]; + +function getNumberOption(v) { + const opts = RepeatOptions.filter(option => typeof option.value === 'number').reverse() as any[]; + return opts.find(item => !(v % item.value)); +} + +function getRepeatTypeValue(v) { + switch (typeof v) { + case 'number': + const option = getNumberOption(v); + return option ? option.value : 'none'; + case 'string': + return 'cron'; + default: + break; + } + return 'none'; +} + + +function CommonRepeatField({ value, onChange }) { + const { t } = useTranslation(); + const option = getNumberOption(value); + + return ( + onChange(v * option.value)} + min={1} + addonBefore={t('Every')} + addonAfter={t(option.unitText)} + /> + ); +} + +export function RepeatField({ value = null, onChange }) { + const { t } = useTranslation(); + const typeValue = getRepeatTypeValue(value); + function onTypeChange(v) { + if (v === 'none') { + onChange(null); + return; + } + if (v === 'cron') { + onChange('0 * * * * *'); + return; + } + onChange(v); + } + + const locale = languages[localStorage.getItem('NOCOBASE_LOCALE') || 'en-US']; + + return ( +
span{ + margin: 0 .5em 0 0; + } + + > .react-js-cron-select{ + margin: 0 .5em 0 0; + } + } + } + `}> + + {typeof typeValue === 'number' + ? + : null} + {typeValue === 'cron' + ? ( + onChange(`0 ${v}`)} + clearButton={false} + locale={locale} + /> + ) + : null} +
+ ); +} diff --git a/packages/core/client/src/workflow/triggers/schedule/ScheduleConfig.tsx b/packages/core/client/src/workflow/triggers/schedule/ScheduleConfig.tsx new file mode 100644 index 000000000..66ff5952e --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/ScheduleConfig.tsx @@ -0,0 +1,221 @@ +import React, { useState } from 'react'; +import { onFieldValueChange } from '@formily/core'; +import { useForm, useFormEffects } from '@formily/react'; +import { css } from '@emotion/css'; + +import { SchemaComponent } from '@nocobase/client'; + +import { collection } from '../../schemas/collection'; +// import { DateFieldsSelect } from './DateFieldsSelect'; +import { OnField } from './OnField'; +import { EndsByField } from './EndsByField'; +import { RepeatField } from './RepeatField'; + +const ModeFieldsets = { + 0: { + startsOn: { + type: 'datetime', + name: 'startsOn', + title: '{{t("Starts on")}}', + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + showTime: true + }, + required: true + }, + repeat: { + type: 'string', + name: 'repeat', + title: '{{t("Repeat mode")}}', + 'x-decorator': 'FormItem', + 'x-component': 'RepeatField', + 'x-reactions': [ + { + target: 'config.endsOn', + fulfill: { + state: { + visible: '{{!!$self.value}}', + }, + } + }, + { + target: 'config.limit', + fulfill: { + state: { + visible: '{{!!$self.value}}', + }, + } + } + ] + }, + endsOn: { + type: 'datetime', + name: 'endsOn', + title: '{{t("Ends on")}}', + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + showTime: true + } + }, + limit: { + type: 'number', + name: 'limit', + title: '{{t("Repeat limit")}}', + 'x-decorator': 'FormItem', + 'x-component': 'InputNumber', + 'x-component-props': { + placeholder: '{{t("No limit")}}', + min: 0 + } + } + }, + 1: { + collection: { + ...collection, + 'x-reactions': [ + ...collection['x-reactions'], + { + // only full path works + target: 'config.startsOn', + fulfill: { + state: { + visible: '{{!!$self.value}}', + }, + } + } + ] + }, + startsOn: { + type: 'object', + title: '{{t("Starts on")}}', + 'x-decorator': 'FormItem', + 'x-component': 'OnField', + 'x-reactions': [ + { + target: 'config.repeat', + fulfill: { + state: { + visible: '{{!!$self.value}}', + }, + } + } + ], + required: true + }, + repeat: { + type: 'string', + name: 'repeat', + title: '{{t("Repeat mode")}}', + 'x-decorator': 'FormItem', + 'x-component': 'RepeatField', + 'x-reactions': [ + { + target: 'config.endsOn', + fulfill: { + state: { + visible: '{{!!$self.value}}', + }, + } + }, + { + target: 'config.limit', + fulfill: { + state: { + visible: '{{!!$self.value}}', + }, + } + } + ] + }, + endsOn: { + type: 'object', + title: '{{t("Ends on")}}', + 'x-decorator': 'FormItem', + 'x-component': 'EndsByField' + }, + limit: { + type: 'number', + name: 'limit', + title: '{{t("Repeat limit")}}', + 'x-decorator': 'FormItem', + 'x-component': 'InputNumber', + 'x-component-props': { + placeholder: '{{t("No limit")}}', + min: 0 + } + } + } +}; + +export const ScheduleConfig = () => { + const { values = {}, clearFormGraph } = useForm(); + const { config = {} } = values; + const [mode, setMode] = useState(config.mode); + useFormEffects(() => { + onFieldValueChange('config.mode', (field) => { + setMode(field.value); + clearFormGraph('config.collection'); + clearFormGraph('config.startsOn'); + clearFormGraph('config.repeat'); + clearFormGraph('config.endsOn'); + clearFormGraph('config.limit'); + }) + }); + + return ( + <> + + + + ); +}; diff --git a/packages/core/client/src/workflow/triggers/schedule/index.tsx b/packages/core/client/src/workflow/triggers/schedule/index.tsx new file mode 100644 index 000000000..0284a6eb3 --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/index.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Select } from 'antd'; + +import { useCompile, useCollectionDataSource, useCollectionManager } from '@nocobase/client'; + +import { ScheduleConfig } from './ScheduleConfig'; +import { useFlowContext } from '../../WorkflowCanvas'; +import { BaseTypeSet } from '../../calculators'; + +export default { + title: '{{t("Schedule event")}}', + type: 'schedule', + fieldset: { + config: { + type: 'object', + name: 'config', + 'x-component': 'ScheduleConfig', + 'x-component-props': { + } + } + }, + scope: { + useCollectionDataSource + }, + components: { + // FieldsSelect + ScheduleConfig + }, + getter({ type, options, onChange }) { + const { t } = useTranslation(); + const compile = useCompile(); + const { collections = [] } = useCollectionManager(); + const { workflow } = useFlowContext(); + const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] }; + + return ( + + ); + } +}; diff --git a/packages/core/client/src/workflow/triggers/schedule/locale/Cron.zh-CN.ts b/packages/core/client/src/workflow/triggers/schedule/locale/Cron.zh-CN.ts new file mode 100644 index 000000000..f997c1981 --- /dev/null +++ b/packages/core/client/src/workflow/triggers/schedule/locale/Cron.zh-CN.ts @@ -0,0 +1,79 @@ +export default { + everyText: '每', + emptyMonths: '每月', + emptyMonthDays: '每日(月)', + emptyMonthDaysShort: '每日', + emptyWeekDays: '每天(周)', + emptyWeekDaysShort: '每天(周)', + emptyHours: '每小时', + emptyMinutes: '每分钟', + emptyMinutesForHourPeriod: '每', + yearOption: '年', + monthOption: '月', + weekOption: '周', + dayOption: '天', + hourOption: '小时', + minuteOption: '分钟', + rebootOption: '重启', + prefixPeriod: '每', + prefixMonths: '的', + prefixMonthDays: '的', + prefixWeekDays: '的', + prefixWeekDaysForMonthAndYearPeriod: '和', + prefixHours: '的', + prefixMinutes: ':', + prefixMinutesForHourPeriod: '的', + suffixMinutesForHourPeriod: '分钟', + errorInvalidCron: '不符合 cron 规则的表达式', + clearButtonText: '清空', + weekDays: [ + // Order is important, the index will be used as value + '周日', // Sunday must always be first, it's "0" + '周一', + '周二', + '周三', + '周四', + '周五', + '周六', + ], + months: [ + // Order is important, the index will be used as value + '一月', + '二月', + '三月', + '四月', + '五月', + '六月', + '七月', + '八月', + '九月', + '十月', + '十一月', + '十二月', + ], + altWeekDays: [ + // Order is important, the index will be used as value + '周日', // Sunday must always be first, it's "0" + '周一', + '周二', + '周三', + '周四', + '周五', + '周六', + ], + altMonths: [ + // Order is important, the index will be used as value + '一月', + '二月', + '三月', + '四月', + '五月', + '六月', + '七月', + '八月', + '九月', + '十月', + '十一月', + '十二月', + ], +} diff --git a/yarn.lock b/yarn.lock index e40ce2a40..3b8fa5823 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5234,7 +5234,14 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@^16.9.8", "@types/react-dom@^17.0.0": +"@types/react-dom@^16.9.8": + version "16.9.16" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.16.tgz#c591f2ed1c6f32e9759dfa6eb4abfd8041f29e39" + integrity sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg== + dependencies: + "@types/react" "^16" + +"@types/react-dom@^17.0.0": version "17.0.11" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== @@ -5294,7 +5301,7 @@ "@types/history" "*" "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.11", "@types/react@^16.9.43", "@types/react@^17.0.0": +"@types/react@*", "@types/react@>=16.9.11", "@types/react@^17.0.0": version "17.0.34" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== @@ -5303,6 +5310,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^16", "@types/react@^16.9.43": + version "16.14.26" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.26.tgz#82540a240ba7207ebe87d9579051bc19c9ef7605" + integrity sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -18729,6 +18745,11 @@ 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-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"