@zat-design/sisyphus-react 4.4.1-beta.2 → 4.4.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/index.esm.css +1 -1
  2. package/dist/less.esm.css +1 -1
  3. package/es/ProConfigProvider/index.d.ts +1 -1
  4. package/es/ProConfigProvider/index.js +6 -7
  5. package/es/ProConfigProvider/propsType.d.ts +22 -0
  6. package/es/ProEditTable/components/RcTable/DraggableTable.js +18 -32
  7. package/es/ProEditTable/index.js +7 -1
  8. package/es/ProEditTable/style/index.less +0 -5
  9. package/es/ProEditTable/utils/config.js +2 -0
  10. package/es/ProEditTable/utils/index.d.ts +15 -1
  11. package/es/ProEditTable/utils/index.js +27 -15
  12. package/es/ProForm/components/combination/Group/utils/index.d.ts +9 -9
  13. package/es/ProForm/utils/useWatch.d.ts +14 -4
  14. package/es/ProForm/utils/useWatch.js +48 -18
  15. package/es/ProTable/components/EditableCell/index.js +55 -22
  16. package/es/ProTable/components/EditableCell/propsType.d.ts +14 -0
  17. package/es/ProTable/components/FormatColumn/index.d.ts +4 -2
  18. package/es/ProTable/components/FormatColumn/index.js +13 -46
  19. package/es/ProTable/components/RcTable/components/BaseTable/index.d.ts +1 -8
  20. package/es/ProTable/components/RcTable/components/BaseTable/index.js +5 -4
  21. package/es/ProTable/components/RcTable/components/DraggableTable/components/DndWrapper/index.d.ts +1 -1
  22. package/es/ProTable/components/RcTable/components/DraggableTable/components/DndWrapper/index.js +19 -32
  23. package/es/ProTable/components/RcTable/components/DraggableTable/index.d.ts +1 -8
  24. package/es/ProTable/components/RcTable/components/DraggableTable/index.js +5 -4
  25. package/es/ProTable/components/RenderColumn/index.js +1 -17
  26. package/es/ProTable/index.d.ts +9 -8
  27. package/es/ProTable/index.js +151 -111
  28. package/es/ProTable/propsType.d.ts +21 -6
  29. package/es/ProTable/utils/columnStorage.d.ts +35 -0
  30. package/es/ProTable/utils/columnStorage.js +171 -0
  31. package/es/ProTable/utils/index.d.ts +16 -2
  32. package/es/ProTable/utils/index.js +34 -21
  33. package/es/hooks/useDraggableRow.d.ts +34 -0
  34. package/es/hooks/useDraggableRow.js +70 -0
  35. package/package.json +1 -3
  36. package/es/ProForm/components/base/DatePicker/useDateLimit.d.ts +0 -3
  37. package/es/ProForm/components/base/DatePicker/useDateLimit.js +0 -7
  38. package/es/ProForm/components/combination/FormList/components/BlockTitle.d.ts +0 -13
  39. package/es/ProForm/components/combination/FormList/components/BlockTitle.js +0 -31
@@ -5,5 +5,5 @@ export declare const ProConfigContext: React.Context<ConfigContext>;
5
5
  export declare function useProConfig(): ConfigContext;
6
6
  export declare function useProConfig(name: keyof ConfigContext['state']): ConfigContext['state'][keyof ConfigContext['state']];
7
7
  export declare const ProConfigProvider: FC<ProConfigProviderProps>;
8
- export type { ProConfigProviderType, ProConfigProviderProps } from './propsType';
8
+ export type { ProConfigProviderType, ProConfigProviderProps, GlobalConfig, ProTableGlobalConfig, StorageType, } from './propsType';
9
9
  export default ProConfigProvider;
