@zat-design/sisyphus-react 4.5.8-beta.6 → 4.5.9-beta.1
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.
- package/dist/index.esm.css +1 -1
- package/dist/less.esm.css +1 -1
- package/es/ProEditTable/components/RenderField/index.js +74 -62
- package/es/ProEditTable/index.js +66 -36
- package/es/ProEditTable/propsType.d.ts +4 -4
- package/es/ProEditTable/utils/useShouldUpdateForTable.d.ts +4 -0
- package/es/ProEditTable/utils/useShouldUpdateForTable.js +16 -4
- package/es/ProForm/components/combination/ProModalSelect/hooks/useRequestList.js +4 -2
- package/es/ProLayout/components/TabsManager/components/TabsHeader.js +1 -0
- package/es/ProLayout/components/TabsManager/style/index.less +1 -2
- package/es/ProLayout/index.js +22 -2
- package/es/ProLayout/style/index.less +2 -3
- package/es/ProTable/components/EditableCell/EditIcon.js +5 -8
- package/es/ProTable/components/EditableCell/index.js +3 -2
- package/es/ProTable/style/index.less +26 -0
- package/es/ProTreeModal/components/Trigger.js +18 -14
- package/es/ProTreeModal/index.js +199 -187
- package/es/ProTreeModal/style/index.less +25 -1
- package/package.json +1 -1
- package/es/ProTable/components/EditableCell/index.less +0 -29
- package/es/assets/edit.svg +0 -1
|
@@ -12,7 +12,7 @@ import classNames from 'classnames';
|
|
|
12
12
|
import { compatStartTransition } from "../../../utils";
|
|
13
13
|
import valueTypeMap from "../../../ProForm/utils/valueType";
|
|
14
14
|
import transformMap from "../../utils/transform";
|
|
15
|
-
import { getNamePath, difference, getDisabled, resolveChangedFields, syncGroupCombinedValues } from "../../utils/tools";
|
|
15
|
+
import { getNamePath, difference, getDisabled, isRecordShallowEqual, resolveChangedFields, syncGroupCombinedValues } from "../../utils/tools";
|
|
16
16
|
import * as componentMap from "../../../ProForm/components";
|
|
17
17
|
import { useProConfig } from "../../../ProConfigProvider";
|
|
18
18
|
import Container from "../../../ProForm/components/Container";
|
|
@@ -26,7 +26,7 @@ import { getDefaultProps } from "../../utils/getDefaultProps";
|
|
|
26
26
|
import ListChangedWrapper from "./ListChangedWrapper";
|
|
27
27
|
import useShouldUpdateForTable from "../../utils/useShouldUpdateForTable";
|
|
28
28
|
import { OMIT_FORM_ITEM_AND_DOM_KEYS, arePropsEqual, hasRowFunctionDependency, mergeOnFieldChangeRow } from "./tools";
|
|
29
|
-
import {
|
|
29
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
30
30
|
const RenderField = ({
|
|
31
31
|
text: value,
|
|
32
32
|
record,
|
|
@@ -90,6 +90,8 @@ const RenderField = ({
|
|
|
90
90
|
|
|
91
91
|
// type类型 首字母转大写
|
|
92
92
|
type = type?.replace?.(type[0], type[0].toUpperCase()) || 'Input';
|
|
93
|
+
const fallbackType = type;
|
|
94
|
+
const hasCustomComponent = 'component' in column || 'editRender' in column;
|
|
93
95
|
|
|
94
96
|
// 自定义组件时 防止默认值影像后续判断
|
|
95
97
|
if ('component' in column) {
|
|
@@ -104,6 +106,11 @@ const RenderField = ({
|
|
|
104
106
|
const namePath = useMemo(() => getNamePath(name, virtualKey), [namePathKey, name, virtualKey]);
|
|
105
107
|
const rowNamePath = useMemo(() => [...namePath, index], [namePath, index]);
|
|
106
108
|
const rowData = form.getFieldValue(rowNamePath) || record || {};
|
|
109
|
+
|
|
110
|
+
// Form.Item shouldUpdate 可能在 RenderField 外层未重渲染时多次执行 render props。
|
|
111
|
+
// 缓存本次外层渲染期间的最新行计算结果,避免同一行版本重复调用动态配置函数。
|
|
112
|
+
const shouldUpdateStateCacheRef = useRef(undefined);
|
|
113
|
+
shouldUpdateStateCacheRef.current = undefined;
|
|
107
114
|
let currentValue = dataIndex ? rowData?.[dataIndex] : null;
|
|
108
115
|
|
|
109
116
|
// 构建新的参数格式(与 ProForm 保持一致)
|
|
@@ -171,7 +178,7 @@ const RenderField = ({
|
|
|
171
178
|
|
|
172
179
|
// component 处理 - 优先使用 hook 返回的值
|
|
173
180
|
if (_isFunction(lastComponent)) {
|
|
174
|
-
lastComponent = dynamicProps.component
|
|
181
|
+
lastComponent = dynamicProps.componentResolved ? dynamicProps.component : lastComponent(rowData, reactiveParams);
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
// 更新 lastEditRender 为处理后的 lastComponent
|
|
@@ -193,6 +200,23 @@ const RenderField = ({
|
|
|
193
200
|
|
|
194
201
|
// 多态:可能是内置组件类型、自定义 component,或已渲染的 ReactElement(lastEditRender),保留显式 any
|
|
195
202
|
let TargetComponent;
|
|
203
|
+
const fallbackTargetComponent = typeof fallbackType === 'string' ? componentMap[fallbackType] : undefined;
|
|
204
|
+
const invalidComponentWarnedRef = useRef(false);
|
|
205
|
+
const resolveEditTargetComponent = candidate => {
|
|
206
|
+
if ( /*#__PURE__*/React.isValidElement(candidate)) {
|
|
207
|
+
invalidComponentWarnedRef.current = false;
|
|
208
|
+
return candidate;
|
|
209
|
+
}
|
|
210
|
+
if (hasCustomComponent && !invalidComponentWarnedRef.current && process.env.NODE_ENV !== 'production') {
|
|
211
|
+
const columnName = dataIndex ?? column?.name ?? title ?? originTitle ?? 'unknown';
|
|
212
|
+
console.warn('[ProEditTable] component 必须返回合法的 ReactElement,已回退到内置组件。', {
|
|
213
|
+
column: Array.isArray(columnName) ? columnName.join('.') : String(columnName),
|
|
214
|
+
received: candidate
|
|
215
|
+
});
|
|
216
|
+
invalidComponentWarnedRef.current = true;
|
|
217
|
+
}
|
|
218
|
+
return fallbackTargetComponent;
|
|
219
|
+
};
|
|
196
220
|
|
|
197
221
|
// 将Hooks调用移到组件顶层
|
|
198
222
|
const proConfig = useProConfig();
|
|
@@ -315,20 +339,9 @@ const RenderField = ({
|
|
|
315
339
|
...valueTypeTransform()
|
|
316
340
|
}, form, names, namesStr, type);
|
|
317
341
|
}, [names, baseName, index, dataIndex, lastFormItemProps, type]);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
TargetComponent =
|
|
321
|
-
}
|
|
322
|
-
if (isEditable && isEditing) {
|
|
323
|
-
// lastEditRender 已经是处理后的值(通过 dynamicProps.component 或直接计算)
|
|
324
|
-
// 如果原本是函数,此时 lastEditRender 已经是执行后的 ReactNode
|
|
325
|
-
// 如果原本是 ReactElement,lastEditRender 就是 ReactElement
|
|
326
|
-
if ( /*#__PURE__*/React.isValidElement(lastEditRender)) {
|
|
327
|
-
TargetComponent = lastEditRender;
|
|
328
|
-
} else if (lastEditRender) {
|
|
329
|
-
// 其他情况(可能是字符串或其他类型的 ReactNode)
|
|
330
|
-
TargetComponent = lastEditRender;
|
|
331
|
-
}
|
|
342
|
+
TargetComponent = fallbackTargetComponent;
|
|
343
|
+
if (isEditable && isEditing && hasCustomComponent) {
|
|
344
|
+
TargetComponent = resolveEditTargetComponent(lastEditRender);
|
|
332
345
|
}
|
|
333
346
|
|
|
334
347
|
// 查看模式
|
|
@@ -663,45 +676,48 @@ const RenderField = ({
|
|
|
663
676
|
namePath: rowNamePath
|
|
664
677
|
};
|
|
665
678
|
const latestRowParams = [latestRowData, latestReactiveParams];
|
|
679
|
+
const cachedState = shouldUpdateStateCacheRef.current;
|
|
680
|
+
if (cachedState && isRecordShallowEqual(cachedState.rowSnapshot, latestRowData)) {
|
|
681
|
+
return cachedState.result;
|
|
682
|
+
}
|
|
666
683
|
|
|
667
|
-
//
|
|
668
|
-
|
|
669
|
-
|
|
684
|
+
// 初次 render props 会与外层 RenderField 使用同一行数据,直接复用 hook 已计算结果。
|
|
685
|
+
const canReuseCurrentState = isRecordShallowEqual(latestRowData, dynamicProps.rowSnapshot);
|
|
686
|
+
let latestFieldProps = canReuseCurrentState ? lastFieldProps : fieldProps || {};
|
|
687
|
+
if (!canReuseCurrentState && _isFunction(fieldProps)) {
|
|
670
688
|
latestFieldProps = fieldProps(latestRowData, latestReactiveParams);
|
|
671
689
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
let latestRequired = required;
|
|
675
|
-
if (_isFunction(required)) {
|
|
690
|
+
let latestRequired = canReuseCurrentState ? lastRequired : required;
|
|
691
|
+
if (!canReuseCurrentState && _isFunction(required)) {
|
|
676
692
|
latestRequired = required(latestRowData, latestReactiveParams);
|
|
677
693
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
let latestRules = rules;
|
|
681
|
-
if (_isFunction(rules)) {
|
|
694
|
+
let latestRules = canReuseCurrentState ? lastRules : rules;
|
|
695
|
+
if (!canReuseCurrentState && _isFunction(rules)) {
|
|
682
696
|
latestRules = rules(latestRowData, latestReactiveParams);
|
|
683
697
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
let latestDesensitization = desensitization || [];
|
|
687
|
-
if (_isFunction(desensitization)) {
|
|
698
|
+
let latestDesensitization = canReuseCurrentState ? lastDesensitization : desensitization || [];
|
|
699
|
+
if (!canReuseCurrentState && _isFunction(desensitization)) {
|
|
688
700
|
latestDesensitization = desensitization(latestRowData, latestReactiveParams);
|
|
689
701
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
let latestComponent = component || editRender;
|
|
693
|
-
if (_isFunction(latestComponent)) {
|
|
702
|
+
let latestComponent = canReuseCurrentState ? lastComponent : component || editRender;
|
|
703
|
+
if (!canReuseCurrentState && _isFunction(latestComponent)) {
|
|
694
704
|
latestComponent = latestComponent(latestRowData, latestReactiveParams);
|
|
695
705
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
let latestIsEditable = column.isEditable ?? true;
|
|
699
|
-
if (_isFunction(column.isEditable)) {
|
|
706
|
+
let latestIsEditable = canReuseCurrentState ? isEditable : column.isEditable ?? true;
|
|
707
|
+
if (!canReuseCurrentState && _isFunction(column.isEditable)) {
|
|
700
708
|
latestIsEditable = column.isEditable(latestRowData, latestReactiveParams);
|
|
701
709
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
710
|
+
let latestValueType = canReuseCurrentState ? lastValueType : valueType;
|
|
711
|
+
if (!canReuseCurrentState && _isFunction(valueType)) {
|
|
712
|
+
const latestCurrentValue = dataIndex ? latestRowData?.[dataIndex] : null;
|
|
713
|
+
latestValueType = valueType(latestCurrentValue, latestRowData, {
|
|
714
|
+
index,
|
|
715
|
+
form,
|
|
716
|
+
namePath: rowNamePath,
|
|
717
|
+
name: column?.name
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
const latestDisabled = canReuseCurrentState ? lastDisabled : getDisabled({
|
|
705
721
|
globalControl: otherProps?.globalControl,
|
|
706
722
|
formDisabled: otherProps?.formDisabled,
|
|
707
723
|
column,
|
|
@@ -721,15 +737,7 @@ const RenderField = ({
|
|
|
721
737
|
onChange: handleChange,
|
|
722
738
|
onBlur: handleBlur
|
|
723
739
|
};
|
|
724
|
-
|
|
725
|
-
// ⭐ 关键修改:在 shouldUpdate 模式下,需要重新设置 TargetComponent
|
|
726
|
-
// 因为 component 函数可能返回不同的结果
|
|
727
|
-
let latestTargetComponent = TargetComponent;
|
|
728
|
-
|
|
729
|
-
// 首先检查是否有内置type
|
|
730
|
-
if (!latestComponent && typeof type === 'string') {
|
|
731
|
-
latestTargetComponent = componentMap[type] ?? /*#__PURE__*/_jsx(_Fragment, {});
|
|
732
|
-
}
|
|
740
|
+
let latestTargetComponent = canReuseCurrentState ? TargetComponent : fallbackTargetComponent;
|
|
733
741
|
|
|
734
742
|
// 然后处理自定义 component
|
|
735
743
|
const latestIsView = !latestIsEditable || record?.['is-view'] || config.isView || virtualKey && !isEditing || getDisabled({
|
|
@@ -741,16 +749,12 @@ const RenderField = ({
|
|
|
741
749
|
params: latestRowParams,
|
|
742
750
|
rowDisabled: rowDisabled || 'empty'
|
|
743
751
|
});
|
|
744
|
-
if (latestIsEditable && isEditing) {
|
|
745
|
-
|
|
746
|
-
latestTargetComponent = latestComponent;
|
|
747
|
-
} else if (latestComponent) {
|
|
748
|
-
latestTargetComponent = latestComponent;
|
|
749
|
-
}
|
|
752
|
+
if (!canReuseCurrentState && latestIsEditable && isEditing && hasCustomComponent) {
|
|
753
|
+
latestTargetComponent = resolveEditTargetComponent(latestComponent);
|
|
750
754
|
}
|
|
751
755
|
|
|
752
756
|
// 查看模式
|
|
753
|
-
if (latestIsView) {
|
|
757
|
+
if (!canReuseCurrentState && latestIsView) {
|
|
754
758
|
if (typeof viewRender === 'function') {
|
|
755
759
|
const latestCurrentValue = dataIndex ? latestRowData?.[dataIndex] : value;
|
|
756
760
|
const View = viewRender(latestCurrentValue, latestRowData || {}, options);
|
|
@@ -769,7 +773,8 @@ const RenderField = ({
|
|
|
769
773
|
...latestComponentProps,
|
|
770
774
|
otherProps: {
|
|
771
775
|
...latestComponentProps.otherProps,
|
|
772
|
-
isView: latestIsView
|
|
776
|
+
isView: latestIsView,
|
|
777
|
+
valueType: latestValueType
|
|
773
778
|
}
|
|
774
779
|
};
|
|
775
780
|
|
|
@@ -788,7 +793,7 @@ const RenderField = ({
|
|
|
788
793
|
};
|
|
789
794
|
}
|
|
790
795
|
}
|
|
791
|
-
|
|
796
|
+
const result = {
|
|
792
797
|
finalComponentProps: latestComponentProps,
|
|
793
798
|
targetComponent: latestTargetComponent,
|
|
794
799
|
latestIsView,
|
|
@@ -796,6 +801,13 @@ const RenderField = ({
|
|
|
796
801
|
effectiveRules: latestRules,
|
|
797
802
|
effectiveFieldProps: latestFieldProps
|
|
798
803
|
};
|
|
804
|
+
shouldUpdateStateCacheRef.current = {
|
|
805
|
+
rowSnapshot: {
|
|
806
|
+
...latestRowData
|
|
807
|
+
},
|
|
808
|
+
result
|
|
809
|
+
};
|
|
810
|
+
return result;
|
|
799
811
|
};
|
|
800
812
|
|
|
801
813
|
// 根据 latestIsView 和最新 required/rules 构建正确的 Form.Item props
|
|
@@ -925,7 +937,7 @@ const RenderField = ({
|
|
|
925
937
|
}) : /*#__PURE__*/_jsx(Container, {
|
|
926
938
|
viewEmpty: viewEmpty
|
|
927
939
|
});
|
|
928
|
-
if (type === 'FormList') {
|
|
940
|
+
if (type === 'FormList' && !hasCustomComponent && TargetComponent) {
|
|
929
941
|
FormItem = /*#__PURE__*/_jsx(Form.List, {
|
|
930
942
|
name: formNamePath ? cellName.slice(formNamePath?.length - 1) : cellName,
|
|
931
943
|
..._omit(activeFinalFormItemProps, ['render', 'key', 'width', 'hiddenNames', 'name']),
|
package/es/ProEditTable/index.js
CHANGED
|
@@ -17,8 +17,24 @@ import Empty from "../assets/empty.png";
|
|
|
17
17
|
import locale, { formatMessage } from "../locale";
|
|
18
18
|
import { BaseTable, DraggableTable } from "./components/RcTable";
|
|
19
19
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
|
+
const EMPTY_VALUE = [];
|
|
21
|
+
const EMPTY_ACTION_PROPS = [];
|
|
22
|
+
const EMPTY_TOOLBAR_PROPS = [];
|
|
23
|
+
|
|
24
|
+
/** 保留浅层语义相同对象的引用,避免 rest props 每次分配新对象导致整表重算。 */
|
|
25
|
+
const useShallowStableObject = value => {
|
|
26
|
+
const valueRef = useRef(value);
|
|
27
|
+
const previous = valueRef.current;
|
|
28
|
+
const previousKeys = Object.keys(previous);
|
|
29
|
+
const nextKeys = Object.keys(value);
|
|
30
|
+
const changed = previousKeys.length !== nextKeys.length || nextKeys.some(key => !Object.is(previous[key], value[key]));
|
|
31
|
+
if (changed) {
|
|
32
|
+
valueRef.current = value;
|
|
33
|
+
}
|
|
34
|
+
return valueRef.current;
|
|
35
|
+
};
|
|
20
36
|
const ProEditTable = ({
|
|
21
|
-
value =
|
|
37
|
+
value = EMPTY_VALUE,
|
|
22
38
|
onChange,
|
|
23
39
|
onDrag,
|
|
24
40
|
onDragEnd,
|
|
@@ -32,8 +48,8 @@ const ProEditTable = ({
|
|
|
32
48
|
insertType = 'after',
|
|
33
49
|
emptyBtnText = locale.ProEditTable.clickAdd,
|
|
34
50
|
actionWidth,
|
|
35
|
-
actionProps =
|
|
36
|
-
toolbarProps =
|
|
51
|
+
actionProps = EMPTY_ACTION_PROPS,
|
|
52
|
+
toolbarProps = EMPTY_TOOLBAR_PROPS,
|
|
37
53
|
toolbarSticky,
|
|
38
54
|
rowSelection,
|
|
39
55
|
onlyOneLineMsg = locale.ProEditTable.onlyOneLineMsg,
|
|
@@ -53,13 +69,14 @@ const ProEditTable = ({
|
|
|
53
69
|
diffConfig,
|
|
54
70
|
...resetProps
|
|
55
71
|
}, ref) => {
|
|
72
|
+
const stableResetProps = useShallowStableObject(resetProps);
|
|
56
73
|
// 上下文form
|
|
57
74
|
const contentForm = Form.useFormInstance();
|
|
58
75
|
const formFieldProps = ProForm.useFieldProps() || {};
|
|
59
76
|
const affixRef = useRef(null);
|
|
60
|
-
const form = contentForm || formFieldProps?.form ||
|
|
61
|
-
let name = formFieldProps?.name ||
|
|
62
|
-
const shouldUpdateDebounce = formFieldProps?.shouldUpdateDebounce ??
|
|
77
|
+
const form = contentForm || formFieldProps?.form || stableResetProps?.form;
|
|
78
|
+
let name = formFieldProps?.name || stableResetProps?.name || stableResetProps?.id?.split?.('_');
|
|
79
|
+
const shouldUpdateDebounce = formFieldProps?.shouldUpdateDebounce ?? stableResetProps?.shouldUpdateDebounce ?? 200;
|
|
63
80
|
const {
|
|
64
81
|
isView = false,
|
|
65
82
|
viewEmpty = '-',
|
|
@@ -71,6 +88,9 @@ const ProEditTable = ({
|
|
|
71
88
|
name = [...namePath, ...name.slice(1)];
|
|
72
89
|
disabled = formFieldProps?.disabled || disabled; // formFieldProps?.disabled可能是函数??
|
|
73
90
|
}
|
|
91
|
+
const nameSignature = Array.isArray(name) ? name.join('\u0001') : String(name);
|
|
92
|
+
// ProForm 上下文可能每次渲染都生成新的 namePath 数组,按路径语义稳定引用。
|
|
93
|
+
name = useMemo(() => name, [nameSignature]);
|
|
74
94
|
const tableRef = useRef(null);
|
|
75
95
|
// 虚拟表格错误存储:脱离 antd Field 生命周期持久化真实报错文案,
|
|
76
96
|
// 解决滚动卸载后报错丢失(报错常驻)。按表格实例隔离。
|
|
@@ -95,6 +115,15 @@ const ProEditTable = ({
|
|
|
95
115
|
const requiredAlign = useMemo(() => {
|
|
96
116
|
return themeConfig?.data?.zauiFormAlign ?? configRequiredAlign ?? 'left';
|
|
97
117
|
}, [themeConfig?.data?.zauiFormAlign, configRequiredAlign]);
|
|
118
|
+
const renderOtherProps = useMemo(() => ({
|
|
119
|
+
globalControl: stableResetProps?.otherProps?.globalControl,
|
|
120
|
+
formDisabled: stableResetProps?.otherProps?.formDisabled,
|
|
121
|
+
desensitizationKey: stableResetProps?.otherProps?.desensitizationKey
|
|
122
|
+
}), [stableResetProps?.otherProps?.desensitizationKey, stableResetProps?.otherProps?.formDisabled, stableResetProps?.otherProps?.globalControl]);
|
|
123
|
+
const mergedDiffConfig = useMemo(() => ({
|
|
124
|
+
...stableResetProps?.otherProps?.diffConfig,
|
|
125
|
+
...diffConfig
|
|
126
|
+
}), [diffConfig, stableResetProps?.otherProps?.diffConfig]);
|
|
98
127
|
const [state, setState] = useSetState({
|
|
99
128
|
forceUpdate: {},
|
|
100
129
|
// 表格内部强制刷新开关
|
|
@@ -127,7 +156,7 @@ const ProEditTable = ({
|
|
|
127
156
|
pageSize: paginationConfig?.pageSize ?? page.pageSize
|
|
128
157
|
}), [page.pageNum, page.pageSize, paginationConfig?.current, paginationConfig?.pageSize]);
|
|
129
158
|
const mergedSelectedRowKeys = rowSelection?.selectedRowKeys ?? selectedRowKeys;
|
|
130
|
-
const virtualRowName = getNamePath(_isArray(name) ? name : [name], virtualKey);
|
|
159
|
+
const virtualRowName = useMemo(() => getNamePath(_isArray(name) ? name : [name], virtualKey), [name, virtualKey]);
|
|
131
160
|
|
|
132
161
|
// 样式处理
|
|
133
162
|
const _className = classnames({
|
|
@@ -150,7 +179,7 @@ const ProEditTable = ({
|
|
|
150
179
|
const originalValues = useMemo(() => {
|
|
151
180
|
let nextOriginalValues;
|
|
152
181
|
// 原始值也需要设置对应的rowkey,后续比对通过rowkey对比
|
|
153
|
-
const originalArr = _get(
|
|
182
|
+
const originalArr = _get(stableResetProps?.originalValues, name);
|
|
154
183
|
const isOriginalAllHasKey = originalArr ? originalArr?.every?.(item => item.rowKey) : true;
|
|
155
184
|
const isNeedCoverOriginal = originalArr?.some?.(item => item.rowKey !== getRowKey(item));
|
|
156
185
|
// 初始化默认生成row-key
|
|
@@ -164,14 +193,14 @@ const ProEditTable = ({
|
|
|
164
193
|
_set(nextOriginalValues, name, originalArr);
|
|
165
194
|
}
|
|
166
195
|
return nextOriginalValues;
|
|
167
|
-
}, [
|
|
196
|
+
}, [getRowKey, name, stableResetProps?.originalValues]);
|
|
168
197
|
const getIsNew = useCallback(record => {
|
|
169
198
|
const originalArr = _get(originalValues, name);
|
|
170
199
|
if (!originalArr?.length) {
|
|
171
200
|
return false;
|
|
172
201
|
}
|
|
173
202
|
return !originalArr?.find?.(item => getRowKey(item) === getRowKey(record));
|
|
174
|
-
}, [originalValues, name]);
|
|
203
|
+
}, [getRowKey, originalValues, name]);
|
|
175
204
|
|
|
176
205
|
// 分页变更
|
|
177
206
|
const handlePageChange = useCallback(async (current, pageSize) => {
|
|
@@ -230,27 +259,27 @@ const ProEditTable = ({
|
|
|
230
259
|
prefixCls,
|
|
231
260
|
rowDisabled,
|
|
232
261
|
actionDirection,
|
|
233
|
-
diffConfig:
|
|
234
|
-
...resetProps?.otherProps?.diffConfig,
|
|
235
|
-
...diffConfig
|
|
236
|
-
},
|
|
262
|
+
diffConfig: mergedDiffConfig,
|
|
237
263
|
shouldUpdateDebounce,
|
|
238
264
|
getIsNew,
|
|
239
265
|
handlePageChange,
|
|
240
|
-
|
|
266
|
+
validateTrigger: stableResetProps?.validateTrigger,
|
|
267
|
+
validateMessages: stableResetProps?.validateMessages,
|
|
268
|
+
virtual: stableResetProps?.virtual,
|
|
269
|
+
otherProps: renderOtherProps
|
|
241
270
|
}), [actionDirection, actionProps, actionWidth, columns, deletePoConfirmMsg, diffConfig, editingKeys, emptyBtnText, form,
|
|
242
271
|
// forceUpdate 不应该作为依赖项,因为它是触发刷新的信号,而非用于比较的数据
|
|
243
|
-
getIsNew, handlePageChange, insertType, isView, max, mode, mulDeletePoConfirmMsg, name, namePath, onlyOneLineMsg, originalValues, prefixCls, requiredAlign,
|
|
272
|
+
getIsNew, handlePageChange, insertType, isView, max, mergedDiffConfig, mode, mulDeletePoConfirmMsg, name, namePath, onlyOneLineMsg, originalValues, prefixCls, requiredAlign, renderOtherProps, rowDisabled, mergedSelectedRowKeys, selectedRows, shouldUpdateDebounce, stableResetProps?.validateMessages, stableResetProps?.validateTrigger, stableResetProps?.virtual, tableRef, toolbarProps, value?.length, viewEmpty, virtualKey, resolvedPage]);
|
|
244
273
|
|
|
245
274
|
// 编辑行设置下样式
|
|
246
|
-
const _rowClassName = record => {
|
|
275
|
+
const _rowClassName = useCallback(record => {
|
|
247
276
|
const isEdit = !virtualKey || editingKeys.includes(record.rowKey);
|
|
248
|
-
const
|
|
277
|
+
const rowClassName = isEdit ? 'is-editing' : '';
|
|
249
278
|
if (getIsNew(record)) {
|
|
250
|
-
return `is-new-row ${
|
|
279
|
+
return `is-new-row ${rowClassName}`;
|
|
251
280
|
}
|
|
252
|
-
return
|
|
253
|
-
};
|
|
281
|
+
return rowClassName;
|
|
282
|
+
}, [editingKeys, getIsNew, virtualKey]);
|
|
254
283
|
|
|
255
284
|
// 判断是否禁止添加、批量删除
|
|
256
285
|
const isForbiddenBtn = useCallback(type => {
|
|
@@ -274,7 +303,7 @@ const ProEditTable = ({
|
|
|
274
303
|
const isHideCheckBox = !rowSelection && isForbiddenBtn('mulDelete');
|
|
275
304
|
|
|
276
305
|
// 复选框
|
|
277
|
-
const _rowSelection = {
|
|
306
|
+
const _rowSelection = useMemo(() => ({
|
|
278
307
|
fixed: true,
|
|
279
308
|
columnWidth: 48,
|
|
280
309
|
getCheckboxProps: record => {
|
|
@@ -296,7 +325,7 @@ const ProEditTable = ({
|
|
|
296
325
|
});
|
|
297
326
|
rowSelection?.onChange?.(nextSelectedRowKeys, nextSelectedRows, info);
|
|
298
327
|
}
|
|
299
|
-
};
|
|
328
|
+
}), [isHideCheckBox, mergedSelectedRowKeys, rowDisabled, rowSelection, setState]);
|
|
300
329
|
|
|
301
330
|
// 空列表状态
|
|
302
331
|
const emptyDom = useMemo(() => () => /*#__PURE__*/_jsxs("div", {
|
|
@@ -321,7 +350,7 @@ const ProEditTable = ({
|
|
|
321
350
|
* 渲染表格选择器
|
|
322
351
|
* @returns rowSelection
|
|
323
352
|
*/
|
|
324
|
-
const renderRowSelection = () => {
|
|
353
|
+
const renderRowSelection = useCallback(() => {
|
|
325
354
|
if (disabled || isView) {
|
|
326
355
|
return null;
|
|
327
356
|
}
|
|
@@ -332,10 +361,10 @@ const ProEditTable = ({
|
|
|
332
361
|
return _rowSelection;
|
|
333
362
|
}
|
|
334
363
|
return null;
|
|
335
|
-
};
|
|
364
|
+
}, [_rowSelection, disabled, draggable, isHideCheckBox, isView]);
|
|
336
365
|
const _columns = useMemo(() => {
|
|
337
366
|
return transformColumns(columns, config, cellCachesRef.current);
|
|
338
|
-
}, [
|
|
367
|
+
}, [columns, config, forceUpdate]);
|
|
339
368
|
const initDataSource = () => {
|
|
340
369
|
// 检查每一项是否有 rowKey 或通过 rowKey 字段获取的 key
|
|
341
370
|
const isAllHasKey = value?.every?.(item => item.rowKey || typeof rowKey === 'string' && item[rowKey]);
|
|
@@ -359,7 +388,7 @@ const ProEditTable = ({
|
|
|
359
388
|
useEffect(() => {
|
|
360
389
|
initDataSource();
|
|
361
390
|
}, [value, originalValues]);
|
|
362
|
-
useImperativeHandle(
|
|
391
|
+
useImperativeHandle(stableResetProps?.ref || ref, () => {
|
|
363
392
|
return {
|
|
364
393
|
getInternalState: () => state,
|
|
365
394
|
setInternalState: setState
|
|
@@ -377,11 +406,11 @@ const ProEditTable = ({
|
|
|
377
406
|
}
|
|
378
407
|
}, [toolbarSticky]);
|
|
379
408
|
const _toolbarSticky = _isBoolean(toolbarSticky) ? {} : toolbarSticky;
|
|
380
|
-
const _summary = typeof summary === 'object' ? data => /*#__PURE__*/_jsx(Summary, {
|
|
409
|
+
const _summary = useMemo(() => typeof summary === 'object' ? data => /*#__PURE__*/_jsx(Summary, {
|
|
381
410
|
data: data,
|
|
382
411
|
summary: summary,
|
|
383
412
|
rowSelection: renderRowSelection()
|
|
384
|
-
}) : summary;
|
|
413
|
+
}) : summary, [renderRowSelection, summary]);
|
|
385
414
|
const TableComponent = draggable ? DraggableTable : BaseTable;
|
|
386
415
|
|
|
387
416
|
// 给每个 record 附加编辑状态标识,让 shouldCellUpdate 能检测到状态变化
|
|
@@ -435,17 +464,18 @@ const ProEditTable = ({
|
|
|
435
464
|
}) => rest);
|
|
436
465
|
onChange?.(cleanValue);
|
|
437
466
|
}, [onChange]);
|
|
467
|
+
const draggableProps = useMemo(() => ({
|
|
468
|
+
onChange: draggable ? handleDragChange : onChange,
|
|
469
|
+
onDrag,
|
|
470
|
+
onDragEnd,
|
|
471
|
+
draggable
|
|
472
|
+
}), [draggable, handleDragChange, onChange, onDrag, onDragEnd]);
|
|
438
473
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
439
474
|
children: [/*#__PURE__*/_jsx(ConfigProvider, {
|
|
440
475
|
renderEmpty: value?.length ? undefined : emptyDom,
|
|
441
476
|
children: /*#__PURE__*/_jsx(TableComponent, {
|
|
442
477
|
...resetProps,
|
|
443
|
-
draggableProps:
|
|
444
|
-
onChange: draggable ? handleDragChange : onChange,
|
|
445
|
-
onDrag,
|
|
446
|
-
onDragEnd,
|
|
447
|
-
draggable
|
|
448
|
-
},
|
|
478
|
+
draggableProps: draggableProps,
|
|
449
479
|
tableProps: tableProps
|
|
450
480
|
})
|
|
451
481
|
}), !isView && value?.length ? toolbarSticky ? /*#__PURE__*/_jsx(Affix, {
|
|
@@ -485,7 +515,7 @@ const ProEditTable = ({
|
|
|
485
515
|
virtualKey: virtualKey,
|
|
486
516
|
editingKeys: editingKeys,
|
|
487
517
|
onlyOneLineMsg: onlyOneLineMsg,
|
|
488
|
-
virtual:
|
|
518
|
+
virtual: stableResetProps?.virtual,
|
|
489
519
|
columns: columns,
|
|
490
520
|
config: config,
|
|
491
521
|
errorStore: errorStoreRef.current
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AffixProps, ModalFuncProps } from 'antd';
|
|
2
|
-
import { Key, ReactNode } from 'react';
|
|
2
|
+
import { Key, ReactElement, ReactNode } from 'react';
|
|
3
3
|
import { DragEndEvent } from '@dnd-kit/core';
|
|
4
4
|
import type { ButtonProps } from 'antd/es/button';
|
|
5
5
|
import type { TooltipProps } from 'antd/es/tooltip';
|
|
@@ -134,7 +134,7 @@ export type viewRenderFn<T = any> = (text?: any, record?: T, options?: OptionsPr
|
|
|
134
134
|
* 组件渲染函数类型(与 ReactiveFunction 保持一致)
|
|
135
135
|
* @template T 记录类型
|
|
136
136
|
*/
|
|
137
|
-
export type ComponentRenderFn<T = any> = ReactiveFunction<T,
|
|
137
|
+
export type ComponentRenderFn<T = any> = ReactiveFunction<T, ReactElement>;
|
|
138
138
|
/**
|
|
139
139
|
* 表格列属性接口
|
|
140
140
|
* @template Values 值类型
|
|
@@ -157,8 +157,8 @@ export interface ProColumnsProps<Values = any, T = any> extends Omit<FormItemPro
|
|
|
157
157
|
tooltip?: string | ({
|
|
158
158
|
icon?: string | ReactNode;
|
|
159
159
|
} & TooltipProps);
|
|
160
|
-
/**
|
|
161
|
-
component?:
|
|
160
|
+
/** 自定义组件,或返回合法 ReactElement 的响应式渲染函数 */
|
|
161
|
+
component?: ReactElement | ComponentRenderFn<T>;
|
|
162
162
|
/** 视图渲染 */
|
|
163
163
|
viewRender?: string | number | ReactNode | viewRenderFn<T>;
|
|
164
164
|
/** 隐藏的字段名 */
|
|
@@ -11,6 +11,10 @@ interface Result {
|
|
|
11
11
|
desensitization: any;
|
|
12
12
|
valueType: any;
|
|
13
13
|
component: any;
|
|
14
|
+
/** 动态 component 是否已经执行过,用于区分“返回 undefined”和“尚未计算”。 */
|
|
15
|
+
componentResolved: boolean;
|
|
16
|
+
/** 当前缓存结果对应的行浅快照,供 RenderField 判断是否可安全复用。 */
|
|
17
|
+
rowSnapshot: Record<string, any> | undefined;
|
|
14
18
|
}
|
|
15
19
|
declare const useShouldUpdateForTable: (props: UseShouldUpdateForTableProps) => Result;
|
|
16
20
|
export default useShouldUpdateForTable;
|
|
@@ -3,6 +3,7 @@ import _isEqualWith from "lodash/isEqualWith";
|
|
|
3
3
|
import _isFunction from "lodash/isFunction";
|
|
4
4
|
import { useRef, useState, useEffect } from 'react';
|
|
5
5
|
import { customEqualForFunction } from "../../utils";
|
|
6
|
+
import { isRecordShallowEqual } from "./tools";
|
|
6
7
|
const useShouldUpdateForTable = props => {
|
|
7
8
|
const {
|
|
8
9
|
rowParams,
|
|
@@ -18,6 +19,8 @@ const useShouldUpdateForTable = props => {
|
|
|
18
19
|
const desensitizationRef = useRef(undefined);
|
|
19
20
|
const valueTypeRef = useRef(undefined);
|
|
20
21
|
const componentRef = useRef(undefined);
|
|
22
|
+
const componentResolvedRef = useRef(false);
|
|
23
|
+
const resolvedRowSnapshotRef = useRef(undefined);
|
|
21
24
|
const [, reRender] = useState({});
|
|
22
25
|
const debouncedUpdateRef = useRef(null);
|
|
23
26
|
const pendingParamsRef = useRef(null);
|
|
@@ -99,6 +102,7 @@ const useShouldUpdateForTable = props => {
|
|
|
99
102
|
const componentOrEditRender = column.component || column.editRender;
|
|
100
103
|
if (_isFunction(componentOrEditRender)) {
|
|
101
104
|
const newComponent = componentOrEditRender(values, reactiveParams);
|
|
105
|
+
componentResolvedRef.current = true;
|
|
102
106
|
// 对于 ReactNode 类型的返回值,使用深度比较
|
|
103
107
|
if (!_isEqualWith(componentRef.current, newComponent, customEqualForFunction)) {
|
|
104
108
|
componentRef.current = newComponent;
|
|
@@ -107,6 +111,9 @@ const useShouldUpdateForTable = props => {
|
|
|
107
111
|
} else {
|
|
108
112
|
componentRef.current = componentOrEditRender;
|
|
109
113
|
}
|
|
114
|
+
resolvedRowSnapshotRef.current = values && typeof values === 'object' ? {
|
|
115
|
+
...values
|
|
116
|
+
} : values;
|
|
110
117
|
if (hasChange) {
|
|
111
118
|
reRender({});
|
|
112
119
|
}
|
|
@@ -136,12 +143,15 @@ const useShouldUpdateForTable = props => {
|
|
|
136
143
|
const fieldPropsFactoryChanged = prevFieldPropsFactoryRef.current !== currentFieldPropsFactory;
|
|
137
144
|
prevFieldPropsFactoryRef.current = currentFieldPropsFactory;
|
|
138
145
|
|
|
139
|
-
//
|
|
146
|
+
// 上一次保存的是浅快照,可同时覆盖引用稳定和 onFieldChange 原地修改两种路径。
|
|
140
147
|
const prevRowParams = prevRowParamsRef.current;
|
|
141
|
-
const rowDataUnchanged = isInitializedRef.current && prevRowParams?.[0]
|
|
148
|
+
const rowDataUnchanged = isInitializedRef.current && isRecordShallowEqual(prevRowParams?.[0], rowParams[0]) && prevRowParams?.[1]?.index === rowParams[1]?.index;
|
|
142
149
|
const shouldProcess = !isInitializedRef.current || fieldPropsFactoryChanged || !rowDataUnchanged && !_isEqualWith(prevRowParams, rowParams, customEqualForFunction);
|
|
143
150
|
if (shouldProcess) {
|
|
144
|
-
|
|
151
|
+
const currentRowData = rowParams[0];
|
|
152
|
+
prevRowParamsRef.current = [currentRowData && typeof currentRowData === 'object' ? {
|
|
153
|
+
...currentRowData
|
|
154
|
+
} : currentRowData, rowParams[1]];
|
|
145
155
|
isInitializedRef.current = true;
|
|
146
156
|
if (fieldPropsFactoryChanged) {
|
|
147
157
|
// 外部依赖变化会生成新的 fieldProps 函数,需要立即替换旧闭包缓存
|
|
@@ -162,7 +172,9 @@ const useShouldUpdateForTable = props => {
|
|
|
162
172
|
fieldProps: fieldPropsRef.current ?? column.fieldProps,
|
|
163
173
|
desensitization: desensitizationRef.current ?? column.desensitization,
|
|
164
174
|
valueType: valueTypeRef.current ?? column.valueType,
|
|
165
|
-
component: componentRef.current
|
|
175
|
+
component: componentResolvedRef.current ? componentRef.current : column.component || column.editRender,
|
|
176
|
+
componentResolved: componentResolvedRef.current,
|
|
177
|
+
rowSnapshot: resolvedRowSnapshotRef.current
|
|
166
178
|
};
|
|
167
179
|
};
|
|
168
180
|
export default useShouldUpdateForTable;
|
|
@@ -81,9 +81,11 @@ export function useRequestList(service, options, useRequestOptions) {
|
|
|
81
81
|
});
|
|
82
82
|
}, [onPageChange]);
|
|
83
83
|
const onSearch = useCallback(values => {
|
|
84
|
+
if (page.pageNum !== 1) {
|
|
85
|
+
// 查询或重置都从第一页开始,保留当前每页条数
|
|
86
|
+
onChange(1, page.pageSize);
|
|
87
|
+
}
|
|
84
88
|
if (!_isEqual(searchValues, values)) {
|
|
85
|
-
// 保留当前页码和每页条数
|
|
86
|
-
onChange(page.pageNum, page.pageSize);
|
|
87
89
|
setSearchValues(values);
|
|
88
90
|
}
|
|
89
91
|
}, [onChange, searchValues, page]);
|
|
@@ -51,6 +51,7 @@ export function TabsHeader({
|
|
|
51
51
|
const tabKeys = (tabsItems ?? []).map(item => item.key);
|
|
52
52
|
return /*#__PURE__*/_jsx("div", {
|
|
53
53
|
className: "pro-layout-tabs-header",
|
|
54
|
+
"data-pro-layout-tabs-measure": "true",
|
|
54
55
|
children: /*#__PURE__*/_jsx(Tabs, {
|
|
55
56
|
activeKey: activeKey,
|
|
56
57
|
onChange: onTabChange,
|
|
@@ -171,12 +171,11 @@
|
|
|
171
171
|
padding: 4px 0 5px !important;
|
|
172
172
|
margin-right: 8px !important;
|
|
173
173
|
margin-left: 0;
|
|
174
|
-
background: #
|
|
174
|
+
background: #F5F5F5;
|
|
175
175
|
transition: none;
|
|
176
176
|
border-color: #e7e8ee;
|
|
177
177
|
&.@{ant-prefix}-tabs-tab-active {
|
|
178
178
|
background: #ffffff;
|
|
179
|
-
border-top: 2px solid var(--zaui-brand, #006aff);
|
|
180
179
|
}
|
|
181
180
|
}
|
|
182
181
|
.ant-tabs-nav {
|