mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 06:35:20 +00:00
refactor: form data templates and depulicate action support sync from form fields (#2314)
* refactor: sync from form fields * refactor: sync from form fields * refactor: sync from form fields * refactor: data fields * refactor: traverseFields * refactor: traverseFields * refactor: locale improve * fix: merge bug * refactor: code improve * refactor: code improve * refactor: code improve * refactor: depulicate action support sync form form fields * refactor: code refactor * refactor: direct duplicate support select all * refactor: code improve * refactor: code improve * refactor: hasOne and hasMany avaliable for deplicate * refactor: code improve * refactor: locale improve * refactor: code improve * refactor: code improve
This commit is contained in:
parent
c743d66b8e
commit
8f1d0d80af
@ -1100,8 +1100,7 @@ export const useAssociationFilterBlockProps = () => {
|
||||
labelKey,
|
||||
};
|
||||
};
|
||||
|
||||
function getAssociationPath(str) {
|
||||
export function getAssociationPath(str) {
|
||||
const lastIndex = str.lastIndexOf('.');
|
||||
if (lastIndex !== -1) {
|
||||
return str.substring(0, lastIndex);
|
||||
|
@ -710,5 +710,8 @@ export default {
|
||||
"Allow add new, update and delete actions":"Allow add new, update and delete actions",
|
||||
"Date display format":"Date display format",
|
||||
"Assign data scope for the template":"Assign data scope for the template",
|
||||
"Table selected records":"Table selected records"
|
||||
"Table selected records":"Table selected records",
|
||||
"Sync successfully":"Sync successfully",
|
||||
"Sync from form fields":"Sync from form fields",
|
||||
"Select all":"Select all"
|
||||
};
|
||||
|
@ -621,4 +621,7 @@ export default {
|
||||
"Allow add new, update and delete actions":"削除変更操作の許可",
|
||||
"Date display format":"日付表示形式",
|
||||
"Assign data scope for the template":"テンプレートのデータ範囲の指定",
|
||||
"Sync successfully":"同期成功",
|
||||
"Sync from form fields":"フォームフィールドの同期",
|
||||
"Select all":"すべて選択"
|
||||
}
|
||||
|
@ -795,5 +795,8 @@ export default {
|
||||
"Allow add new, update and delete actions":"允许增删改操作",
|
||||
"Date display format":"日期显示格式",
|
||||
"Assign data scope for the template":"为模板指定数据范围",
|
||||
"Table selected records":"表格中选中的记录"
|
||||
"Table selected records":"表格中选中的记录",
|
||||
"Sync successfully":"同步成功",
|
||||
"Sync from form fields":"同步表单字段",
|
||||
"Select all":"全选"
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { connect, ISchema, mapProps, useField, useFieldSchema } from '@formily/react';
|
||||
import { connect, ISchema, mapProps, useField, useFieldSchema, useForm } from '@formily/react';
|
||||
import { isValid, uid } from '@formily/shared';
|
||||
import { Tree as AntdTree } from 'antd';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDesignable } from '../..';
|
||||
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
||||
@ -12,15 +12,21 @@ import { useCollectionState } from '../../../schema-settings/DataTemplates/hooks
|
||||
import { useLinkageAction } from './hooks';
|
||||
import { requestSettingsSchema } from './utils';
|
||||
import { useRecord } from '../../../record-provider';
|
||||
import { useSyncFromForm } from '../../../schema-settings/DataTemplates/utils';
|
||||
|
||||
const Tree = connect(
|
||||
AntdTree,
|
||||
mapProps((props, field: any) => {
|
||||
const [checkedKeys, setCheckedKeys] = useState(props.defaultCheckedKeys || []);
|
||||
const onCheck = (checkedKeys) => {
|
||||
setCheckedKeys(checkedKeys);
|
||||
field.value = checkedKeys;
|
||||
};
|
||||
field.onCheck = onCheck;
|
||||
return {
|
||||
...props,
|
||||
onCheck: (checkedKeys) => {
|
||||
field.value = checkedKeys;
|
||||
},
|
||||
checkedKeys,
|
||||
onCheck,
|
||||
};
|
||||
}),
|
||||
);
|
||||
@ -218,6 +224,28 @@ function SaveMode() {
|
||||
);
|
||||
}
|
||||
|
||||
const findFormBlock = (schema) => {
|
||||
const formSchema = schema.reduceProperties((_, s) => {
|
||||
if (s['x-decorator'] === 'FormBlockProvider') {
|
||||
return s;
|
||||
} else {
|
||||
return findFormBlock(s);
|
||||
}
|
||||
}, null);
|
||||
return formSchema;
|
||||
};
|
||||
|
||||
const getAllkeys = (data, result) => {
|
||||
for (let i = 0; i < data?.length; i++) {
|
||||
const { children, ...rest } = data[i];
|
||||
result.push(rest.key);
|
||||
if (children) {
|
||||
getAllkeys(children, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
function DuplicationMode() {
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
@ -227,7 +255,27 @@ function DuplicationMode() {
|
||||
const { collectionList, getEnableFieldTree, getOnLoadData, getOnCheck } = useCollectionState(name);
|
||||
const duplicateValues = cloneDeep(fieldSchema['x-component-props'].duplicateFields || []);
|
||||
const record = useRecord();
|
||||
|
||||
const syncCallBack = useCallback((treeData, selectFields, form) => {
|
||||
form.query('duplicateFields').take((f) => {
|
||||
f.componentProps.treeData = treeData;
|
||||
f.componentProps.defaultCheckedKeys = selectFields;
|
||||
f.setInitialValue(selectFields);
|
||||
f?.onCheck(selectFields);
|
||||
form.setValues({ ...form.values, treeData });
|
||||
});
|
||||
}, []);
|
||||
const useSelectAllFields = (form) => {
|
||||
return {
|
||||
async run() {
|
||||
form.query('duplicateFields').take((f) => {
|
||||
const selectFields = getAllkeys(f.componentProps.treeData, []);
|
||||
f.componentProps.defaultCheckedKeys = selectFields;
|
||||
f.setInitialValue(selectFields);
|
||||
f?.onCheck(selectFields);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
return (
|
||||
<SchemaSettings.ModalItem
|
||||
title={t('Duplicate mode')}
|
||||
@ -238,6 +286,7 @@ function DuplicationMode() {
|
||||
currentCollection: record?.__collection || name,
|
||||
getOnLoadData,
|
||||
getOnCheck,
|
||||
treeData: fieldSchema['x-component-props']?.treeData,
|
||||
}}
|
||||
schema={
|
||||
{
|
||||
@ -278,11 +327,60 @@ function DuplicationMode() {
|
||||
},
|
||||
],
|
||||
},
|
||||
syncFromForm: {
|
||||
type: 'void',
|
||||
title: '{{ t("Sync from form fields") }}',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
style: { float: 'right', position: 'relative', zIndex: 1200 },
|
||||
useAction: () => {
|
||||
const formSchema = useMemo(() => findFormBlock(fieldSchema), [fieldSchema]);
|
||||
return useSyncFromForm(
|
||||
formSchema,
|
||||
fieldSchema['x-component-props']?.duplicateCollection || record?.__collection || name,
|
||||
syncCallBack,
|
||||
);
|
||||
},
|
||||
},
|
||||
'x-reactions': [
|
||||
{
|
||||
dependencies: ['.duplicateMode'],
|
||||
fulfill: {
|
||||
state: {
|
||||
visible: `{{ $deps[0]!=="quickDulicate" }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
selectAll: {
|
||||
type: 'void',
|
||||
title: '{{ t("Select all") }}',
|
||||
'x-component': 'Action.Link',
|
||||
'x-reactions': [
|
||||
{
|
||||
dependencies: ['.duplicateMode'],
|
||||
fulfill: {
|
||||
state: {
|
||||
visible: `{{ $deps[0]==="quickDulicate" }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
style: { float: 'right', position: 'relative', zIndex: 1200 },
|
||||
useAction: () => {
|
||||
const from = useForm();
|
||||
return useSelectAllFields(from);
|
||||
},
|
||||
},
|
||||
},
|
||||
duplicateFields: {
|
||||
type: 'array',
|
||||
title: '{{ t("Data fields") }}',
|
||||
required: true,
|
||||
default: duplicateValues,
|
||||
description: t('Only the selected fields will be used as the initialization data for the form'),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': Tree,
|
||||
@ -310,7 +408,7 @@ function DuplicationMode() {
|
||||
state: {
|
||||
disabled: '{{ !$deps[0] }}',
|
||||
componentProps: {
|
||||
treeData: '{{ getEnableFieldTree($deps[0], $self) }}',
|
||||
treeData: '{{ getEnableFieldTree($deps[0], $self,treeData) }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -320,7 +418,7 @@ function DuplicationMode() {
|
||||
},
|
||||
} as ISchema
|
||||
}
|
||||
onSubmit={({ duplicateMode, collection, duplicateFields }) => {
|
||||
onSubmit={({ duplicateMode, collection, duplicateFields, treeData }) => {
|
||||
const fields = Array.isArray(duplicateFields) ? duplicateFields : duplicateFields.checked || [];
|
||||
field.componentProps.duplicateMode = duplicateMode;
|
||||
field.componentProps.duplicateFields = fields;
|
||||
@ -328,6 +426,7 @@ function DuplicationMode() {
|
||||
fieldSchema['x-component-props'].duplicateMode = duplicateMode;
|
||||
fieldSchema['x-component-props'].duplicateFields = fields;
|
||||
fieldSchema['x-component-props'].duplicateCollection = collection;
|
||||
fieldSchema['x-component-props'].treeData = treeData;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
@ -580,7 +679,7 @@ export const ActionDesigner = (props) => {
|
||||
const { name } = useCollection();
|
||||
const { getChildrenCollections } = useCollectionManager();
|
||||
const isAction = useLinkageAction();
|
||||
const isPopupAction = ['create', 'update', 'view', 'customize:popup', 'duplicate','customize:create'].includes(
|
||||
const isPopupAction = ['create', 'update', 'view', 'customize:popup', 'duplicate', 'customize:create'].includes(
|
||||
fieldSchema['x-action'] || '',
|
||||
);
|
||||
const isUpdateModePopupAction = ['customize:bulkUpdate', 'customize:bulkEdit'].includes(fieldSchema['x-action']);
|
||||
|
@ -163,7 +163,7 @@ export const Templates = ({ style = {}, form }) => {
|
||||
{targetTemplate !== 'none' && (
|
||||
<RemoteSelect
|
||||
style={{ width: 220 }}
|
||||
fieldNames={{ label: template.titleField, value: 'id' }}
|
||||
fieldNames={{ label: template?.titleField, value: 'id' }}
|
||||
target={template?.collection}
|
||||
value={targetTemplateData}
|
||||
objectValue
|
||||
|
@ -13,6 +13,7 @@ import { AsDefaultTemplate } from './components/AsDefaultTemplate';
|
||||
import { ArrayCollapse } from './components/DataTemplateTitle';
|
||||
import { getSelectedIdFilter } from './components/Designer';
|
||||
import { useCollectionState } from './hooks/useCollectionState';
|
||||
import { useSyncFromForm } from './utils';
|
||||
|
||||
const Tree = connect(
|
||||
AntdTree,
|
||||
@ -48,7 +49,6 @@ export const FormDataTemplates = observer(
|
||||
} = useCollectionState(collectionName);
|
||||
const { getCollection, getCollectionField } = useCollectionManager();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// 不要在后面的数组中依赖 defaultValues,否则会因为 defaultValues 的变化导致 activeData 响应性丢失
|
||||
const activeData = useMemo<ITemplate>(
|
||||
() =>
|
||||
@ -61,7 +61,6 @@ export const FormDataTemplates = observer(
|
||||
),
|
||||
[],
|
||||
);
|
||||
console.log(activeData);
|
||||
const getTargetField = (collectionName: string) => {
|
||||
const collection = getCollection(collectionName);
|
||||
return getCollectionField(
|
||||
@ -170,6 +169,16 @@ export const FormDataTemplates = observer(
|
||||
required: true,
|
||||
'x-reactions': '{{useTitleFieldDataSource}}',
|
||||
},
|
||||
syncFromForm: {
|
||||
type: 'void',
|
||||
title: '{{ t("Sync from form fields") }}',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
style: { float: 'right', position: 'relative', zIndex: 1200 },
|
||||
useAction: () => useSyncFromForm(formSchema),
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
title: '{{ t("Data fields") }}',
|
||||
|
@ -48,10 +48,9 @@ const DataTemplateTitle = observer<{ index: number; item: any }>((props) => {
|
||||
export interface IArrayCollapseProps extends CollapseProps {
|
||||
defaultOpenPanelCount?: number;
|
||||
}
|
||||
type ComposedArrayCollapse =
|
||||
| React.FC<React.PropsWithChildren<IArrayCollapseProps>> & {
|
||||
CollapsePanel?: React.FC<React.PropsWithChildren<CollapsePanelProps>>;
|
||||
};
|
||||
type ComposedArrayCollapse = React.FC<React.PropsWithChildren<IArrayCollapseProps>> & {
|
||||
CollapsePanel?: React.FC<React.PropsWithChildren<CollapsePanelProps>>;
|
||||
};
|
||||
|
||||
const isAdditionComponent = (schema: ISchema) => {
|
||||
return schema['x-component']?.indexOf?.('Addition') > -1;
|
||||
@ -218,6 +217,9 @@ export const ArrayCollapse: ComposedArrayCollapse = observer(
|
||||
onAdd={(index) => {
|
||||
setActiveKeys(insertActiveKeys(activeKeys, index));
|
||||
}}
|
||||
onRemove={() => {
|
||||
field.initialValue = field.value;
|
||||
}}
|
||||
>
|
||||
{renderEmpty()}
|
||||
{renderItems()}
|
||||
|
@ -1,32 +1,34 @@
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { useField } from '@formily/react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useCollectionManager } from '../../../collection-manager';
|
||||
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
import { TreeNode } from '../TreeLabel';
|
||||
|
||||
// 过滤掉系统字段
|
||||
export const systemKeys = [
|
||||
// 'id',
|
||||
'sort',
|
||||
'createdById',
|
||||
'createdBy',
|
||||
'createdAt',
|
||||
'updatedById',
|
||||
'updatedBy',
|
||||
'updatedAt',
|
||||
'password',
|
||||
'sequence',
|
||||
];
|
||||
export const useCollectionState = (currentCollectionName: string) => {
|
||||
const { getCollectionFields, getAllCollectionsInheritChain, getCollection, getInterface } = useCollectionManager();
|
||||
const [collectionList] = useState(getCollectionList);
|
||||
const compile = useCompile();
|
||||
const templateField: any = useField();
|
||||
|
||||
function getCollectionList() {
|
||||
const collections = getAllCollectionsInheritChain(currentCollectionName);
|
||||
return collections.map((name) => ({ label: getCollection(name)?.title, value: name }));
|
||||
}
|
||||
|
||||
// 过滤掉系统字段
|
||||
const systemKeys = [
|
||||
// 'id',
|
||||
'sort',
|
||||
'createdById',
|
||||
'createdBy',
|
||||
'createdAt',
|
||||
'updatedById',
|
||||
'updatedBy',
|
||||
'updatedAt',
|
||||
];
|
||||
|
||||
/**
|
||||
* maxDepth: 从 0 开始,0 表示一层,1 表示两层,以此类推
|
||||
*/
|
||||
@ -115,12 +117,25 @@ export const useCollectionState = (currentCollectionName: string) => {
|
||||
})
|
||||
.filter(Boolean);
|
||||
};
|
||||
const parseTreeData = (data) => {
|
||||
return data.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
title: React.createElement(TreeNode, { ...v, type: v.type }),
|
||||
children: v.children ? parseTreeData(v.children) : null,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const getEnableFieldTree = useCallback((collectionName: string) => {
|
||||
const getEnableFieldTree = useCallback((collectionName: string, field, treeData?) => {
|
||||
const index = field.index;
|
||||
const targetTemplate = templateField.initialValue?.items?.[index];
|
||||
if (!collectionName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (targetTemplate?.treeData || treeData) {
|
||||
return parseTreeData(treeData || targetTemplate.treeData);
|
||||
}
|
||||
try {
|
||||
return traverseFields(collectionName, { exclude: ['id', ...systemKeys], maxDepth: 1 });
|
||||
} catch (error) {
|
||||
@ -242,9 +257,8 @@ function findNode(treeData, item) {
|
||||
}
|
||||
|
||||
function loadChildren({ node, traverseAssociations, traverseFields, systemKeys, fields }) {
|
||||
const activeNode = findNode(fields.componentProps.treeData, node);
|
||||
const activeNode = findNode(fields.dataSource || fields.componentProps.treeData, node);
|
||||
let children = [];
|
||||
|
||||
// 多对多和多对一只展示关系字段
|
||||
if (['belongsTo', 'belongsToMany'].includes(node.field.type)) {
|
||||
children = traverseAssociations(node.field.target, {
|
||||
|
232
packages/core/client/src/schema-settings/DataTemplates/utils.tsx
Normal file
232
packages/core/client/src/schema-settings/DataTemplates/utils.tsx
Normal file
@ -0,0 +1,232 @@
|
||||
import { ArrayBase } from '@formily/antd-v5';
|
||||
import { useForm } from '@formily/react';
|
||||
import { message } from 'antd';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAssociationPath } from '../../block-provider/hooks';
|
||||
import { useCollectionManager } from '../../collection-manager';
|
||||
import { useCompile } from '../../schema-component';
|
||||
import { TreeNode } from './TreeLabel';
|
||||
import { systemKeys } from './hooks/useCollectionState';
|
||||
import LRUCache from 'lru-cache';
|
||||
|
||||
export const useSyncFromForm = (fieldSchema, collection?, callBack?) => {
|
||||
const { getCollectionJoinField, getCollectionFields } = useCollectionManager();
|
||||
const array = ArrayBase.useArray();
|
||||
const index = ArrayBase.useIndex();
|
||||
const record = ArrayBase.useRecord();
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const from = useForm();
|
||||
|
||||
const traverseFields = ((cache) => {
|
||||
return (collectionName, { exclude = [], depth = 0, maxDepth, prefix = '', disabled = false }, formData) => {
|
||||
const cacheKey = `${collectionName}-${exclude.join(',')}-${depth}-${maxDepth}-${prefix}`;
|
||||
const cachedResult = cache.get(cacheKey);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
}
|
||||
if (depth > maxDepth) {
|
||||
return [];
|
||||
}
|
||||
const result = getCollectionFields(collectionName)
|
||||
.map((field) => {
|
||||
if (exclude.includes(field.name)) {
|
||||
return;
|
||||
}
|
||||
if (!field.interface) {
|
||||
return;
|
||||
}
|
||||
if (['sort', 'password', 'sequence'].includes(field.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = {
|
||||
type: 'duplicate',
|
||||
tag: compile(field.uiSchema?.title) || field.name,
|
||||
};
|
||||
const option = {
|
||||
...node,
|
||||
title: React.createElement(TreeNode, node),
|
||||
key: prefix ? `${prefix}.${field.name}` : field.name,
|
||||
isLeaf: true,
|
||||
field,
|
||||
disabled,
|
||||
};
|
||||
const tatgetFormField = formData.find((v) => v.name === field.name);
|
||||
if (
|
||||
['belongsTo', 'belongsToMany'].includes(field.type) &&
|
||||
(!tatgetFormField || ['Select', 'Picker'].includes(tatgetFormField?.fieldMode))
|
||||
) {
|
||||
node['type'] = 'reference';
|
||||
option['type'] = 'reference';
|
||||
option['title'] = React.createElement(TreeNode, { ...node, type: 'reference' });
|
||||
option.isLeaf = false;
|
||||
option['children'] = traverseAssociations(field.target, {
|
||||
depth: depth + 1,
|
||||
maxDepth,
|
||||
prefix: option.key,
|
||||
exclude: systemKeys,
|
||||
});
|
||||
} else if (
|
||||
['hasOne', 'hasMany'].includes(field.type) ||
|
||||
['Nester', 'SubTable'].includes(tatgetFormField?.fieldMode)
|
||||
) {
|
||||
let childrenDisabled = false;
|
||||
if (
|
||||
['hasOne', 'hasMany'].includes(field.type) &&
|
||||
['Select', 'Picker'].includes(tatgetFormField?.fieldMode)
|
||||
) {
|
||||
childrenDisabled = true;
|
||||
}
|
||||
option.disabled = true;
|
||||
option.isLeaf = false;
|
||||
option['children'] = traverseFields(
|
||||
field.target,
|
||||
{
|
||||
depth: depth + 1,
|
||||
maxDepth,
|
||||
prefix: option.key,
|
||||
exclude: ['id', ...systemKeys],
|
||||
disabled: childrenDisabled,
|
||||
},
|
||||
formData,
|
||||
);
|
||||
}
|
||||
return option;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
cache.set(cacheKey, result);
|
||||
return result;
|
||||
};
|
||||
})(
|
||||
new LRUCache<string, any>({ max: 100 }),
|
||||
);
|
||||
|
||||
const traverseAssociations = ((cache) => {
|
||||
return (collectionName, { prefix, maxDepth, depth = 0, exclude = [] }) => {
|
||||
const cacheKey = `${collectionName}-${exclude.join(',')}-${depth}-${maxDepth}-${prefix}`;
|
||||
const cachedResult = cache.get(cacheKey);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
if (depth > maxDepth) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = getCollectionFields(collectionName)
|
||||
.map((field) => {
|
||||
if (!field.target || !field.interface) {
|
||||
return;
|
||||
}
|
||||
if (exclude.includes(field.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const option = {
|
||||
type: 'preloading',
|
||||
tag: compile(field.uiSchema?.title) || field.name,
|
||||
};
|
||||
const value = prefix ? `${prefix}.${field.name}` : field.name;
|
||||
return {
|
||||
type: 'preloading',
|
||||
tag: compile(field.uiSchema?.title) || field.name,
|
||||
title: React.createElement(TreeNode, option),
|
||||
key: value,
|
||||
isLeaf: false,
|
||||
field,
|
||||
children: traverseAssociations(field.target, {
|
||||
prefix: value,
|
||||
depth: depth + 1,
|
||||
maxDepth,
|
||||
exclude,
|
||||
}),
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
cache.set(cacheKey, result);
|
||||
return result;
|
||||
};
|
||||
})(
|
||||
new LRUCache<string, any>({ max: 100 }),
|
||||
);
|
||||
const getEnableFieldTree = useCallback((collectionName: string, formData) => {
|
||||
if (!collectionName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
return traverseFields(collectionName, { exclude: ['id', ...systemKeys], maxDepth: 1, disabled: false }, formData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
async run() {
|
||||
const formData = new Set([]);
|
||||
const selectFields = new Set([]);
|
||||
const getAssociationAppends = (schema, str) => {
|
||||
schema.reduceProperties((pre, s) => {
|
||||
const prefix = pre || str;
|
||||
const collectionfield = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']);
|
||||
const isAssociationSubfield = s.name.includes('.');
|
||||
const isAssociationField =
|
||||
collectionfield && ['hasOne', 'hasMany', 'belongsTo', 'belongsToMany'].includes(collectionfield.type);
|
||||
const fieldPath = !isAssociationField && isAssociationSubfield ? getAssociationPath(s.name) : s.name;
|
||||
const path = prefix === '' || !prefix ? fieldPath : prefix + '.' + fieldPath;
|
||||
if (
|
||||
collectionfield &&
|
||||
!(
|
||||
['hasOne', 'hasMany'].includes(collectionfield.type) ||
|
||||
['SubForm', 'Nester'].includes(s['x-component-props']?.mode)
|
||||
)
|
||||
) {
|
||||
selectFields.add(path);
|
||||
}
|
||||
if (collectionfield && (isAssociationField || isAssociationSubfield) && s['x-component'] !== 'TableField') {
|
||||
formData.add({ name: path, fieldMode: s['x-component-props']['mode'] || 'Select' });
|
||||
if (['Nester', 'SubTable'].includes(s['x-component-props']?.mode)) {
|
||||
const bufPrefix = prefix && prefix !== '' ? prefix + '.' + s.name : s.name;
|
||||
getAssociationAppends(s, bufPrefix);
|
||||
}
|
||||
} else if (
|
||||
![
|
||||
'ActionBar',
|
||||
'Action',
|
||||
'Action.Link',
|
||||
'Action.Modal',
|
||||
'Selector',
|
||||
'Viewer',
|
||||
'AddNewer',
|
||||
'AssociationField.Selector',
|
||||
'AssociationField.AddNewer',
|
||||
'TableField',
|
||||
].includes(s['x-component'])
|
||||
) {
|
||||
getAssociationAppends(s, str);
|
||||
}
|
||||
}, str);
|
||||
};
|
||||
getAssociationAppends(fieldSchema, '');
|
||||
const treeData = getEnableFieldTree(record?.collection || collection, [...formData]);
|
||||
if (callBack) {
|
||||
callBack(treeData, [...selectFields], from);
|
||||
} else {
|
||||
array?.field.form.query(`fieldReaction.items.${index}.layout.fields`).take((f: any) => {
|
||||
f.componentProps.treeData = [];
|
||||
setTimeout(() => (f.componentProps.treeData = treeData));
|
||||
});
|
||||
array?.field.value.splice(index, 1, {
|
||||
...array?.field?.value[index],
|
||||
fields: [...selectFields],
|
||||
treeData: treeData,
|
||||
});
|
||||
}
|
||||
message.success(t('Sync successfully'));
|
||||
},
|
||||
};
|
||||
};
|
@ -1146,7 +1146,6 @@ SchemaSettings.DataTemplates = function DataTemplates(props) {
|
||||
const { t } = useTranslation();
|
||||
const formSchema = findFormBlock(fieldSchema) || fieldSchema;
|
||||
const { templateData } = useDataTemplates();
|
||||
|
||||
const schema = useMemo(
|
||||
() => ({
|
||||
type: 'object',
|
||||
@ -1171,7 +1170,6 @@ SchemaSettings.DataTemplates = function DataTemplates(props) {
|
||||
);
|
||||
const onSubmit = useCallback((v) => {
|
||||
const data = { ...(formSchema['x-data-templates'] || {}), ...v.fieldReaction };
|
||||
|
||||
// 当 Tree 组件开启 checkStrictly 属性时,会导致 checkedKeys 的值是一个对象,而不是数组,所以这里需要转换一下以支持旧版本
|
||||
data.items.forEach((item) => {
|
||||
item.fields = Array.isArray(item.fields) ? item.fields : item.fields.checked;
|
||||
|
Loading…
Reference in New Issue
Block a user