@@ -113,16 +113,15 @@ const ThemeAwareConfigProvider = ({
113
113
  };
114
114
  export const ProConfigProvider = props => {
115
115
  const [state, dispatch] = useReducer(reducer, initialState);
116
- const enumRes = useEnumRequest({
116
+
117
+ // globalConfig(含 storage)合并进 ProEnum 配置,useEnumRequest 内部对所有字段均有默认值
118
+ const enumEnv = {
117
119
  ...state.ProEnum,
118
120
  ...props?.value?.ProEnum,
119
121
  ...props?.value?.globalConfig
120
- }, dispatch);
121
- const frequentEnumRes = useFrequentEnumRequest({
122
- ...state.ProEnum,
123
- ...props?.value?.ProEnum,
124
- ...props?.value?.globalConfig
125
- }, dispatch);
122
+ };
123
+ const enumRes = useEnumRequest(enumEnv, dispatch);
124
+ const frequentEnumRes = useFrequentEnumRequest(enumEnv, dispatch);
126
125
  const cacheLang = localStorage.getItem('locale');
127
126
  const lang = state?.locale ?? cacheLang ?? props?.locale ?? 'zh-CN';
128
127
  useEffect(() => {
@@ -4,9 +4,31 @@ import type { PropSelectType } from '../ProSelect/propsType';
4
4
  import type { ProModalSelectType } from '../ProForm/components/combination/ProModalSelect/propsType';
5
5
  import type { ProCascaderType } from '../ProForm/components/combination/ProCascader/propsType';
6
6
  import type { ProEnumConfigType } from '../ProEnum/propsType';
7
+ /** 浏览器缓存类型,与 ProEnum / ProTable 的 StorageType 定义一致 */
8
+ export type StorageType = 'localStorage' | 'sessionStorage' | 'indexedDB';
9
+ /** 项目级全局配置(一处配置,全局生效) */
10
+ export interface GlobalConfig {
11
+ /** 缓存存储位置,影响 ProTable 列配置缓存与 ProEnum 枚举缓存,默认由各组件自身决定 */
12
+ storage?: StorageType;
13
+ }
14
+ /** ProTable 组件全局默认配置,通过 ProConfigProvider 统一设置,无需每个实例单独声明 */
15
+ export interface ProTableGlobalConfig {
16
+ /** 是否开启可伸缩列功能,默认 false */
17
+ resizeColumn?: boolean;
18
+ /** 是否开启列配置功能,开启时默认同步开启缓存,默认 false */
19
+ columnConfig?: boolean | {
20
+ onColumnOk?: (catchColumns: any, columns: any[]) => Promise<void>;
21
+ };
22
+ /** 是否开启列配置缓存,单位为天,true 时缓存一天,默认 false */
23
+ cacheTime?: number | boolean;
24
+ }
7
25
  /** 对外配置项类型(value、内部 state 均为此类型,字段均可选) */
8
26
  export interface ProConfigProviderType {
9
27
  locale?: string;
28
+ /** 项目级全局配置:缓存位置等跨组件通用配置 */
29
+ globalConfig?: GlobalConfig;
30
+ /** ProTable 组件全局默认配置 */
31
+ ProTable?: ProTableGlobalConfig;
10
32
  ProEnum?: Partial<ProEnumConfigType>;
11
33
  ProSelect?: Partial<PropSelectType> & Record<string, any>;
12
34
  ProModalSelect?: Partial<ProModalSelectType> & Record<string, any>;
@@ -1,10 +1,9 @@
1
1
  import { Table } from 'antd';
2
2
  import React, { memo, useCallback } from 'react';
3
- import { DndContext } from '@dnd-kit/core';
4
- import { arrayMove, useSortable, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
5
- import { CSS } from '@dnd-kit/utilities';
3
+ import { arrayMove } from '@dnd-kit/sortable';
6
4
  import dragSvg from "../../../assets/drag.svg";
7
5
  import ProIcon from "../../../ProIcon";
6
+ import { SortableTableContext, useDraggableRowState } from "../../../hooks/useDraggableRow";
8
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
8
  const Row = props => {
10
9
  const {
@@ -12,37 +11,25 @@ const Row = props => {
12
11
  children,
13
12
  ...restProps
14
13
  } = props;
15
- const data = useSortable({
14
+ const {
15
+ setNodeRef,
16
+ style,
17
+ listeners,
18
+ attributes
19
+ } = useDraggableRowState({
16
20
  id: props['data-row-key'],
17
21
  disabled
18
22
  });
19
- const {
20
- attributes,
21
- listeners,
22
- setNodeRef,
23
- transform,
24
- transition,
25
- isDragging
26
- } = data;
27
- const style = {
28
- ...props.style,
29
- transform: CSS.Transform.toString(transform && {
30
- ...transform,
31
- scaleY: 1
32
- }),
33
- transition,
34
- ...(isDragging ? {
35
- position: 'relative',
36
- zIndex: 2
37
- } : {})
38
- };
39
23
 
40
24
  // 检查是否是空状态占位行
41
25
  const isPlaceholderRow = restProps.className?.includes('ant-table-placeholder');
42
26
  return /*#__PURE__*/_jsx("tr", {
43
27
  ...restProps,
44
28
  ref: setNodeRef,
45
- style: style,
29
+ style: {
30
+ ...props.style,
31
+ ...style
32
+ },
46
33
  ...attributes,
47
34
  children: React.Children.map(children, (child, index) => {
48
35
  // 在第一列(通常是rowSelection列)显示拖拽图标
@@ -101,14 +88,13 @@ const DndWrapper = ({
101
88
  onChange(nextValue);
102
89
  }
103
90
  };
104
- return draggable && !disabled ? /*#__PURE__*/_jsx(DndContext, {
91
+ return /*#__PURE__*/_jsx(SortableTableContext, {
92
+ draggable: draggable,
93
+ disabled: disabled,
94
+ items: value?.[0]?.rowKey ? value.map(i => i.rowKey) : [],
105
95
  onDragEnd: onDragEndtInside,
106
- children: /*#__PURE__*/_jsx(SortableContext, {
107
- items: value?.[0]?.rowKey ? value.map(i => i.rowKey) : [],
108
- strategy: verticalListSortingStrategy,
109
- children: children
110
- })
111
- }) : children;
96
+ children: children
97
+ });
112
98
  };
113
99
  const DraggableTable = ({
114
100
  tableProps,
@@ -69,6 +69,12 @@ const ProEditTable = ({
69
69
  disabled = formFieldProps?.disabled || disabled; // formFieldProps?.disabled可能是函数??
70
70
  }
71
71
  const tableRef = useRef(null);
72
+ // shouldCellUpdate 缓存按表格实例隔离,避免 module-level Map 在多实例间交叉污染
73
+ const cellCachesRef = useRef({
74
+ dataSourceRef: new Map(),
75
+ componentRef: new Map(),
76
+ listLength: new Map()
77
+ });
72
78
  // const valueChangeRef = useRef<number>(0);
73
79
  // valueChangeRef.current = 0;
74
80
 
@@ -312,7 +318,7 @@ const ProEditTable = ({
312
318
  return null;
313
319
  };
314
320
  const _columns = useMemo(() => {
315
- return transformColumns(columns, config);
321
+ return transformColumns(columns, config, cellCachesRef.current);
316
322
  }, [disabled, forceUpdate, columns, page, actionProps, editingKeys, config]);
317
323
  const initDataSource = () => {
318
324
  // 检查每一项是否有 rowKey 或通过 rowKey 字段获取的 key
@@ -71,11 +71,6 @@
71
71
  }
72
72
  }
73
73
 
74
- .is-hidden {
75
- position: absolute;
76
- left: -9999px;
77
- }
78
-
79
74
  .pro-edit-table-drag-wrapper {
80
75
  display: flex;
81
76
  align-items: center;
@@ -91,6 +91,8 @@ export const actions = {
91
91
  // 防止value引用不变, 值不更新 - (自定义组件)
92
92
  const values = form.getFieldsValue();
93
93
  _set(values, rowName, record);
94
+ // 同步清理虚拟路径上的 _addFlag,避免再次编辑→取消时把已保存行误删
95
+ _set(values, virtualRowName, record);
94
96
  form.setFieldsValue(values);
95
97
  setState({
96
98
  editingKeys: nextEditingKeys
@@ -1,3 +1,17 @@
1
+ /**
2
+ * shouldCellUpdate 缓存容器:跟踪 dataSource/component 引用与 filterInList 列表长度。
3
+ * 调用方(ProEditTable 顶层)通过 useRef 持有按实例隔离的容器,避免 module-level Map
4
+ * 在多实例场景下交叉污染;实例卸载后缓存随宿主一起释放。
5
+ * 未传 caches 时退化为单次内联 Map(不缓存历史,shouldCellUpdate 始终首次比较)。
6
+ */
7
+ export interface CellCacheRefs {
8
+ /** 跟踪 fieldProps.dataSource 引用变化 */
9
+ dataSourceRef: Map<string, any>;
10
+ /** 跟踪 column.component 函数引用变化(外部 list 异步加载时闭包刷新) */
11
+ componentRef: Map<string, any>;
12
+ /** 跟踪 filterInList 列长度变化(行增删导致索引偏移) */
13
+ listLength: Map<string, number>;
14
+ }
1
15
  /**
2
16
  * 编辑模式分为 多行和单行两种
3
17
  * 单行模式下,render 分为component和viewRender两种,对应编辑和非编辑状态下,edit状态不对外
@@ -6,4 +20,4 @@
6
20
  * @param columns 表格配置数据
7
21
  * @param config
8
22
  */
9
- export declare const transformColumns: (columns: any[], config: any) => any[];
23
+ export declare const transformColumns: (columns: any[], config: any, caches?: CellCacheRefs) => any[];
@@ -14,13 +14,14 @@ import { filterInternalFields } from "../../ProForm/utils";
14
14
  import ProTooltip from "../../ProTooltip";
15
15
  import { RenderField, ActionButton } from "../components";
16
16
  import locale from "../../locale";
17
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
- const columnDataSourceRefCache = new Map();
19
- /** 追踪 component 函数引用,用于检测外部状态(如异步 list)变化导致的闭包更新 */
20
- const columnComponentRefCache = new Map();
21
- /** 追踪 filterInList 列表长度,用于检测行增删导致的索引偏移 */
22
- const columnListLengthCache = new Map();
23
17
 
18
+ /**
19
+ * shouldCellUpdate 缓存容器:跟踪 dataSource/component 引用与 filterInList 列表长度。
20
+ * 调用方(ProEditTable 顶层)通过 useRef 持有按实例隔离的容器,避免 module-level Map
21
+ * 在多实例场景下交叉污染;实例卸载后缓存随宿主一起释放。
22
+ * 未传 caches 时退化为单次内联 Map(不缓存历史,shouldCellUpdate 始终首次比较)。
23
+ */
24
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
24
25
  // 渲染操作栏
25
26
  const getActionColumn = config => {
26
27
  const {
@@ -180,7 +181,7 @@ const getActionColumn = config => {
180
181
  * @param config
181
182
  */
182
183
 
183
- export const transformColumns = (columns = [], config) => {
184
+ export const transformColumns = (columns = [], config, caches) => {
184
185
  const {
185
186
  mode,
186
187
  form,
@@ -193,6 +194,13 @@ export const transformColumns = (columns = [], config) => {
193
194
  setState,
194
195
  page
195
196
  } = config;
197
+
198
+ // 未传 caches 时退化为单次内联 Map(每次 transform 新建,不持久化)
199
+ const cellCaches = caches ?? {
200
+ dataSourceRef: new Map(),
201
+ componentRef: new Map(),
202
+ listLength: new Map()
203
+ };
196
204
  let nextColumns = columns.map(item => cloneDeepFilterNode(item));
197
205
  const pageNum = tools.calc(page.pageNum, '-', 1);
198
206
  const firstIndex = tools.calc(pageNum, '*', page.pageSize);
@@ -261,9 +269,13 @@ export const transformColumns = (columns = [], config) => {
261
269
  required = false;
262
270
  }
263
271
 
264
- // 样式处理
272
+ // 列隐藏使用 antd 原生 column.hidden(antd 5.13+ 支持),不再依赖 is-hidden 错位定位 hack
273
+ if (hidden) {
274
+ item.hidden = true;
275
+ }
276
+
277
+ // 样式处理(不再写 is-hidden className)
265
278
  item.className = classnames({
266
- 'is-hidden': hidden,
267
279
  'is-required': required || labelRequired,
268
280
  'is-required-left': requiredAlign === 'left',
269
281
  'is-required-right': requiredAlign === 'right',
@@ -338,10 +350,10 @@ export const transformColumns = (columns = [], config) => {
338
350
  // cloneDeepFilterNode 每次产生新引用,在 shouldCellUpdate 内更新 cache 只会让第一行触发更新,其余行跳过
339
351
  const originalFieldProps = columns[index]?.fieldProps;
340
352
  const originalDataSourceRef = !_isFunction(originalFieldProps) ? originalFieldProps?.dataSource : undefined;
341
- const prevDataSourceRef = columnDataSourceRefCache.get(columnCacheKey);
353
+ const prevDataSourceRef = cellCaches.dataSourceRef.get(columnCacheKey);
342
354
  const dataSourceChanged = originalDataSourceRef !== undefined && prevDataSourceRef !== originalDataSourceRef;
343
355
  if (dataSourceChanged) {
344
- columnDataSourceRefCache.set(columnCacheKey, originalDataSourceRef);
356
+ cellCaches.dataSourceRef.set(columnCacheKey, originalDataSourceRef);
345
357
  }
346
358
 
347
359
  // Fix 问题2:检测 filterInList 列的行增删,避免行索引移位时 shouldUpdate 闭包中 index 失效
@@ -349,10 +361,10 @@ export const transformColumns = (columns = [], config) => {
349
361
  let filterListChanged = false;
350
362
  if (filterInListEnabled) {
351
363
  const currentListLen = config.tableLength ?? 0;
352
- const prevListLen = columnListLengthCache.get(columnCacheKey) ?? -1;
364
+ const prevListLen = cellCaches.listLength.get(columnCacheKey) ?? -1;
353
365
  if (prevListLen !== currentListLen) {
354
366
  filterListChanged = true;
355
- columnListLengthCache.set(columnCacheKey, currentListLen);
367
+ cellCaches.listLength.set(columnCacheKey, currentListLen);
356
368
  }
357
369
  }
358
370
  item.shouldCellUpdate = (record, prevRecord) => {
@@ -379,10 +391,10 @@ export const transformColumns = (columns = [], config) => {
379
391
  if (hasComponent) {
380
392
  // component 函数引用变化说明外部依赖(如异步 list)已更新,必须重渲染
381
393
  // 即使行数据未变,旧闭包仍持有过时的外部状态
382
- const prevComponentRef = columnComponentRefCache.get(columnCacheKey);
394
+ const prevComponentRef = cellCaches.componentRef.get(columnCacheKey);
383
395
  const currentComponentRef = item.component;
384
396
  if (prevComponentRef !== currentComponentRef) {
385
- columnComponentRefCache.set(columnCacheKey, currentComponentRef);
397
+ cellCaches.componentRef.set(columnCacheKey, currentComponentRef);
386
398
  return true;
387
399
  }
388
400
  // 同引用时 isEqual 恒为 true,无法感知原地修改,必须重渲染
@@ -75,19 +75,19 @@ export declare const useFormItemProps: (column: FlexibleGroupColumnType, context
75
75
  confirm?: boolean | import("antd").ModalFuncProps | import("../../../render/propsType").FunctionArgs<any, boolean | import("antd").ModalFuncProps>;
76
76
  show?: boolean | ReactiveFunction<any, boolean>;
77
77
  component?: React.ReactNode | ReactiveFunction<any, React.ReactNode>;
78
- vertical?: boolean;
79
- isView?: boolean;
80
- hidden?: boolean;
81
- className?: string;
82
- trim?: boolean;
83
- normalize?: (value: any, prevValue: any, allValues: import("@rc-component/form/lib/interface").Store) => any;
84
- children?: React.ReactNode | ((form: FormInstance<any>) => React.ReactNode);
85
78
  prefixCls?: string;
86
- trigger?: string;
79
+ htmlFor?: string;
80
+ className?: string;
81
+ hidden?: boolean;
87
82
  id?: string;
88
83
  style?: React.CSSProperties;
89
- htmlFor?: string;
84
+ children?: React.ReactNode | ((form: FormInstance<any>) => React.ReactNode);
90
85
  onReset?: () => void;
86
+ trim?: boolean;
87
+ normalize?: (value: any, prevValue: any, allValues: import("@rc-component/form/lib/interface").Store) => any;
88
+ vertical?: boolean;
89
+ isView?: boolean;
90
+ trigger?: string;
91
91
  toISOString?: boolean;
92
92
  toCSTString?: boolean;
93
93
  switchValue?: [any, any];
@@ -1,12 +1,22 @@
1
1
  import { FormInstance } from 'antd/es/form/Form';
2
2
  import { NamePath } from 'antd/es/form/interface';
3
- export declare function toArray<T>(value?: T | T[] | null): T[];
3
+ /**
4
+ * selector 函数:接收所有表单值,返回派生值
5
+ * 与 antd 5.12.0+ Form.useWatch selector 行为一致
6
+ */
7
+ export type WatchSelector<T = any> = (values: any) => T;
4
8
  /**
5
9
  * ProForm的useWatch hook,用于监听表单字段变化
6
- * @param dependencies 监听的字段路径
10
+ * 支持三种依赖形式:
11
+ * 1. 字符串路径:useWatch('fieldName', form)
12
+ * 2. 路径数组:useWatch(['a', 'b'], form) 或 useWatch([['a', 'b'], 'c'], form)
13
+ * 3. selector 函数:useWatch((values) => values.a + values.b, form)
14
+ *
15
+ * @param dependencies 监听的字段路径或 selector 函数
7
16
  * @param form 表单实例
8
17
  * @param wait 防抖等待时间
9
- * @returns 表单值
18
+ * @returns 表单值或 selector 返回的派生值
10
19
  */
11
- declare function useWatch(dependencies: string | NamePath[], form?: FormInstance, wait?: number): any;
20
+ declare function useWatch<T = any>(selector: WatchSelector<T>, form?: FormInstance, wait?: number): T;
21
+ declare function useWatch(dependencies: string | NamePath[], form?: FormInstance, wait?: number): Record<string, any>;
12
22
  export default useWatch;
@@ -3,12 +3,7 @@ import warning from 'rc-util/lib/warning';
3
3
  import { useState, useEffect, useRef, useMemo } from 'react';
4
4
  import isEqual from 'lodash/isEqual';
5
5
  import cloneDeep from 'lodash/cloneDeep';
6
- export function toArray(value) {
7
- if (value === undefined || value === null) {
8
- return [];
9
- }
10
- return Array.isArray(value) ? value : [value];
11
- }
6
+ import { toArray } from "./index";
12
7
 
13
8
  // 从一个对象或Form值中获取特定路径的值
14
9
  function get(source, path) {
@@ -53,37 +48,58 @@ function setValueByPath(obj, path, value) {
53
48
  }
54
49
 
55
50
  // 创建之后namepath就不可变的警告
51
+ // selector 模式(dependencies 为函数)下跳过:函数引用每次渲染不同,无法稳定 stringify
56
52
  const useWatchWarning = process.env.NODE_ENV !== 'production' ? dependencies => {
57
- const depsStr = JSON.stringify(dependencies);
53
+ const isSelector = typeof dependencies === 'function';
54
+ const depsStr = isSelector ? '__selector__' : JSON.stringify(dependencies);
58
55
  const depsStrRef = useRef(depsStr);
59
- warning(depsStrRef.current === depsStr, '`useWatch` is not support dynamic `namePath`. Please provide static instead.');
56
+ if (!isSelector) {
57
+ warning(depsStrRef.current === depsStr, '`useWatch` is not support dynamic `namePath`. Please provide static instead.');
58
+ }
60
59
  } : () => {};
61
60
 
61
+ /**
62
+ * selector 函数:接收所有表单值,返回派生值
63
+ * 与 antd 5.12.0+ Form.useWatch selector 行为一致
64
+ */
65
+
62
66
  /**
63
67
  * ProForm的useWatch hook,用于监听表单字段变化
64
- * @param dependencies 监听的字段路径
68
+ * 支持三种依赖形式:
69
+ * 1. 字符串路径:useWatch('fieldName', form)
70
+ * 2. 路径数组:useWatch(['a', 'b'], form) 或 useWatch([['a', 'b'], 'c'], form)
71
+ * 3. selector 函数:useWatch((values) => values.a + values.b, form)
72
+ *
73
+ * @param dependencies 监听的字段路径或 selector 函数
65
74
  * @param form 表单实例
66
75
  * @param wait 防抖等待时间
67
- * @returns 表单值
76
+ * @returns 表单值或 selector 返回的派生值
68
77
  */
78
+
69
79
  function useWatch(dependencies, form, wait) {
70
- // 处理开发环境警告
71
- useWatchWarning(dependencies);
80
+ const isSelector = typeof dependencies === 'function';
81
+
82
+ // selector 模式跳过依赖字符串化警告(函数引用每次都不同,无法稳定比较)
83
+ if (!isSelector) {
84
+ useWatchWarning(dependencies);
85
+ }
72
86
 
73
87
  // 获取表单实例
74
88
  const formInstance = form;
75
89
 
76
- // 用于存储最终结果
77
- const [state, setState] = useState({});
90
+ // 用于存储最终结果(selector 模式下值类型由 selector 决定)
91
+ const [state, setState] = useState(isSelector ? undefined : {});
78
92
 
79
93
  // 使用ref存储中间状态,避免不必要的渲染
80
- const valueRef = useRef({});
81
- const prevStateRef = useRef({});
94
+ const valueRef = useRef(isSelector ? undefined : {});
95
+ const prevStateRef = useRef(isSelector ? undefined : {});
82
96
 
83
- // 标准化依赖为路径数组的数组
97
+ // 标准化依赖为路径数组的数组(selector 模式下为空数组,不参与)
84
98
  const namePaths = useMemo(() => {
85
- // 将依赖标准化为数组的数组形式
86
99
  const paths = [];
100
+ if (isSelector) {
101
+ return paths;
102
+ }
87
103
 
88
104
  // 字符串形式: 'fieldName'
89
105
  if (typeof dependencies === 'string') {
@@ -107,6 +123,12 @@ function useWatch(dependencies, form, wait) {
107
123
  /* dependencies 故意忽略,与原实现一致 */
108
124
  ]);
109
125
 
126
+ // selector 函数 ref:使最新 selector 始终被调用,但不触发 useEffect 重订阅
127
+ const selectorRef = useRef(null);
128
+ if (isSelector) {
129
+ selectorRef.current = dependencies;
130
+ }
131
+
110
132
  // 保存防抖函数引用,避免重复创建
111
133
  const debouncedFn = useMemo(() => {
112
134
  if (wait) {
@@ -133,6 +155,14 @@ function useWatch(dependencies, form, wait) {
133
155
  // 创建更新状态的函数
134
156
  const updateState = () => {
135
157
  const values = formInstance.getFieldsValue(true);
158
+
159
+ // selector 模式:直接调用 selector 计算派生值
160
+ if (isSelector && selectorRef.current) {
161
+ const next = selectorRef.current(values);
162
+ valueRef.current = next;
163
+ debouncedFn(next);
164
+ return;
165
+ }
136
166
  const newState = {};
137
167
 
138
168
  // 处理每个监听路径
@@ -5,18 +5,18 @@ import * as componentMap from "../../../ProForm/components";
5
5
  import EditIcon from "./EditIcon";
6
6
  import styles from "./index.less";
7
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
- const EditableCell = ({
8
+ const InternalCell = ({
9
9
  value,
10
10
  record,
11
11
  index,
12
- dataIndex,
13
12
  editConfig,
14
- displayContent
13
+ displayContent,
14
+ form,
15
+ fieldName,
16
+ wrapInForm
15
17
  }) => {
16
18
  const [editing, setEditing] = useState(false);
17
- const [form] = Form.useForm();
18
19
  const snapshotRef = useRef(value);
19
- const fieldName = Array.isArray(dataIndex) ? dataIndex.join('_') : dataIndex;
20
20
  const {
21
21
  type = 'Input',
22
22
  fieldProps = {},
@@ -31,15 +31,13 @@ const EditableCell = ({
31
31
  const Component = componentMap[type] ?? componentMap.Input;
32
32
  const handleEditIconClick = () => {
33
33
  snapshotRef.current = value;
34
- form.setFieldsValue({
35
- [fieldName]: value
36
- });
34
+ form.setFieldValue(fieldName, value);
37
35
  setEditing(true);
38
36
  };
39
37
  const handleBlur = async () => {
40
38
  try {
41
- const values = await form.validateFields();
42
- const newValue = values[fieldName];
39
+ await form.validateFields([fieldName]);
40
+ const newValue = form.getFieldValue(fieldName);
43
41
  if (_isEqual(newValue, snapshotRef.current)) {
44
42
  setEditing(false);
45
43
  return;
@@ -67,21 +65,56 @@ const EditableCell = ({
67
65
  })]
68
66
  });
69
67
  }
68
+ const formItem = /*#__PURE__*/_jsx(Form.Item, {
69
+ name: fieldName,
70
+ rules: rules,
71
+ required: required,
72
+ noStyle: true,
73
+ children: /*#__PURE__*/_jsx(Component, {
74
+ ...restFieldProps,
75
+ valueType: valueType,
76
+ autoFocus: true,
77
+ onBlur: handleBlur
78
+ })
79
+ });
80
+ if (!wrapInForm) {
81
+ return formItem;
82
+ }
70
83
  return /*#__PURE__*/_jsx(Form, {
71
84
  form: form,
72
85
  component: false,
73
- children: /*#__PURE__*/_jsx(Form.Item, {
74
- name: fieldName,
75
- rules: rules,
76
- required: required,
77
- noStyle: true,
78
- children: /*#__PURE__*/_jsx(Component, {
79
- ...restFieldProps,
80
- valueType: valueType,
81
- autoFocus: true,
82
- onBlur: handleBlur
83
- })
84
- })
86
+ children: formItem
87
+ });
88
+ };
89
+ const PerCellEditableCell = props => {
90
+ const [form] = Form.useForm();
91
+ const fieldName = Array.isArray(props.dataIndex) ? props.dataIndex.join('_') : props.dataIndex;
92
+ return /*#__PURE__*/_jsx(InternalCell, {
93
+ ...props,
94
+ form: form,
95
+ fieldName: fieldName,
96
+ wrapInForm: true
97
+ });
98
+ };
99
+ const SharedFormEditableCell = props => {
100
+ const fieldName = props.sharedFieldName ?? (Array.isArray(props.dataIndex) ? props.dataIndex.join('_') : props.dataIndex);
101
+ return /*#__PURE__*/_jsx(InternalCell, {
102
+ ...props,
103
+ form: props.sharedForm,
104
+ fieldName: fieldName,
105
+ wrapInForm: false
106
+ });
107
+ };
108
+ const EditableCell = props => {
109
+ // editMode 默认 'per-cell-form',与重构前完全一致
110
+ // 'shared-form':外层 ProTable 提供共享 Form,跳过内部 useForm
111
+ if (props.editMode === 'shared-form' && props.sharedForm) {
112
+ return /*#__PURE__*/_jsx(SharedFormEditableCell, {
113
+ ...props
114
+ });
115
+ }
116
+ return /*#__PURE__*/_jsx(PerCellEditableCell, {
117
+ ...props
85
118
  });
86
119
  };
87
120
  export default EditableCell;
@@ -1,5 +1,8 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { FormInstance } from 'antd/es/form/Form';
3
+ import type { NamePath } from 'antd/es/form/interface';
2
4
  import type { EditTableCellConfig } from '../../propsType';
5
+ export type EditableCellEditMode = 'per-cell-form' | 'shared-form';
3
6
  export interface EditableCellProps {
4
7
  value: any;
5
8
  record: any;
@@ -7,4 +10,15 @@ export interface EditableCellProps {
7
10
  dataIndex: string | string[];
8
11
  editConfig: EditTableCellConfig;
9
12
  displayContent: ReactNode;
13
+ /**
14
+ * 编辑态 Form 模式
15
+ * @default 'per-cell-form'
16
+ * - 'per-cell-form':每个 cell 独立 Form 实例(默认,与重构前一致)
17
+ * - 'shared-form':使用外层 ProTable 顶层共享 Form(性能优化,需配合 ProTable.editMode='shared-form')
18
+ */
19
+ editMode?: EditableCellEditMode;
20
+ /** shared-form 模式下外层共享的 Form 实例 */
21
+ sharedForm?: FormInstance;
22
+ /** shared-form 模式下字段路径(如 ['__editable__', rowKey, dataIndex]) */
23
+ sharedFieldName?: NamePath;
10
24
  }
@@ -3,16 +3,18 @@
3
3
  * 重构优化:提取公共逻辑,修复 key prop 警告
4
4
  */
5
5
  import React from 'react';
6
+ import type { FormInstance } from 'antd/es/form/Form';
6
7
  import type { ProTableColumnType } from '../../propsType';
7
- export declare const formatColumn: ({ column, originalObj, rowKey, wrapToolTipProps, scroll, onUpdateMinWidth, isInNewRow, diffConfig, }: {
8
+ export declare const formatColumn: ({ column, originalObj, rowKey, wrapToolTipProps, scroll, isInNewRow, diffConfig, editMode, sharedForm, }: {
8
9
  column: ProTableColumnType;
9
10
  originalObj: any;
10
11
  rowKey: any;
11
12
  wrapToolTipProps?: any;
12
13
  scroll?: any;
13
- onUpdateMinWidth?: any;
14
14
  isInNewRow?: any;
15
15
  diffConfig: any;
16
+ editMode?: 'per-cell-form' | 'shared-form';
17
+ sharedForm?: FormInstance;
16
18
  }) => void;
17
19
  /**
18
20
  * 查找最近的父级className