@zat-design/sisyphus-react 4.5.8-beta.6 → 4.5.8
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 +48 -37
- package/es/ProEditTable/index.js +66 -36
- package/es/ProEditTable/utils/useShouldUpdateForTable.d.ts +2 -0
- package/es/ProEditTable/utils/useShouldUpdateForTable.js +13 -4
- package/es/ProLayout/components/TabsManager/components/TabsHeader.js +1 -0
- package/es/ProLayout/index.js +22 -2
- package/es/ProLayout/style/index.less +2 -3
- package/es/ProTable/components/EditableCell/EditIcon.js +2 -6
- package/es/ProTable/components/EditableCell/index.less +0 -5
- 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/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";
|
|
@@ -104,6 +104,11 @@ const RenderField = ({
|
|
|
104
104
|
const namePath = useMemo(() => getNamePath(name, virtualKey), [namePathKey, name, virtualKey]);
|
|
105
105
|
const rowNamePath = useMemo(() => [...namePath, index], [namePath, index]);
|
|
106
106
|
const rowData = form.getFieldValue(rowNamePath) || record || {};
|
|
107
|
+
|
|
108
|
+
// Form.Item shouldUpdate 可能在 RenderField 外层未重渲染时多次执行 render props。
|
|
109
|
+
// 缓存本次外层渲染期间的最新行计算结果,避免同一行版本重复调用动态配置函数。
|
|
110
|
+
const shouldUpdateStateCacheRef = useRef(undefined);
|
|
111
|
+
shouldUpdateStateCacheRef.current = undefined;
|
|
107
112
|
let currentValue = dataIndex ? rowData?.[dataIndex] : null;
|
|
108
113
|
|
|
109
114
|
// 构建新的参数格式(与 ProForm 保持一致)
|
|
@@ -663,45 +668,48 @@ const RenderField = ({
|
|
|
663
668
|
namePath: rowNamePath
|
|
664
669
|
};
|
|
665
670
|
const latestRowParams = [latestRowData, latestReactiveParams];
|
|
671
|
+
const cachedState = shouldUpdateStateCacheRef.current;
|
|
672
|
+
if (cachedState && isRecordShallowEqual(cachedState.rowSnapshot, latestRowData)) {
|
|
673
|
+
return cachedState.result;
|
|
674
|
+
}
|
|
666
675
|
|
|
667
|
-
//
|
|
668
|
-
|
|
669
|
-
|
|
676
|
+
// 初次 render props 会与外层 RenderField 使用同一行数据,直接复用 hook 已计算结果。
|
|
677
|
+
const canReuseCurrentState = isRecordShallowEqual(latestRowData, dynamicProps.rowSnapshot);
|
|
678
|
+
let latestFieldProps = canReuseCurrentState ? lastFieldProps : fieldProps || {};
|
|
679
|
+
if (!canReuseCurrentState && _isFunction(fieldProps)) {
|
|
670
680
|
latestFieldProps = fieldProps(latestRowData, latestReactiveParams);
|
|
671
681
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
let latestRequired = required;
|
|
675
|
-
if (_isFunction(required)) {
|
|
682
|
+
let latestRequired = canReuseCurrentState ? lastRequired : required;
|
|
683
|
+
if (!canReuseCurrentState && _isFunction(required)) {
|
|
676
684
|
latestRequired = required(latestRowData, latestReactiveParams);
|
|
677
685
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
let latestRules = rules;
|
|
681
|
-
if (_isFunction(rules)) {
|
|
686
|
+
let latestRules = canReuseCurrentState ? lastRules : rules;
|
|
687
|
+
if (!canReuseCurrentState && _isFunction(rules)) {
|
|
682
688
|
latestRules = rules(latestRowData, latestReactiveParams);
|
|
683
689
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
let latestDesensitization = desensitization || [];
|
|
687
|
-
if (_isFunction(desensitization)) {
|
|
690
|
+
let latestDesensitization = canReuseCurrentState ? lastDesensitization : desensitization || [];
|
|
691
|
+
if (!canReuseCurrentState && _isFunction(desensitization)) {
|
|
688
692
|
latestDesensitization = desensitization(latestRowData, latestReactiveParams);
|
|
689
693
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
let latestComponent = component || editRender;
|
|
693
|
-
if (_isFunction(latestComponent)) {
|
|
694
|
+
let latestComponent = canReuseCurrentState ? lastComponent : component || editRender;
|
|
695
|
+
if (!canReuseCurrentState && _isFunction(latestComponent)) {
|
|
694
696
|
latestComponent = latestComponent(latestRowData, latestReactiveParams);
|
|
695
697
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
let latestIsEditable = column.isEditable ?? true;
|
|
699
|
-
if (_isFunction(column.isEditable)) {
|
|
698
|
+
let latestIsEditable = canReuseCurrentState ? isEditable : column.isEditable ?? true;
|
|
699
|
+
if (!canReuseCurrentState && _isFunction(column.isEditable)) {
|
|
700
700
|
latestIsEditable = column.isEditable(latestRowData, latestReactiveParams);
|
|
701
701
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
702
|
+
let latestValueType = canReuseCurrentState ? lastValueType : valueType;
|
|
703
|
+
if (!canReuseCurrentState && _isFunction(valueType)) {
|
|
704
|
+
const latestCurrentValue = dataIndex ? latestRowData?.[dataIndex] : null;
|
|
705
|
+
latestValueType = valueType(latestCurrentValue, latestRowData, {
|
|
706
|
+
index,
|
|
707
|
+
form,
|
|
708
|
+
namePath: rowNamePath,
|
|
709
|
+
name: column?.name
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
const latestDisabled = canReuseCurrentState ? lastDisabled : getDisabled({
|
|
705
713
|
globalControl: otherProps?.globalControl,
|
|
706
714
|
formDisabled: otherProps?.formDisabled,
|
|
707
715
|
column,
|
|
@@ -721,13 +729,8 @@ const RenderField = ({
|
|
|
721
729
|
onChange: handleChange,
|
|
722
730
|
onBlur: handleBlur
|
|
723
731
|
};
|
|
724
|
-
|
|
725
|
-
// ⭐ 关键修改:在 shouldUpdate 模式下,需要重新设置 TargetComponent
|
|
726
|
-
// 因为 component 函数可能返回不同的结果
|
|
727
732
|
let latestTargetComponent = TargetComponent;
|
|
728
|
-
|
|
729
|
-
// 首先检查是否有内置type
|
|
730
|
-
if (!latestComponent && typeof type === 'string') {
|
|
733
|
+
if (!canReuseCurrentState && !latestComponent && typeof type === 'string') {
|
|
731
734
|
latestTargetComponent = componentMap[type] ?? /*#__PURE__*/_jsx(_Fragment, {});
|
|
732
735
|
}
|
|
733
736
|
|
|
@@ -741,7 +744,7 @@ const RenderField = ({
|
|
|
741
744
|
params: latestRowParams,
|
|
742
745
|
rowDisabled: rowDisabled || 'empty'
|
|
743
746
|
});
|
|
744
|
-
if (latestIsEditable && isEditing) {
|
|
747
|
+
if (!canReuseCurrentState && latestIsEditable && isEditing) {
|
|
745
748
|
if ( /*#__PURE__*/React.isValidElement(latestComponent)) {
|
|
746
749
|
latestTargetComponent = latestComponent;
|
|
747
750
|
} else if (latestComponent) {
|
|
@@ -750,7 +753,7 @@ const RenderField = ({
|
|
|
750
753
|
}
|
|
751
754
|
|
|
752
755
|
// 查看模式
|
|
753
|
-
if (latestIsView) {
|
|
756
|
+
if (!canReuseCurrentState && latestIsView) {
|
|
754
757
|
if (typeof viewRender === 'function') {
|
|
755
758
|
const latestCurrentValue = dataIndex ? latestRowData?.[dataIndex] : value;
|
|
756
759
|
const View = viewRender(latestCurrentValue, latestRowData || {}, options);
|
|
@@ -769,7 +772,8 @@ const RenderField = ({
|
|
|
769
772
|
...latestComponentProps,
|
|
770
773
|
otherProps: {
|
|
771
774
|
...latestComponentProps.otherProps,
|
|
772
|
-
isView: latestIsView
|
|
775
|
+
isView: latestIsView,
|
|
776
|
+
valueType: latestValueType
|
|
773
777
|
}
|
|
774
778
|
};
|
|
775
779
|
|
|
@@ -788,7 +792,7 @@ const RenderField = ({
|
|
|
788
792
|
};
|
|
789
793
|
}
|
|
790
794
|
}
|
|
791
|
-
|
|
795
|
+
const result = {
|
|
792
796
|
finalComponentProps: latestComponentProps,
|
|
793
797
|
targetComponent: latestTargetComponent,
|
|
794
798
|
latestIsView,
|
|
@@ -796,6 +800,13 @@ const RenderField = ({
|
|
|
796
800
|
effectiveRules: latestRules,
|
|
797
801
|
effectiveFieldProps: latestFieldProps
|
|
798
802
|
};
|
|
803
|
+
shouldUpdateStateCacheRef.current = {
|
|
804
|
+
rowSnapshot: {
|
|
805
|
+
...latestRowData
|
|
806
|
+
},
|
|
807
|
+
result
|
|
808
|
+
};
|
|
809
|
+
return result;
|
|
799
810
|
};
|
|
800
811
|
|
|
801
812
|
// 根据 latestIsView 和最新 required/rules 构建正确的 Form.Item props
|
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
|
|
@@ -11,6 +11,8 @@ interface Result {
|
|
|
11
11
|
desensitization: any;
|
|
12
12
|
valueType: any;
|
|
13
13
|
component: any;
|
|
14
|
+
/** 当前缓存结果对应的行浅快照,供 RenderField 判断是否可安全复用。 */
|
|
15
|
+
rowSnapshot: Record<string, any> | undefined;
|
|
14
16
|
}
|
|
15
17
|
declare const useShouldUpdateForTable: (props: UseShouldUpdateForTableProps) => Result;
|
|
16
18
|
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,7 @@ const useShouldUpdateForTable = props => {
|
|
|
18
19
|
const desensitizationRef = useRef(undefined);
|
|
19
20
|
const valueTypeRef = useRef(undefined);
|
|
20
21
|
const componentRef = useRef(undefined);
|
|
22
|
+
const resolvedRowSnapshotRef = useRef(undefined);
|
|
21
23
|
const [, reRender] = useState({});
|
|
22
24
|
const debouncedUpdateRef = useRef(null);
|
|
23
25
|
const pendingParamsRef = useRef(null);
|
|
@@ -107,6 +109,9 @@ const useShouldUpdateForTable = props => {
|
|
|
107
109
|
} else {
|
|
108
110
|
componentRef.current = componentOrEditRender;
|
|
109
111
|
}
|
|
112
|
+
resolvedRowSnapshotRef.current = values && typeof values === 'object' ? {
|
|
113
|
+
...values
|
|
114
|
+
} : values;
|
|
110
115
|
if (hasChange) {
|
|
111
116
|
reRender({});
|
|
112
117
|
}
|
|
@@ -136,12 +141,15 @@ const useShouldUpdateForTable = props => {
|
|
|
136
141
|
const fieldPropsFactoryChanged = prevFieldPropsFactoryRef.current !== currentFieldPropsFactory;
|
|
137
142
|
prevFieldPropsFactoryRef.current = currentFieldPropsFactory;
|
|
138
143
|
|
|
139
|
-
//
|
|
144
|
+
// 上一次保存的是浅快照,可同时覆盖引用稳定和 onFieldChange 原地修改两种路径。
|
|
140
145
|
const prevRowParams = prevRowParamsRef.current;
|
|
141
|
-
const rowDataUnchanged = isInitializedRef.current && prevRowParams?.[0]
|
|
146
|
+
const rowDataUnchanged = isInitializedRef.current && isRecordShallowEqual(prevRowParams?.[0], rowParams[0]) && prevRowParams?.[1]?.index === rowParams[1]?.index;
|
|
142
147
|
const shouldProcess = !isInitializedRef.current || fieldPropsFactoryChanged || !rowDataUnchanged && !_isEqualWith(prevRowParams, rowParams, customEqualForFunction);
|
|
143
148
|
if (shouldProcess) {
|
|
144
|
-
|
|
149
|
+
const currentRowData = rowParams[0];
|
|
150
|
+
prevRowParamsRef.current = [currentRowData && typeof currentRowData === 'object' ? {
|
|
151
|
+
...currentRowData
|
|
152
|
+
} : currentRowData, rowParams[1]];
|
|
145
153
|
isInitializedRef.current = true;
|
|
146
154
|
if (fieldPropsFactoryChanged) {
|
|
147
155
|
// 外部依赖变化会生成新的 fieldProps 函数,需要立即替换旧闭包缓存
|
|
@@ -162,7 +170,8 @@ const useShouldUpdateForTable = props => {
|
|
|
162
170
|
fieldProps: fieldPropsRef.current ?? column.fieldProps,
|
|
163
171
|
desensitization: desensitizationRef.current ?? column.desensitization,
|
|
164
172
|
valueType: valueTypeRef.current ?? column.valueType,
|
|
165
|
-
component: componentRef.current ?? (column.component || column.editRender)
|
|
173
|
+
component: componentRef.current ?? (column.component || column.editRender),
|
|
174
|
+
rowSnapshot: resolvedRowSnapshotRef.current
|
|
166
175
|
};
|
|
167
176
|
};
|
|
168
177
|
export default useShouldUpdateForTable;
|
|
@@ -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,
|
package/es/ProLayout/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import headerBg from "../assets/header_bg.png";
|
|
|
17
17
|
// 全局上下文
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
19
|
export const LayoutContext = /*#__PURE__*/createContext(undefined);
|
|
20
|
+
const TABS_HEADER_MEASURE_SELECTOR = '[data-pro-layout-tabs-measure="true"]';
|
|
20
21
|
const ProLayout = props => {
|
|
21
22
|
const {
|
|
22
23
|
children,
|
|
@@ -94,26 +95,45 @@ const ProLayout = props => {
|
|
|
94
95
|
useLayoutEffect(() => {
|
|
95
96
|
const noticeEl = notice && !isIframePure ? noticeElRef.current : null;
|
|
96
97
|
const visibleTabsBarEl = isTabsLayout && hasTabs && !isIframePure ? tabsBarEl : null;
|
|
98
|
+
const visibleTabsHeaderEl = visibleTabsBarEl?.querySelector(TABS_HEADER_MEASURE_SELECTOR);
|
|
97
99
|
let active = true;
|
|
100
|
+
let animationFrameId;
|
|
98
101
|
const measureStickyElements = () => {
|
|
99
102
|
if (!active) return;
|
|
100
103
|
const nextHeights = {
|
|
101
104
|
notice: noticeEl?.getBoundingClientRect().height ?? 0,
|
|
102
|
-
tabsBar:
|
|
105
|
+
tabsBar: visibleTabsHeaderEl?.getBoundingClientRect().height ?? 0
|
|
103
106
|
};
|
|
104
107
|
setStickyHeights(currentHeights => currentHeights.notice === nextHeights.notice && currentHeights.tabsBar === nextHeights.tabsBar ? currentHeights : nextHeights);
|
|
105
108
|
};
|
|
106
109
|
measureStickyElements();
|
|
107
|
-
|
|
110
|
+
if (typeof window.requestAnimationFrame === 'function') {
|
|
111
|
+
animationFrameId = window.requestAnimationFrame(measureStickyElements);
|
|
112
|
+
}
|
|
113
|
+
const handleTabsTransitionEnd = event => {
|
|
114
|
+
if (event.target === visibleTabsBarEl && event.propertyName === 'grid-template-rows') {
|
|
115
|
+
measureStickyElements();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
visibleTabsBarEl?.addEventListener('transitionend', handleTabsTransitionEnd);
|
|
119
|
+
const observedElements = [noticeEl, visibleTabsHeaderEl].filter(element => element !== null && element !== undefined);
|
|
108
120
|
if (observedElements.length === 0 || typeof ResizeObserver === 'undefined') {
|
|
109
121
|
return () => {
|
|
110
122
|
active = false;
|
|
123
|
+
if (animationFrameId !== undefined) {
|
|
124
|
+
window.cancelAnimationFrame(animationFrameId);
|
|
125
|
+
}
|
|
126
|
+
visibleTabsBarEl?.removeEventListener('transitionend', handleTabsTransitionEnd);
|
|
111
127
|
};
|
|
112
128
|
}
|
|
113
129
|
const resizeObserver = new ResizeObserver(measureStickyElements);
|
|
114
130
|
observedElements.forEach(element => resizeObserver.observe(element));
|
|
115
131
|
return () => {
|
|
116
132
|
active = false;
|
|
133
|
+
if (animationFrameId !== undefined) {
|
|
134
|
+
window.cancelAnimationFrame(animationFrameId);
|
|
135
|
+
}
|
|
136
|
+
visibleTabsBarEl?.removeEventListener('transitionend', handleTabsTransitionEnd);
|
|
117
137
|
resizeObserver.disconnect();
|
|
118
138
|
};
|
|
119
139
|
}, [hasTabs, isIframePure, isTabsLayout, notice, tabsBarEl]);
|
|
@@ -388,11 +388,10 @@
|
|
|
388
388
|
|
|
389
389
|
.pro-layout-tabs-content {
|
|
390
390
|
> .tab-pane {
|
|
391
|
-
// ProStep 以 ProHeader
|
|
392
|
-
// ProStep
|
|
391
|
+
// ProStep 以 ProHeader 开头时,仅去掉 tabs 内容区的顶部留白。
|
|
392
|
+
// 保留右侧内边距,让 ProStep wrapper 继续为贴右步骤条预留 16px 间距。
|
|
393
393
|
> .pro-step-wrapper:has(> .pro-header:first-child) {
|
|
394
394
|
margin-top: calc(-1px - var(--zaui-space-size-md, 16px));
|
|
395
|
-
margin-right: calc(-1px - var(--zaui-space-size-md, 16px));
|
|
396
395
|
}
|
|
397
396
|
|
|
398
397
|
> .pro-header.pro-header-no-describe:first-child {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { EditOutlined } from '@ant-design/icons';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import ProIcon from "../../../ProIcon";
|
|
3
|
-
import editSvg from "../../../assets/edit.svg";
|
|
4
3
|
import styles from "./index.less";
|
|
5
4
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
5
|
const EditIcon = ({
|
|
@@ -9,10 +8,7 @@ const EditIcon = ({
|
|
|
9
8
|
return /*#__PURE__*/_jsx("span", {
|
|
10
9
|
className: styles['editable-cell-edit-icon'],
|
|
11
10
|
onClick: onClick,
|
|
12
|
-
children: /*#__PURE__*/_jsx(
|
|
13
|
-
src: editSvg,
|
|
14
|
-
size: "20px"
|
|
15
|
-
})
|
|
11
|
+
children: /*#__PURE__*/_jsx(EditOutlined, {})
|
|
16
12
|
});
|
|
17
13
|
};
|
|
18
14
|
export default EditIcon;
|
|
@@ -14,10 +14,6 @@
|
|
|
14
14
|
color: rgba(0, 0, 0, 0.65);
|
|
15
15
|
flex-shrink: 0;
|
|
16
16
|
|
|
17
|
-
svg path {
|
|
18
|
-
fill: currentColor !important;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
17
|
&:hover {
|
|
22
18
|
color: var(--zaui-brand, #006aff);
|
|
23
19
|
}
|
|
@@ -26,4 +22,3 @@
|
|
|
26
22
|
td:hover .editable-cell-edit-icon {
|
|
27
23
|
visibility: visible;
|
|
28
24
|
}
|
|
29
|
-
|