From c25f51d99bded6567121648c52c9c9496a46f1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=B0=94-Rain?= <958414905@qq.com> Date: Tue, 14 Nov 2023 14:15:47 +0800 Subject: [PATCH] fix: fix display association fields with subform (#3036) * fix: fix display association fields with subform * fix: transformVariableValue * fix: should also to request data when value is null * fix: better way to fix --- .../useLazyLoadAssociationFieldOfForm.ts | 10 +++-- ...eLazyLoadDisplayAssociationFieldsOfForm.ts | 37 +++++++++++++++---- .../schema-component/antd/form-item/utils.ts | 10 +++++ .../src/variables/VariablesProvider.tsx | 9 +---- .../utils/transformVariableValue.test.ts | 11 ++++++ .../variables/utils/transformVariableValue.ts | 4 ++ packages/core/utils/src/common.ts | 4 ++ 7 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 packages/core/client/src/schema-component/antd/form-item/utils.ts diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadAssociationFieldOfForm.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadAssociationFieldOfForm.ts index 64239d9c4..ad8286912 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadAssociationFieldOfForm.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadAssociationFieldOfForm.ts @@ -7,6 +7,7 @@ import { useCollection, useCollectionManager } from '../../../../collection-mana import { useFlag } from '../../../../flag-provider'; import { useVariables } from '../../../../variables'; import { transformVariableValue } from '../../../../variables/utils/transformVariableValue'; +import { isDisplayField, isFieldValueEmpty } from '../utils'; /** * 用于懒加载 Form 区块中正常的关系字段 @@ -34,7 +35,8 @@ const useLazyLoadAssociationFieldOfForm = () => { if ( isInAssignFieldValues || _.isEmpty(cloneRecord) || - (formBlockType === 'create' && !isInSubForm && !isInSubTable) + (formBlockType === 'create' && !isInSubForm && !isInSubTable) || + isDisplayField(schemaName) ) { return; } @@ -45,7 +47,7 @@ const useLazyLoadAssociationFieldOfForm = () => { return; } - if (!variables || record[schemaName] != null) { + if (!variables || !isFieldValueEmpty(_.get(record, schemaName))) { return; } @@ -67,7 +69,9 @@ const useLazyLoadAssociationFieldOfForm = () => { // 3. 所以,当 `变量预加载` 的请求晚于编辑表单的请求时,就会造成这个问题; // 更优的解决方案是:在 `变量预加载` 中也加上子表单中的关系字段路径,并删除编辑表单请求数据的逻辑(因为没必要了)。 // 但是这样改动较大,可以等之后 e2e 测试较完备后再处理。 - if (field.value) return; + if (!isFieldValueEmpty(field.value)) { + return; + } field.value = transformVariableValue(value, { targetCollectionField: collectionField }); }) diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadDisplayAssociationFieldsOfForm.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadDisplayAssociationFieldsOfForm.ts index 54da901ea..9a49b5e6f 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadDisplayAssociationFieldsOfForm.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useLazyLoadDisplayAssociationFieldsOfForm.ts @@ -1,10 +1,14 @@ import { Field } from '@formily/core'; import { useField, useFieldSchema, useForm } from '@formily/react'; -import { useEffect } from 'react'; +import { nextTick } from '@nocobase/utils/client'; +import _ from 'lodash'; +import { useEffect, useRef } from 'react'; import { useCollection, useCollectionManager } from '../../../../collection-manager'; +import { useFlag } from '../../../../flag-provider'; import { useVariables } from '../../../../variables'; import { transformVariableValue } from '../../../../variables/utils/transformVariableValue'; import { useSubFormValue } from '../../association-field/hooks'; +import { isDisplayField } from '../utils'; /** * 用于懒加载 Form 区块中只用于展示的关联字段的值 @@ -20,14 +24,29 @@ const useLazyLoadDisplayAssociationFieldsOfForm = () => { const variables = useVariables(); const field = useField(); const { formValue: subFormValue } = useSubFormValue(); + const { isInSubForm, isInSubTable } = useFlag() || {}; const schemaName = fieldSchema.name.toString(); - const formValue = subFormValue || form.values; + const formValue = isInSubForm || isInSubTable ? subFormValue : form.values; + const collectionFieldRef = useRef(null); + + if (collectionFieldRef.current == null) { + collectionFieldRef.current = getCollectionJoinField(`${name}.${schemaName}`); + } + + const sourceKeyValue = + isDisplayField(schemaName) && collectionFieldRef.current + ? _.get(formValue, `${schemaName.split('.')[0]}.${collectionFieldRef.current.sourceKey}`) + : undefined; useEffect(() => { // 如果 schemaName 中是以 `.` 分割的,说明是一个关联字段,需要去获取关联字段的值; // 在数据表管理页面,也存在 `a.b` 之类的 schema name,其 collectionName 为 fields,所以在这里排除一下 `name === 'fields'` 的情况 - if (!schemaName.includes('.') || !variables || name === 'fields') { + if (!isDisplayField(schemaName) || !variables || name === 'fields' || !collectionFieldRef.current) { + return; + } + + if (_.isEmpty(formValue)) { return; } @@ -37,21 +56,23 @@ const useLazyLoadDisplayAssociationFieldsOfForm = () => { collectionName: name, }; const variableString = `{{ $nForm.${schemaName} }}`; - const collectionField = getCollectionJoinField(`${name}.${schemaName}`); - if (!collectionField) { - return console.error(new Error(`useLazyLoadAssociationField: ${schemaName} not found in collection ${name}`)); + // 如果关系字段的 id 为空,则说明请求的数据还没有返回,此时还不能去解析变量 + if (sourceKeyValue === undefined) { + return; } variables .parseVariable(variableString, formVariable) .then((value) => { - field.value = transformVariableValue(value, { targetCollectionField: collectionField }); + nextTick(() => { + field.value = transformVariableValue(value, { targetCollectionField: collectionFieldRef.current }); + }); }) .catch((err) => { console.error(err); }); - }, [schemaName.includes('.') ? formValue[schemaName.split('.')[0]] : null]); + }, [sourceKeyValue]); }; export default useLazyLoadDisplayAssociationFieldsOfForm; diff --git a/packages/core/client/src/schema-component/antd/form-item/utils.ts b/packages/core/client/src/schema-component/antd/form-item/utils.ts new file mode 100644 index 000000000..58418e736 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/form-item/utils.ts @@ -0,0 +1,10 @@ +import _ from 'lodash'; + +export const isFieldValueEmpty = (value) => { + value = _.isArray(value) ? _.filter(value, (v) => !_.isEmpty(v)) : value; + return _.isEmpty(value); +}; + +export const isDisplayField = (schemaName: string) => { + return schemaName.includes('.'); +}; diff --git a/packages/core/client/src/variables/VariablesProvider.tsx b/packages/core/client/src/variables/VariablesProvider.tsx index 0f0894d01..9acc043fa 100644 --- a/packages/core/client/src/variables/VariablesProvider.tsx +++ b/packages/core/client/src/variables/VariablesProvider.tsx @@ -277,13 +277,6 @@ VariablesProvider.displayName = 'VariablesProvider'; export default VariablesProvider; -/** - * 判断是否应该请求关系字段的数据。如果是 null 则可以确定后端的数据为空,此时不需要请求;如果是 undefined 则需要请求。 - * - * 注意:如果后端接口的这一 “规则” 发生了变更,那么可能会出现问题。 - * @param value - * @returns - */ function shouldToRequest(value) { // fix https://nocobase.height.app/T-2502 // 兼容 `对多` 和 `对一` 子表单子表格字段的情况 @@ -291,5 +284,5 @@ function shouldToRequest(value) { return true; } - return value === undefined; + return _.isEmpty(value); } diff --git a/packages/core/client/src/variables/__tests__/utils/transformVariableValue.test.ts b/packages/core/client/src/variables/__tests__/utils/transformVariableValue.test.ts index 5823702b1..a8e15d7ec 100644 --- a/packages/core/client/src/variables/__tests__/utils/transformVariableValue.test.ts +++ b/packages/core/client/src/variables/__tests__/utils/transformVariableValue.test.ts @@ -103,4 +103,15 @@ describe('transformVariableValue', () => { } `); }); + + test('value is undefined', () => { + const value = undefined; + const deps = { + targetCollectionField: { type: 'belongsToMany', uiSchema: { 'x-component': 'AssociationField' } }, + }; + + const result = transformVariableValue(value, deps as any); + + expect(result).toMatchInlineSnapshot(`undefined`); + }); }); diff --git a/packages/core/client/src/variables/utils/transformVariableValue.ts b/packages/core/client/src/variables/utils/transformVariableValue.ts index b94cdccb3..a20266c2b 100644 --- a/packages/core/client/src/variables/utils/transformVariableValue.ts +++ b/packages/core/client/src/variables/utils/transformVariableValue.ts @@ -14,6 +14,10 @@ interface Deps { export const transformVariableValue = (value: any, deps: Deps) => { const { targetCollectionField } = deps; + if (value == null) { + return value; + } + if (!targetCollectionField) { return value; } diff --git a/packages/core/utils/src/common.ts b/packages/core/utils/src/common.ts index efa216a81..f7d232401 100644 --- a/packages/core/utils/src/common.ts +++ b/packages/core/utils/src/common.ts @@ -41,3 +41,7 @@ export const hasEmptyValue = (objOrArr: object | any[]) => { } return result; }; + +export const nextTick = (fn: () => void) => { + setTimeout(fn); +};