From 3b76a84605b46753eb77188a943471705163568e 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: Sun, 8 Oct 2023 20:55:30 +0800 Subject: [PATCH] fix(subtable): should not have a value by default and fix key of table (#2763) * fix(subtable): should not have a value by default and fix key of table * fix(subtable): fix invalid to set default value --- .../antd/association-field/SubTable.tsx | 2 +- .../form-item/hooks/useParseDefaultValue.ts | 5 ++-- .../antd/form-item/hooks/useSpecialCase.ts | 11 +++---- .../schema-component/antd/table-v2/Table.tsx | 30 +++++++++++++++++-- .../core/client/src/variables/constants.ts | 2 +- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx index e1b302f4a..199eabe8c 100644 --- a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx @@ -91,7 +91,7 @@ export const SubTable: any = observer( onClick={() => { field.value = field.value || []; field.value.push({}); - void field.onInput(field.value); + field.onInput(field.value); }} icon={} > diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts index 07206a546..95887953d 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts @@ -72,9 +72,8 @@ const useParseDefaultValue = () => { setDefaultValue(value); } } else { - // TODO: 可能会因为异步时序问题导致设置默认值无效 // eslint-disable-next-line promise/catch-or-return - void Promise.resolve().then(() => { + Promise.resolve().then(() => { field.setInitialValue(value); }); } @@ -91,7 +90,7 @@ const useParseDefaultValue = () => { // 使用防抖,提高性能和用户体验 const run = _.debounce(_run, DEBOUNCE_WAIT); - void _run(); + _run(); if (isVariable(fieldSchema.default)) { const variableName = getVariableName(fieldSchema.default); diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts index 8998d1497..b1a83307b 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useSpecialCase.ts @@ -162,13 +162,14 @@ export function isFromDatabase(value: Record) { export const useSubTableSpecialCase = ({ field }) => { useEffect(() => { if (_.isEmpty(field.value)) { + const value = field.value; field.value = [{}]; // 因为默认值的解析是异步的,所以下面的代码会优先于默认值的设置,这样就防止了设置完默认值后又被清空的问题 - Promise.resolve() - .then(() => { - field.value = []; - }) - .catch(console.error); + setTimeout(() => { + if (JSON.stringify(field.value) === '[{}]') { + field.value = value; + } + }); } }, []); }; diff --git a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx index c6ee9db30..ed41f61c6 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx @@ -6,11 +6,12 @@ import { ArrayField } from '@formily/core'; import { spliceArrayState } from '@formily/core/esm/shared/internals'; import { RecursionField, Schema, observer, useField, useFieldSchema } from '@formily/react'; import { action } from '@formily/reactive'; +import { uid } from '@formily/shared'; import { isPortalInBody } from '@nocobase/utils/client'; import { useMemoizedFn } from 'ahooks'; import { Table as AntdTable, TableColumnProps } from 'antd'; import { default as classNames, default as cls } from 'classnames'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { DndContext, useDesignable, useTableSize } from '../..'; import { @@ -101,12 +102,13 @@ const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) => { - void action(() => { + action(() => { spliceArrayState(field as any, { startIndex: index, deleteCount: 1, }); field.value.splice(index, 1); + field.initialValue?.splice(index, 1); return field.onInput(field.value); }); }} @@ -231,6 +233,7 @@ export const Table: any = observer( const [selectedRow, setSelectedRow] = useState([]); const dataSource = field?.value?.slice?.()?.filter?.(Boolean) || []; const isRowSelect = rowSelection?.type !== 'none'; + const defaultRowKeyMap = useRef(new Map()); let onRow = null, highlightRow = ''; @@ -339,8 +342,29 @@ export const Table: any = observer( }; }, [field, onRowDragEnd, dragSort]); + /** + * 为没有设置 key 属性的 record 生成一个唯一的 key + * 1. rowKey 的默认值是 “key”,所以先判断有没有 record.key; + * 2. 如果没有就生成一个唯一的 key,并以 record 的值作为索引; + * 3. 这样下次就能取到对应的 key 的值; + * + * 这里有效的前提是:数组中对应的 record 的引用不会发生改变。 + * + * @param record + * @returns + */ const defaultRowKey = (record: any) => { - return field.value?.indexOf?.(record); + if (record.key) { + return record.key; + } + + if (defaultRowKeyMap.current.has(record)) { + return defaultRowKeyMap.current.get(record); + } + + const key = uid(); + defaultRowKeyMap.current.set(record, key); + return key; }; const getRowKey = (record: any) => { diff --git a/packages/core/client/src/variables/constants.ts b/packages/core/client/src/variables/constants.ts index efca9325c..ed01ddb99 100644 --- a/packages/core/client/src/variables/constants.ts +++ b/packages/core/client/src/variables/constants.ts @@ -1 +1 @@ -export const DEBOUNCE_WAIT = 500; +export const DEBOUNCE_WAIT = 300;