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
This commit is contained in:
katherinehhh 2023-08-04 15:20:06 +08:00 committed by GitHub
parent e3b0f13923
commit dcee24a1b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 39 deletions

View File

@ -29,7 +29,6 @@ const ToOneNester = (props) => {
const ToManyNester = observer( const ToManyNester = observer(
(props) => { (props) => {
console.log(props);
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext<ArrayField>(); const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext<ArrayField>();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -48,7 +48,20 @@ export const findColumnFieldSchema = (fieldSchema, getCollectionJoinField) => {
getAssociationAppends(fieldSchema); getAssociationAppends(fieldSchema);
return [...childsSchema]; 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( export const FormItem: any = observer(
(props: any) => { (props: any) => {
useEnsureOperatorsValid(); useEnsureOperatorsValid();
@ -78,7 +91,8 @@ export const FormItem: any = observer(
const data = parseVariables(schema.default, { $context: v }); const data = parseVariables(schema.default, { $context: v });
iniValues = iniValues.concat(data); iniValues = iniValues.concat(data);
}); });
field.setInitialValue?.(_.uniqBy(iniValues, 'id')); const data = _.uniqBy(iniValues, 'id');
field.setInitialValue?.(data.length > 0 ? data : [{}]);
} else if ( } else if (
collectionField?.interface === 'o2m' && collectionField?.interface === 'o2m' &&
['SubTable', 'Nester'].includes(schema?.['x-component-props']?.['mode']) // 间接对多 ['SubTable', 'Nester'].includes(schema?.['x-component-props']?.['mode']) // 间接对多
@ -86,15 +100,19 @@ export const FormItem: any = observer(
const childrenFieldWithDefault = findColumnFieldSchema(schema, getCollectionJoinField); const childrenFieldWithDefault = findColumnFieldSchema(schema, getCollectionJoinField);
// 子表格/子表单中找出所有belongsTo字段的上下文默认值 // 子表格/子表单中找出所有belongsTo字段的上下文默认值
if (childrenFieldWithDefault.length > 0) { if (childrenFieldWithDefault.length > 0) {
const contextData = parseVariables('{{$context}}', variablesCtx); const tableData = parseVariables('{{$context}}', variablesCtx);
const initValues = contextData?.map((v) => { const contextData = {};
const obj = {}; // 将数据拍平
childrenFieldWithDefault.forEach((s: any) => { childrenFieldWithDefault?.forEach((s: any) => {
const child = JSON.parse(s); const child = JSON.parse(s);
obj[child.name] = parseVariables(child.default, { $context: v }); 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); field.setInitialValue?.(initValues);
} }
} }

View File

@ -57,6 +57,7 @@ import {
useGlobalTheme, useGlobalTheme,
useLinkageCollectionFilterOptions, useLinkageCollectionFilterOptions,
useSortFields, useSortFields,
useActionContext,
} from '..'; } from '..';
import { useTableBlockContext } from '../block-provider'; import { useTableBlockContext } from '../block-provider';
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks'; import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
@ -1449,6 +1450,7 @@ SchemaSettings.DefaultValue = function DefaultValueConfigure(props) {
const field = useField<Field>(); const field = useField<Field>();
const { dn } = useDesignable(); const { dn } = useDesignable();
const { t } = useTranslation(); const { t } = useTranslation();
const actionCtx = useActionContext();
let targetField; let targetField;
const { getField } = useCollection(); const { getField } = useCollection();
const { getCollectionJoinField } = useCollectionManager(); const { getCollectionJoinField } = useCollectionManager();
@ -1462,10 +1464,10 @@ SchemaSettings.DefaultValue = function DefaultValueConfigure(props) {
const parentFieldSchema = collectionField?.interface === 'm2o' && findParentFieldSchema(fieldSchema); const parentFieldSchema = collectionField?.interface === 'm2o' && findParentFieldSchema(fieldSchema);
const parentCollectionField = parentFieldSchema && getCollectionJoinField(parentFieldSchema?.['x-collection-field']); const parentCollectionField = parentFieldSchema && getCollectionJoinField(parentFieldSchema?.['x-collection-field']);
const tableCtx = useTableBlockContext(); const tableCtx = useTableBlockContext();
const isAllowContextVariable = const isAllowContexVariable =
collectionField?.interface === 'm2m' || actionCtx?.fieldSchema?.['x-action'] === 'customize:create' &&
(parentCollectionField?.type === 'hasMany' && collectionField?.interface === 'm2o'); (collectionField?.interface === 'm2m' ||
(parentCollectionField?.type === 'hasMany' && collectionField?.interface === 'm2o'));
return ( return (
<SchemaSettings.ModalItem <SchemaSettings.ModalItem
title={t('Set default value')} title={t('Set default value')}
@ -1482,7 +1484,7 @@ SchemaSettings.DefaultValue = function DefaultValueConfigure(props) {
'x-component-props': { 'x-component-props': {
...(fieldSchema?.['x-component-props'] || {}), ...(fieldSchema?.['x-component-props'] || {}),
collectionField, collectionField,
contextCollectionName: isAllowContextVariable && tableCtx.collection, contextCollectionName: isAllowContexVariable && tableCtx.collection,
schema: collectionField?.uiSchema, schema: collectionField?.uiSchema,
className: defaultInputStyle, className: defaultInputStyle,
renderSchemaComponent: function Com(props) { renderSchemaComponent: function Com(props) {

View File

@ -1,7 +1,7 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { CollectionFieldOptions } from '../../collection-manager'; import { CollectionFieldOptions } from '../../collection-manager';
import { useCompile, Variable } from '../../schema-component'; import { useCompile, Variable } from '../../schema-component';
import { useContextAssociationFields } from './hooks/useContextAssociationFields'; import { useContextAssociationFields, useIsSameOrChildCollection } from './hooks/useContextAssociationFields';
import { useUserVariable } from './hooks/useUserVariable'; import { useUserVariable } from './hooks/useUserVariable';
type Props = { type Props = {
@ -31,7 +31,9 @@ export const VariableInput = (props: Props) => {
} = props; } = props;
const compile = useCompile(); const compile = useCompile();
const userVariable = useUserVariable({ schema, maxDepth: 1 }); 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 scope = useMemo(() => {
const data = [ const data = [
compile({ compile({
@ -64,7 +66,7 @@ export const VariableInput = (props: Props) => {
onChange={onChange} onChange={onChange}
scope={scope} scope={scope}
style={style} style={style}
changeOnSelect={contextCollectionName !== null} changeOnSelect={isAllowTableContext}
> >
<RenderSchemaComponent value={value} onChange={onChange} /> <RenderSchemaComponent value={value} onChange={onChange} />
</Variable.Input> </Variable.Input>

View File

@ -1,9 +1,22 @@
import { error } from '@nocobase/utils/client'; import { error } from '@nocobase/utils/client';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCollectionManager, CollectionFieldOptions } from '../../../collection-manager';
import { useCompile, useGetFilterOptions } from '../../../schema-component'; import { useCompile, useGetFilterOptions } from '../../../schema-component';
import { FieldOption, Option } from '../type'; 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 { interface GetOptionsParams {
schema: any; schema: any;
depth: number; depth: number;
@ -15,37 +28,40 @@ interface GetOptionsParams {
const getChildren = ( const getChildren = (
options: FieldOption[], options: FieldOption[],
{ schema, depth, maxDepth, loadChildren, compile }: GetOptionsParams, { schema, depth, maxDepth, loadChildren, compile }: GetOptionsParams,
collectionField,
getIsSameOrChildCollection,
): Option[] => { ): Option[] => {
const result = options const result = options
.map((option): Option => { .map(
if (!option.target) { (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 { return {
key: option.name, key: option.name,
value: option.name, value: option.name,
label: compile(option.title), label: compile(option.title),
// TODO: 现在是通过组件的名称来过滤能够被选择的选项,这样的坏处是不够精确,后续可以优化 disabled: disabled,
// disabled: schema?.['x-component'] !== option.schema?.['x-component'],
isLeaf: true, isLeaf: true,
field: option,
depth, 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); .filter(Boolean);
return result; return result;
}; };
@ -53,15 +69,17 @@ export const useContextAssociationFields = ({
schema, schema,
maxDepth = 3, maxDepth = 3,
contextCollectionName, contextCollectionName,
collectionField,
}: { }: {
schema: any; schema: any;
maxDepth?: number; maxDepth?: number;
contextCollectionName: string; contextCollectionName: string;
collectionField: CollectionFieldOptions;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const compile = useCompile(); const compile = useCompile();
const getFilterOptions = useGetFilterOptions(); const getFilterOptions = useGetFilterOptions();
const getIsSameOrChildCollection = useIsSameOrChildCollection();
const loadChildren = (option: Option): Promise<void> => { const loadChildren = (option: Option): Promise<void> => {
if (!option.field?.target) { if (!option.field?.target) {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -87,6 +105,8 @@ export const useContextAssociationFields = ({
loadChildren, loadChildren,
compile, compile,
}, },
collectionField,
getIsSameOrChildCollection,
) || []; ) || [];
if (children.length === 0) { if (children.length === 0) {