From dcee24a1b076ef2f7f8d0c7b9aeacb7898d469dc Mon Sep 17 00:00:00 2001 From: katherinehhh Date: Fri, 4 Aug 2023 15:20:06 +0800 Subject: [PATCH] refactor: table select record restricted the usage association field (#2338) * refactor: table select recordrestricted the usage association field * refactor: table select recordrestricted the usage action * refactor: code improve * refactor: code improve * fix: code improve * refactor: code improve --- .../antd/association-field/Nester.tsx | 1 - .../antd/form-item/FormItem.tsx | 34 +++++++--- .../src/schema-settings/SchemaSettings.tsx | 12 ++-- .../VariableInput/VariableInput.tsx | 8 ++- .../hooks/useContextAssociationFields.tsx | 64 ++++++++++++------- 5 files changed, 80 insertions(+), 39 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/association-field/Nester.tsx b/packages/core/client/src/schema-component/antd/association-field/Nester.tsx index fb81a543d..6b195cef9 100644 --- a/packages/core/client/src/schema-component/antd/association-field/Nester.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/Nester.tsx @@ -29,7 +29,6 @@ const ToOneNester = (props) => { const ToManyNester = observer( (props) => { - console.log(props); const fieldSchema = useFieldSchema(); const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext(); const { t } = useTranslation(); 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 08d061fe3..2230cfb4d 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 @@ -48,7 +48,20 @@ export const findColumnFieldSchema = (fieldSchema, getCollectionJoinField) => { getAssociationAppends(fieldSchema); return [...childsSchema]; }; +function transformData(inputData) { + const transformedData = []; + const keys = Object.keys(inputData) || []; + const values: any[] = Object.values(inputData) || []; + for (let i = 0; i < values[0]?.length; i++) { + const newObj = {}; + keys.forEach((key, index) => { + newObj[key] = values[index][i]; + }); + transformedData.push(newObj); + } + return transformedData; +} export const FormItem: any = observer( (props: any) => { useEnsureOperatorsValid(); @@ -78,7 +91,8 @@ export const FormItem: any = observer( const data = parseVariables(schema.default, { $context: v }); iniValues = iniValues.concat(data); }); - field.setInitialValue?.(_.uniqBy(iniValues, 'id')); + const data = _.uniqBy(iniValues, 'id'); + field.setInitialValue?.(data.length > 0 ? data : [{}]); } else if ( collectionField?.interface === 'o2m' && ['SubTable', 'Nester'].includes(schema?.['x-component-props']?.['mode']) // 间接对多 @@ -86,15 +100,19 @@ export const FormItem: any = observer( const childrenFieldWithDefault = findColumnFieldSchema(schema, getCollectionJoinField); // 子表格/子表单中找出所有belongsTo字段的上下文默认值 if (childrenFieldWithDefault.length > 0) { - const contextData = parseVariables('{{$context}}', variablesCtx); - const initValues = contextData?.map((v) => { - const obj = {}; - childrenFieldWithDefault.forEach((s: any) => { - const child = JSON.parse(s); - obj[child.name] = parseVariables(child.default, { $context: v }); + const tableData = parseVariables('{{$context}}', variablesCtx); + const contextData = {}; + // 将数据拍平 + childrenFieldWithDefault?.forEach((s: any) => { + const child = JSON.parse(s); + tableData?.map((v) => { + contextData[child.name] = _.uniqBy( + (contextData[child.name] || []).concat(parseVariables(child.default, { $context: v })), + 'id', + ); }); - return obj; }); + const initValues = transformData(contextData); field.setInitialValue?.(initValues); } } diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx index ab26e84d9..9825e658b 100644 --- a/packages/core/client/src/schema-settings/SchemaSettings.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx @@ -57,6 +57,7 @@ import { useGlobalTheme, useLinkageCollectionFilterOptions, useSortFields, + useActionContext, } from '..'; import { useTableBlockContext } from '../block-provider'; import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks'; @@ -1449,6 +1450,7 @@ SchemaSettings.DefaultValue = function DefaultValueConfigure(props) { const field = useField(); const { dn } = useDesignable(); const { t } = useTranslation(); + const actionCtx = useActionContext(); let targetField; const { getField } = useCollection(); const { getCollectionJoinField } = useCollectionManager(); @@ -1462,10 +1464,10 @@ SchemaSettings.DefaultValue = function DefaultValueConfigure(props) { const parentFieldSchema = collectionField?.interface === 'm2o' && findParentFieldSchema(fieldSchema); const parentCollectionField = parentFieldSchema && getCollectionJoinField(parentFieldSchema?.['x-collection-field']); const tableCtx = useTableBlockContext(); - const isAllowContextVariable = - collectionField?.interface === 'm2m' || - (parentCollectionField?.type === 'hasMany' && collectionField?.interface === 'm2o'); - + const isAllowContexVariable = + actionCtx?.fieldSchema?.['x-action'] === 'customize:create' && + (collectionField?.interface === 'm2m' || + (parentCollectionField?.type === 'hasMany' && collectionField?.interface === 'm2o')); return ( { } = props; const compile = useCompile(); const userVariable = useUserVariable({ schema, maxDepth: 1 }); - const contextVariable = useContextAssociationFields({ schema, maxDepth: 2, contextCollectionName }); + const contextVariable = useContextAssociationFields({ schema, maxDepth: 2, contextCollectionName, collectionField }); + const getIsSameOrChildCollection = useIsSameOrChildCollection(); + const isAllowTableContext = getIsSameOrChildCollection(contextCollectionName, collectionField?.target); const scope = useMemo(() => { const data = [ compile({ @@ -64,7 +66,7 @@ export const VariableInput = (props: Props) => { onChange={onChange} scope={scope} style={style} - changeOnSelect={contextCollectionName !== null} + changeOnSelect={isAllowTableContext} > diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx b/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx index ce1087529..b39171f59 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx @@ -1,9 +1,22 @@ import { error } from '@nocobase/utils/client'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import { useCollectionManager, CollectionFieldOptions } from '../../../collection-manager'; import { useCompile, useGetFilterOptions } from '../../../schema-component'; import { FieldOption, Option } from '../type'; +export const useIsSameOrChildCollection = () => { + const { getChildrenCollections } = useCollectionManager(); + return (contextCollection, targetCollection) => { + console.log(contextCollection, targetCollection); + if (contextCollection === targetCollection) { + return true; + } + const childrens = getChildrenCollections(targetCollection); + return childrens?.some((v) => v.name === contextCollection); + }; +}; + interface GetOptionsParams { schema: any; depth: number; @@ -15,37 +28,40 @@ interface GetOptionsParams { const getChildren = ( options: FieldOption[], { schema, depth, maxDepth, loadChildren, compile }: GetOptionsParams, + collectionField, + getIsSameOrChildCollection, ): Option[] => { const result = options - .map((option): Option => { - if (!option.target) { + .map( + (option): Option => { + const disabled = !getIsSameOrChildCollection(option.target, collectionField?.target); + if (!option.target) { + return { + key: option.name, + value: option.name, + label: compile(option.title), + disabled: disabled, + isLeaf: true, + depth, + }; + } + + if (depth >= maxDepth) { + return null; + } return { key: option.name, value: option.name, label: compile(option.title), - // TODO: 现在是通过组件的名称来过滤能够被选择的选项,这样的坏处是不够精确,后续可以优化 - // disabled: schema?.['x-component'] !== option.schema?.['x-component'], + disabled: disabled, isLeaf: true, + field: option, depth, + loadChildren, }; - } - - if (depth >= maxDepth) { - return null; - } - - return { - key: option.name, - value: option.name, - label: compile(option.title), - isLeaf: true, - field: option, - depth, - loadChildren, - }; - }) + }, + ) .filter(Boolean); - return result; }; @@ -53,15 +69,17 @@ export const useContextAssociationFields = ({ schema, maxDepth = 3, contextCollectionName, + collectionField, }: { schema: any; maxDepth?: number; contextCollectionName: string; + collectionField: CollectionFieldOptions; }) => { const { t } = useTranslation(); const compile = useCompile(); const getFilterOptions = useGetFilterOptions(); - + const getIsSameOrChildCollection = useIsSameOrChildCollection(); const loadChildren = (option: Option): Promise => { if (!option.field?.target) { return new Promise((resolve) => { @@ -87,6 +105,8 @@ export const useContextAssociationFields = ({ loadChildren, compile, }, + collectionField, + getIsSameOrChildCollection, ) || []; if (children.length === 0) {