Katherine 1423b62f02
feat: time field support time format (#6329)
* feat: time field support time format settings

* test: e2e test

* test: e2e test

* test: e2e test

* fix: bug
2025-02-28 15:30:22 +08:00

258 lines
7.6 KiB
TypeScript

/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { ArrayTable } from '@formily/antd-v5';
import { ISchema, useForm } from '@formily/react';
import { uid } from '@formily/shared';
import {
ActionContextProvider,
IField,
RecordProvider,
SchemaComponent,
useActionContext,
useAPIClient,
useCancelAction,
useCollectionManager_deprecated,
useCollectionParentRecordData,
useCompile,
useCurrentAppInfo,
useDataSourceManager,
useRecord,
useRequest,
useResourceActionContext,
} from '@nocobase/client';
import { cloneDeep, omit, set } from 'lodash';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { useRemoteCollectionContext } from './CollectionFields';
const getSchema = ({
schema,
record,
parentRecord,
compile,
getContainer,
}: {
schema: IField;
record: any;
parentRecord: any;
compile;
getContainer;
}): ISchema => {
if (!schema) {
return;
}
const properties = schema.getConfigureFormProperties();
if (properties?.name) {
properties.name['x-disabled'] = true;
}
return {
type: 'object',
properties: {
[uid()]: {
type: 'void',
'x-component': 'Action.Drawer',
'x-component-props': {
getContainer: '{{ getContainer }}',
},
'x-decorator': 'Form',
'x-decorator-props': {
useValues(options) {
return useRequest(
() =>
Promise.resolve({
data: {
...cloneDeep(omit(schema.default, ['uiSchema.rawTitle'])),
autoFill: schema.default?.autoFill !== false,
},
}),
options,
);
},
},
title: `${compile(parentRecord?.title || parentRecord?.name)} - ${compile('{{ t("Edit field") }}')}`,
properties: {
summary: {
type: 'void',
'x-component': 'FieldSummary',
'x-component-props': {
schemaKey: schema.name,
},
},
// @ts-ignore
...properties,
description: {
type: 'string',
title: '{{t("Description")}}',
'x-decorator': 'FormItem',
'x-component': 'Input.TextArea',
},
footer: {
type: 'void',
'x-component': 'Action.Drawer.Footer',
properties: {
action1: {
title: '{{ t("Cancel") }}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useCancelAction }}',
},
},
action2: {
title: '{{ t("Submit") }}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
useAction: '{{ useUpdateCollectionField }}',
},
},
},
},
},
},
},
};
};
const useUpdateCollectionField = () => {
const form = useForm();
const api = useAPIClient();
const ctx = useActionContext();
const { refresh } = useResourceActionContext();
const { targetCollection } = useRemoteCollectionContext();
const { name: dataSourceKey } = useParams();
const { name: filterByTk } = useRecord();
const dm = useDataSourceManager();
return {
async run() {
await form.submit();
const values = cloneDeep(form.values);
if (values.autoCreateReverseField) {
/* empty */
} else {
delete values.reverseField;
}
delete values.autoCreateReverseField;
await api.request({
url: `dataSourcesCollections/${dataSourceKey}.${targetCollection.name}/fields:update?filterByTk=${filterByTk}`,
method: 'post',
data: values,
});
ctx.setVisible(false);
dm.getDataSource(dataSourceKey).reload();
await form.reset();
refresh();
},
};
};
export const EditCollectionField = (props) => {
const record = useRecord();
const parentRecordData = useCollectionParentRecordData();
return <EditFieldAction item={record} parentItem={parentRecordData} {...props} />;
};
const EditFieldAction = (props) => {
const { scope, getContainer, item: record, parentItem: parentRecord, children, ...otherProps } = props;
const { getInterface, collections, getCollection } = useCollectionManager_deprecated();
const {
data: { database: currentDatabase },
} = useCurrentAppInfo() || {
data: { database: {} as any },
};
const [visible, setVisible] = useState(false);
const [schema, setSchema] = useState({});
const api = useAPIClient();
const { t } = useTranslation();
const compile = useCompile();
const { name } = useParams();
const isDialect = (dialect: string) => currentDatabase?.dialect === dialect;
const fields = record?.fields || getCollection(record.collectionName, name)?.options?.fields;
const currentCollections = useMemo(() => {
return collections.map((v) => {
return {
label: compile(v.title),
value: v.name,
};
});
}, []);
const scopeKeyOptions = useMemo(() => {
return (
record?.fields ||
getCollection(record.collectionName, name)
?.options?.fields.filter((v) => {
return ['string', 'bigInt', 'integer'].includes(v.type);
})
.map((k) => {
return {
value: k.name,
label: compile(k.uiSchema?.title),
};
})
);
}, [record.name, fields]);
return (
<RecordProvider record={record} parent={parentRecord}>
<ActionContextProvider value={{ visible, setVisible }}>
<a
{...otherProps}
onClick={async () => {
const { data } = await api.request({
url: `dataSourcesCollections/${name}.${parentRecord.name}/fields:get?filterByTk=${record.name}`,
params: { appends: ['reverseField'] },
});
const interfaceConf = getInterface(data?.data?.interface);
const defaultValues: any = cloneDeep(data?.data) || {};
if (!defaultValues?.reverseField) {
defaultValues.autoCreateReverseField = false;
defaultValues.reverseField = interfaceConf?.default?.reverseField;
set(defaultValues.reverseField, 'name', `f_${uid()}`);
set(defaultValues.reverseField, 'uiSchema.title', record.__parent?.title);
}
const schema = getSchema({
schema: {
...interfaceConf,
default: defaultValues,
},
record,
parentRecord,
compile,
getContainer,
});
setSchema(schema);
setVisible(true);
}}
>
{children || t('Edit')}
</a>
<SchemaComponent
schema={schema}
components={{ ArrayTable }}
scope={{
getContainer,
useUpdateCollectionField,
useCancelAction,
showReverseFieldConfig: false,
collections: currentCollections,
isDialect,
scopeKeyOptions,
disabledJSONB: true,
...scope,
createOnly: false,
createMainOnly: false,
editMainOnly: true,
}}
/>
</ActionContextProvider>
</RecordProvider>
);
};