diff --git a/packages/core/client/src/locale/en_US.ts b/packages/core/client/src/locale/en_US.ts index 68a8875db..f4fd5e52c 100644 --- a/packages/core/client/src/locale/en_US.ts +++ b/packages/core/client/src/locale/en_US.ts @@ -707,5 +707,6 @@ export default { "Current form": "Current form", "Current object":"Current object", "Linkage with form fields":"Linkage with form fields", - "Allow add new, update and delete actions":"Allow add new, update and delete actions" + "Allow add new, update and delete actions":"Allow add new, update and delete actions", + "Date display format":"Date display format" }; diff --git a/packages/core/client/src/locale/ja_JP.ts b/packages/core/client/src/locale/ja_JP.ts index ca3496767..39e05fb4a 100644 --- a/packages/core/client/src/locale/ja_JP.ts +++ b/packages/core/client/src/locale/ja_JP.ts @@ -618,5 +618,6 @@ export default { "Current form":"現在のフォーム", "Current object":"現在のオブジェクト", "Linkage with form fields":"フォームデータから連動", - "Allow add new, update and delete actions":"削除変更操作の許可" + "Allow add new, update and delete actions":"削除変更操作の許可", + "Date display format":"日付表示形式" } diff --git a/packages/core/client/src/locale/zh_CN.ts b/packages/core/client/src/locale/zh_CN.ts index 9f808c41a..1bc7ea021 100644 --- a/packages/core/client/src/locale/zh_CN.ts +++ b/packages/core/client/src/locale/zh_CN.ts @@ -792,5 +792,6 @@ export default { "Copy into the form and continue to fill in": "复制到表单并继续填写", "Linkage with form fields":"从表单字段联动", "Failed to load plugin": "插件加载失败", - "Allow add new, update and delete actions":"允许增删改操作" + "Allow add new, update and delete actions":"允许增删改操作", + "Date display format":"日期显示格式" } diff --git a/packages/core/client/src/schema-component/antd/form-item/FormItem.tsx b/packages/core/client/src/schema-component/antd/form-item/FormItem.tsx index 3e781f987..3b1ad0a9b 100644 --- a/packages/core/client/src/schema-component/antd/form-item/FormItem.tsx +++ b/packages/core/client/src/schema-component/antd/form-item/FormItem.tsx @@ -1,7 +1,7 @@ import { css, cx } from '@emotion/css'; -import { ArrayCollapse, ArrayItems, FormLayout, FormItem as Item } from '@formily/antd-v5'; +import { ArrayCollapse, ArrayItems, FormItem as Item, FormLayout } from '@formily/antd-v5'; import { Field } from '@formily/core'; -import { ISchema, Schema, observer, useField, useFieldSchema } from '@formily/react'; +import { ISchema, observer, Schema, useField, useFieldSchema } from '@formily/react'; import { dayjs } from '@nocobase/utils/client'; import { Select } from 'antd'; import _ from 'lodash'; @@ -20,9 +20,9 @@ import { } from '../../../collection-manager'; import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields'; import { GeneralSchemaItems } from '../../../schema-items/GeneralSchemaItems'; -import { GeneralSchemaDesigner, SchemaSettings, isPatternDisabled, isShowDefaultValue } from '../../../schema-settings'; -import { VariableInput } from '../../../schema-settings/VariableInput/VariableInput'; +import { GeneralSchemaDesigner, isPatternDisabled, isShowDefaultValue, SchemaSettings } from '../../../schema-settings'; import { useIsShowMultipleSwitch } from '../../../schema-settings/hooks/useIsShowMultipleSwitch'; +import { VariableInput } from '../../../schema-settings/VariableInput/VariableInput'; import { isVariable, parseVariables, useVariablesCtx } from '../../common/utils/uitls'; import { SchemaComponent } from '../../core'; import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks'; @@ -175,6 +175,8 @@ FormItem.Designer = function Designer() { const isPickerMode = fieldSchema['x-component-props']?.mode === 'Picker'; const showFieldMode = isAssociationField && fieldModeOptions && !isTableField; const showModeSelect = showFieldMode && isPickerMode; + const isDateField = ['datetime', 'createdAt', 'updatedAt'].includes(collectionField.interface); + return ( @@ -840,6 +842,7 @@ FormItem.Designer = function Designer() { }} /> )} + {isDateField && } {collectionField && } { // 需要在组件顶层调用 @@ -44,6 +44,7 @@ export const TableColumnDesigner = (props) => { const { currentMode, field: tableField } = useAssociationFieldContext(); const defaultFilter = fieldSchema?.['x-component-props']?.service?.params?.filter || {}; const dataSource = useCollectionFilterOptions(collectionField?.target); + const isDateField = ['datetime', 'createdAt', 'updatedAt'].includes(collectionField.interface); let readOnlyMode = 'editable'; if (fieldSchema['x-disabled'] === true) { readOnlyMode = 'readonly'; @@ -323,6 +324,8 @@ export const TableColumnDesigner = (props) => { }} /> )} + {isDateField && } + .ant-space-item { + flex: 1; + } +`; +export const DateFormatCom = (props?) => { + const date = moment(); + return ( +
+ {props.format} + +
+ ); +}; + +const DateTimeFormatPreview = ({ content }) => { + const { token } = useToken(); + return ( + + {content} + + ); +}; + +const InternalExpiresRadio = (props) => { + const { onChange, defaultValue, formats, timeFormat } = props; + const [isCustom, { setFalse, setTrue }] = useBoolean(props.value && !formats.includes(props.value)); + const targetValue = props.value && !formats.includes(props.value) ? props.value : defaultValue; + const [customFormatPreview, setCustomFormatPreview] = useState(targetValue ? date.format(targetValue) : null); + const onSelectChange = (v) => { + if (v.target.value === 'custom') { + setTrue(); + onChange(targetValue); + } else { + setFalse(); + onChange(v.target.value); + } + }; + + return ( + + + + {props.options.map((v) => { + if (v.value === 'custom') { + return ( + + { + if ( + e.target.value && + moment(timeFormat ? date.format() : date.toLocaleString(), e.target.value).isValid() + ) { + setCustomFormatPreview(date.format(e.target.value)); + } else { + setCustomFormatPreview(null); + } + if (isCustom) { + onChange(e.target.value); + } + }} + /> + + + ); + } + return {v.label}; + })} + + + + ); +}; + +const ExpiresRadio = connect( + InternalExpiresRadio, + mapProps({ + dataSource: 'options', + }), +); + +export { ExpiresRadio }; diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx index dc9f66410..ae8c4edc4 100644 --- a/packages/core/client/src/schema-settings/SchemaSettings.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx @@ -1,3 +1,4 @@ +import { css } from '@emotion/css'; import { ArrayCollapse, ArrayItems, FormItem, FormLayout, Input } from '@formily/antd-v5'; import { Field, GeneralField, createForm } from '@formily/core'; import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react'; @@ -63,6 +64,7 @@ import { getTargetKey } from '../schema-component/antd/association-filter/utilts import { useSchemaTemplateManager } from '../schema-templates'; import { useBlockTemplateContext } from '../schema-templates/BlockTemplate'; import { FormDataTemplates } from './DataTemplates'; +import { DateFormatCom, ExpiresRadio } from './DateFormat/ExpiresRadio'; import { EnableChildCollections } from './EnableChildCollections'; import { ChildDynamicComponent } from './EnableChildCollections/DynamicComponent'; import { FormLinkageRules } from './LinkageRules'; @@ -1267,6 +1269,151 @@ SchemaSettings.EnableChildCollections = function EnableChildCollectionsItem(prop ); }; +SchemaSettings.DataFormat = function DateFormatConfig(props: { fieldSchema: Schema }) { + const { fieldSchema } = props; + const field = useField(); + const form = useForm(); + const { dn } = useDesignable(); + const { t } = useTranslation(); + const { getCollectionJoinField } = useCollectionManager(); + const collectionField = getCollectionJoinField(fieldSchema?.['x-collection-field']) || {}; + const isShowTime = fieldSchema?.['x-component-props']?.showTime; + const dateFormatDefaultValue = + fieldSchema?.['x-component-props']?.dateFormat || + collectionField?.uiSchema?.['x-component-props']?.dateFormat || + 'YYYY-MM-DD'; + const timeFormatDefaultValue = + fieldSchema?.['x-component-props']?.timeFormat || collectionField?.uiSchema?.['x-component-props']?.timeFormat; + return ( + { + field.query('.timeFormat').take(f => { + f.display = field.value ? 'visible' : 'none'; + }); + }}}`, + ], + }, + timeFormat: { + type: 'string', + title: '{{t("Time format")}}', + 'x-component': ExpiresRadio, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + className: css` + margin-bottom: 0px; + `, + }, + 'x-component-props': { + className: css` + color: red; + .ant-radio-wrapper { + display: flex; + margin: 5px 0px; + } + `, + defaultValue: 'h:mm a', + formats: ['hh:mm:ss a', 'HH:mm:ss'], + timeFormat: true, + }, + default: timeFormatDefaultValue, + enum: [ + { + label: DateFormatCom({ format: 'hh:mm:ss a' }), + value: 'hh:mm:ss a', + }, + { + label: DateFormatCom({ format: 'HH:mm:ss' }), + value: 'HH:mm:ss', + }, + { + label: 'custom', + value: 'custom', + }, + ], + }, + }, + } as ISchema + } + onSubmit={(data) => { + 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']; + field.componentProps = fieldSchema['x-component-props']; + field.query(`.*.${fieldSchema.name}`).forEach((f) => { + f.componentProps = fieldSchema['x-component-props']; + }); + dn.emit('patch', { + schema, + }); + dn.refresh(); + }} + /> + ); +}; + // 是否显示默认值配置项 export const isShowDefaultValue = (collectionField: CollectionFieldOptions, getInterface) => { return (