zmdms-webui 2.3.8 → 2.4.0
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/es/canvastable/canvasTable.js +2 -2
- package/dist/es/canvastable/hooks/useCopyToClipboard.js +34 -28
- package/dist/es/canvastable/hooks/useScroll.js +20 -33
- package/dist/es/canvastable/hooks/useTableInteraction.js +53 -17
- package/dist/es/canvastable/hooks/useTableRender.js +234 -137
- package/dist/es/table/hooks.js +54 -16
- package/package.json +1 -1
|
@@ -155,7 +155,6 @@ function CanvasTable(props) {
|
|
|
155
155
|
}), maxScrollTop = _1.maxScrollTop, maxScrollLeft = _1.maxScrollLeft;
|
|
156
156
|
// 滚动管理(使用预先计算的maxScrollTop和maxScrollLeft)
|
|
157
157
|
var _2 = useTableScroll({
|
|
158
|
-
canvasRef: canvasRef,
|
|
159
158
|
containerRef: containerRef,
|
|
160
159
|
maxScrollTop: maxScrollTop,
|
|
161
160
|
maxScrollLeft: maxScrollLeft,
|
|
@@ -269,9 +268,10 @@ function CanvasTable(props) {
|
|
|
269
268
|
return processedColumns.filter(function (item) { return !item.key.includes("__"); });
|
|
270
269
|
}, [processedColumns]);
|
|
271
270
|
// Excel导出功能
|
|
272
|
-
var exportExcel = useExcelExport(
|
|
271
|
+
var exportExcel = useExcelExport(newDataSource, {
|
|
273
272
|
columns: exportColumns,
|
|
274
273
|
isAutoMerge: isAutoMerge,
|
|
274
|
+
summaryConfig: [],
|
|
275
275
|
});
|
|
276
276
|
// 交互事件处理(使用baseScrollbarMetrics的maxScrollTop/maxScrollLeft以保持稳定)
|
|
277
277
|
var _5 = useTableInteraction({
|
|
@@ -90,28 +90,38 @@ var useCopyToClipboard = function (params) {
|
|
|
90
90
|
}
|
|
91
91
|
return rows.join("\n");
|
|
92
92
|
});
|
|
93
|
-
//
|
|
94
|
-
var
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
// 兼容的复制到剪贴板函数(支持HTTP环境)
|
|
94
|
+
var copyToClipboard = useMemoizedFn(function (text) {
|
|
95
|
+
// 优先使用现代 clipboard API(仅在HTTPS/localhost下可用)
|
|
96
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
97
|
+
navigator.clipboard.writeText(text).catch(function (err) {
|
|
98
|
+
console.error("Failed to copy with clipboard API:", err);
|
|
99
|
+
});
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
// 降级方案:使用 execCommand(支持HTTP环境)
|
|
103
|
+
try {
|
|
104
|
+
var textarea = document.createElement("textarea");
|
|
105
|
+
textarea.value = text;
|
|
106
|
+
textarea.style.position = "fixed";
|
|
107
|
+
textarea.style.opacity = "0";
|
|
108
|
+
textarea.style.pointerEvents = "none";
|
|
109
|
+
document.body.appendChild(textarea);
|
|
110
|
+
textarea.select();
|
|
111
|
+
textarea.setSelectionRange(0, textarea.value.length);
|
|
112
|
+
var success = document.execCommand("copy");
|
|
113
|
+
document.body.removeChild(textarea);
|
|
114
|
+
if (!success) {
|
|
115
|
+
console.error("Failed to copy using execCommand");
|
|
116
|
+
}
|
|
117
|
+
return success;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error("Failed to copy:", err);
|
|
121
|
+
return false;
|
|
102
122
|
}
|
|
103
123
|
});
|
|
104
|
-
//
|
|
105
|
-
useEffect(function () {
|
|
106
|
-
var container = containerRef.current;
|
|
107
|
-
if (!container)
|
|
108
|
-
return;
|
|
109
|
-
container.addEventListener("copy", handleCopy);
|
|
110
|
-
return function () {
|
|
111
|
-
container.removeEventListener("copy", handleCopy);
|
|
112
|
-
};
|
|
113
|
-
}, [handleCopy, containerRef]);
|
|
114
|
-
// 监听键盘事件(Ctrl+C / Cmd+C)- 只在表格容器内部处理
|
|
124
|
+
// 监听键盘事件(Ctrl+C / Cmd+C)处理复制
|
|
115
125
|
useEffect(function () {
|
|
116
126
|
var container = containerRef.current;
|
|
117
127
|
if (!container)
|
|
@@ -125,25 +135,21 @@ var useCopyToClipboard = function (params) {
|
|
|
125
135
|
if (isInContainer) {
|
|
126
136
|
e.preventDefault(); // 阻止默认复制行为
|
|
127
137
|
var text = getSelectedCellsText();
|
|
128
|
-
if (text
|
|
129
|
-
|
|
130
|
-
console.error("Failed to copy:", err);
|
|
131
|
-
});
|
|
138
|
+
if (text) {
|
|
139
|
+
copyToClipboard(text);
|
|
132
140
|
}
|
|
133
141
|
}
|
|
134
142
|
}
|
|
135
143
|
};
|
|
136
|
-
// 监听容器的keydown事件,而不是全局window事件
|
|
137
|
-
container.addEventListener("keydown", handleKeyDown);
|
|
138
144
|
// 同时也监听全局事件,但会检查焦点位置
|
|
139
145
|
document.addEventListener("keydown", handleKeyDown);
|
|
140
146
|
return function () {
|
|
141
|
-
container.removeEventListener("keydown", handleKeyDown);
|
|
142
147
|
document.removeEventListener("keydown", handleKeyDown);
|
|
143
148
|
};
|
|
144
|
-
}, [cellSelection, getSelectedCellsText, containerRef]);
|
|
149
|
+
}, [cellSelection, getSelectedCellsText, copyToClipboard, containerRef]);
|
|
145
150
|
return {
|
|
146
151
|
getSelectedCellsText: getSelectedCellsText,
|
|
152
|
+
copyToClipboard: copyToClipboard,
|
|
147
153
|
};
|
|
148
154
|
};
|
|
149
155
|
|
|
@@ -79,12 +79,11 @@ var useScrollbarMetrics = function (params) {
|
|
|
79
79
|
* 表格滚动管理 Hook
|
|
80
80
|
*
|
|
81
81
|
* 处理滚动相关的所有事件:
|
|
82
|
-
* -
|
|
83
|
-
* - Container 滚轮事件(阻止默认行为)
|
|
82
|
+
* - Container 滚轮事件(统一处理滚动,支持CellOverlay等覆盖层)
|
|
84
83
|
* - 全局鼠标事件(拖拽滚动条)
|
|
85
84
|
*/
|
|
86
85
|
var useTableScroll = function (params) {
|
|
87
|
-
var
|
|
86
|
+
var containerRef = params.containerRef, maxScrollTop = params.maxScrollTop, maxScrollLeft = params.maxScrollLeft, onScroll = params.onScroll;
|
|
88
87
|
var _a = useState({
|
|
89
88
|
scrollLeft: 0,
|
|
90
89
|
scrollTop: 0,
|
|
@@ -98,14 +97,23 @@ var useTableScroll = function (params) {
|
|
|
98
97
|
// 用于节流滚轮事件的refs
|
|
99
98
|
var rafIdRef = useRef(null);
|
|
100
99
|
var pendingDeltaRef = useRef({ deltaX: 0, deltaY: 0 });
|
|
101
|
-
//
|
|
100
|
+
// 注意:滚轮事件统一在Container级别处理,以确保在CellOverlay等覆盖层上也能正常滚动
|
|
101
|
+
// 滚轮事件处理(Container)
|
|
102
102
|
useEffect(function () {
|
|
103
|
-
var
|
|
104
|
-
if (!
|
|
103
|
+
var container = containerRef.current;
|
|
104
|
+
if (!container)
|
|
105
105
|
return;
|
|
106
|
-
var
|
|
106
|
+
var handleNativeContainerWheel = function (e) {
|
|
107
|
+
// 检查事件是否来自过滤弹框,如果是,允许滚动
|
|
108
|
+
var target = e.target;
|
|
109
|
+
var isInFilterPopover = target.closest(".canvas-table-filter-popover");
|
|
110
|
+
if (isInFilterPopover) {
|
|
111
|
+
// 允许过滤弹框内部滚动
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
107
114
|
e.preventDefault();
|
|
108
115
|
e.stopPropagation();
|
|
116
|
+
// 当鼠标在容器内任何位置(包括CellOverlay)时都能滚动
|
|
109
117
|
// 累积滚动增量
|
|
110
118
|
pendingDeltaRef.current.deltaX += e.deltaX;
|
|
111
119
|
pendingDeltaRef.current.deltaY += e.deltaY;
|
|
@@ -134,39 +142,18 @@ var useTableScroll = function (params) {
|
|
|
134
142
|
});
|
|
135
143
|
});
|
|
136
144
|
};
|
|
137
|
-
|
|
145
|
+
container.addEventListener("wheel", handleNativeContainerWheel, {
|
|
146
|
+
passive: false,
|
|
147
|
+
});
|
|
138
148
|
return function () {
|
|
139
|
-
|
|
149
|
+
container.removeEventListener("wheel", handleNativeContainerWheel);
|
|
140
150
|
// 清理pending的动画帧
|
|
141
151
|
if (rafIdRef.current !== null) {
|
|
142
152
|
cancelAnimationFrame(rafIdRef.current);
|
|
143
153
|
rafIdRef.current = null;
|
|
144
154
|
}
|
|
145
155
|
};
|
|
146
|
-
}, [maxScrollTop, maxScrollLeft, onScroll,
|
|
147
|
-
// 滚轮事件处理(Container)
|
|
148
|
-
useEffect(function () {
|
|
149
|
-
var container = containerRef.current;
|
|
150
|
-
if (!container)
|
|
151
|
-
return;
|
|
152
|
-
var handleNativeContainerWheel = function (e) {
|
|
153
|
-
// 检查事件是否来自过滤弹框,如果是,允许滚动
|
|
154
|
-
var target = e.target;
|
|
155
|
-
var isInFilterPopover = target.closest(".canvas-table-filter-popover");
|
|
156
|
-
if (isInFilterPopover) {
|
|
157
|
-
// 允许过滤弹框内部滚动
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
e.preventDefault();
|
|
161
|
-
e.stopPropagation();
|
|
162
|
-
};
|
|
163
|
-
container.addEventListener("wheel", handleNativeContainerWheel, {
|
|
164
|
-
passive: false,
|
|
165
|
-
});
|
|
166
|
-
return function () {
|
|
167
|
-
container.removeEventListener("wheel", handleNativeContainerWheel);
|
|
168
|
-
};
|
|
169
|
-
}, [containerRef]);
|
|
156
|
+
}, [containerRef, maxScrollTop, maxScrollLeft, onScroll, setScrollState]);
|
|
170
157
|
// 全局鼠标事件处理(拖拽滚动条)
|
|
171
158
|
useEffect(function () {
|
|
172
159
|
var handleGlobalMouseUp = function () {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __assign, __spreadArray } from '../../_virtual/_tslib.js';
|
|
2
|
-
import { useEffect } from 'react';
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
3
|
import { useMemoizedFn } from 'ahooks';
|
|
4
4
|
import { FONT_SIZE, FONT_FAMILY } from '../utils/constants.js';
|
|
5
5
|
import { isTextTruncated } from '../utils/canvasDrawHelpers.js';
|
|
@@ -706,10 +706,11 @@ var useTableInteraction = function (params) {
|
|
|
706
706
|
// 清理状态
|
|
707
707
|
setState(function (prev) {
|
|
708
708
|
var updates = {};
|
|
709
|
+
// 注释:不再在鼠标离开时停止框选,只在鼠标松开时停止
|
|
709
710
|
// 结束框选
|
|
710
|
-
if (prev.isSelecting) {
|
|
711
|
-
|
|
712
|
-
}
|
|
711
|
+
// if (prev.isSelecting) {
|
|
712
|
+
// updates.isSelecting = false;
|
|
713
|
+
// }
|
|
713
714
|
// 清理 hover
|
|
714
715
|
if (prev.hoverRowIndex !== null) {
|
|
715
716
|
updates.hoverRowIndex = null;
|
|
@@ -738,14 +739,25 @@ var useTableInteraction = function (params) {
|
|
|
738
739
|
// 显示右键菜单
|
|
739
740
|
menuShow({ event: e });
|
|
740
741
|
});
|
|
741
|
-
//
|
|
742
|
+
// 使用ref存储最新的状态和函数引用,避免闭包问题
|
|
743
|
+
var stateRef = useRef(state);
|
|
744
|
+
stateRef.current = state;
|
|
745
|
+
var scrollStateRef = useRef(scrollState);
|
|
746
|
+
scrollStateRef.current = scrollState;
|
|
747
|
+
var resizeStateRef = useRef(resizeState);
|
|
748
|
+
resizeStateRef.current = resizeState;
|
|
749
|
+
var columnRenderInfosRef = useRef(columnRenderInfos);
|
|
750
|
+
columnRenderInfosRef.current = columnRenderInfos;
|
|
751
|
+
// 在document上监听拖拽事件和框选事件,以支持鼠标移出canvas后继续操作
|
|
742
752
|
useEffect(function () {
|
|
743
753
|
var isDragging = scrollState.isDraggingVertical ||
|
|
744
754
|
scrollState.isDraggingHorizontal ||
|
|
745
755
|
(resizeState === null || resizeState === void 0 ? void 0 : resizeState.isResizing);
|
|
746
|
-
|
|
756
|
+
var isSelecting = state.isSelecting;
|
|
757
|
+
if (!isDragging && !isSelecting)
|
|
747
758
|
return;
|
|
748
759
|
var handleDocumentMouseMove = function (e) {
|
|
760
|
+
var _a;
|
|
749
761
|
// 获取当前实例的canvas
|
|
750
762
|
var canvas = canvasRef.current;
|
|
751
763
|
if (!canvas)
|
|
@@ -753,26 +765,50 @@ var useTableInteraction = function (params) {
|
|
|
753
765
|
var rect = canvas.getBoundingClientRect();
|
|
754
766
|
var x = e.clientX - rect.left;
|
|
755
767
|
var y = e.clientY - rect.top;
|
|
768
|
+
// 使用ref获取最新状态,避免闭包问题
|
|
769
|
+
var currentScrollState = scrollStateRef.current;
|
|
770
|
+
var currentResizeState = resizeStateRef.current;
|
|
771
|
+
var currentState = stateRef.current;
|
|
756
772
|
// 处理列宽调整拖拽
|
|
757
|
-
if ((
|
|
773
|
+
if ((currentResizeState === null || currentResizeState === void 0 ? void 0 : currentResizeState.isResizing) && updateResize) {
|
|
758
774
|
updateResize(x);
|
|
759
775
|
return;
|
|
760
776
|
}
|
|
761
777
|
// 处理垂直滚动条拖拽
|
|
762
|
-
if (
|
|
763
|
-
handleVerticalScrollDrag(y -
|
|
778
|
+
if (currentScrollState.isDraggingVertical) {
|
|
779
|
+
handleVerticalScrollDrag(y - currentScrollState.dragStartY);
|
|
764
780
|
return;
|
|
765
781
|
}
|
|
766
782
|
// 处理水平滚动条拖拽
|
|
767
|
-
if (
|
|
768
|
-
handleHorizontalScrollDrag(x -
|
|
783
|
+
if (currentScrollState.isDraggingHorizontal) {
|
|
784
|
+
handleHorizontalScrollDrag(x - currentScrollState.dragStartX);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
// 处理框选(在canvas外也能继续框选)
|
|
788
|
+
if (currentState.isSelecting) {
|
|
789
|
+
var cell = getCellFromPosition(x, y);
|
|
790
|
+
if (cell) {
|
|
791
|
+
// 检查是否在选择框列,如果是,则不更新框选
|
|
792
|
+
var isSelectionColumn = ((_a = columnRenderInfosRef.current[cell.col]) === null || _a === void 0 ? void 0 : _a.column.key) ===
|
|
793
|
+
"__selection__";
|
|
794
|
+
if (!isSelectionColumn) {
|
|
795
|
+
updateSelection(cell);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
769
798
|
}
|
|
770
799
|
};
|
|
771
800
|
var handleDocumentMouseUp = function () {
|
|
801
|
+
// 使用ref获取最新状态
|
|
802
|
+
var currentResizeState = resizeStateRef.current;
|
|
803
|
+
var currentState = stateRef.current;
|
|
772
804
|
// 结束列宽调整
|
|
773
|
-
if ((
|
|
805
|
+
if ((currentResizeState === null || currentResizeState === void 0 ? void 0 : currentResizeState.isResizing) && endResize) {
|
|
774
806
|
endResize();
|
|
775
807
|
}
|
|
808
|
+
// 结束框选
|
|
809
|
+
if (currentState.isSelecting) {
|
|
810
|
+
setState(function (prev) { return (__assign(__assign({}, prev), { isSelecting: false })); });
|
|
811
|
+
}
|
|
776
812
|
setScrollState(function (prev) { return (__assign(__assign({}, prev), { isDraggingVertical: false, isDraggingHorizontal: false })); });
|
|
777
813
|
};
|
|
778
814
|
document.addEventListener("mousemove", handleDocumentMouseMove);
|
|
@@ -781,16 +817,16 @@ var useTableInteraction = function (params) {
|
|
|
781
817
|
document.removeEventListener("mousemove", handleDocumentMouseMove);
|
|
782
818
|
document.removeEventListener("mouseup", handleDocumentMouseUp);
|
|
783
819
|
};
|
|
820
|
+
// 优化:减少依赖项,避免频繁重注册事件监听器
|
|
784
821
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
785
822
|
}, [
|
|
786
823
|
scrollState.isDraggingVertical,
|
|
787
824
|
scrollState.isDraggingHorizontal,
|
|
788
825
|
resizeState === null || resizeState === void 0 ? void 0 : resizeState.isResizing,
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
handleVerticalScrollDrag,
|
|
792
|
-
|
|
793
|
-
canvasRef,
|
|
826
|
+
state.isSelecting,
|
|
827
|
+
// 移除函数依赖,通过 useMemoizedFn 和 useRef 来稳定引用
|
|
828
|
+
// updateResize, endResize, handleVerticalScrollDrag, handleHorizontalScrollDrag,
|
|
829
|
+
// updateSelection, setState, getCellFromPosition, columnRenderInfos,
|
|
794
830
|
]);
|
|
795
831
|
return {
|
|
796
832
|
handleCanvasMouseDown: handleCanvasMouseDown,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, isValidElement, useEffect } from 'react';
|
|
1
|
+
import { useRef, useMemo, isValidElement, useEffect } from 'react';
|
|
2
2
|
import { useMemoizedFn } from 'ahooks';
|
|
3
3
|
import { IS_SUMMARY } from '../../table/constant.js';
|
|
4
4
|
import '../../_virtual/_tslib.js';
|
|
@@ -19,6 +19,13 @@ import { calculateSelectionState, calculateIconArea } from '../utils/interaction
|
|
|
19
19
|
* 表格渲染 Hook
|
|
20
20
|
*/
|
|
21
21
|
var useTableRender = function (params) {
|
|
22
|
+
// 添加节流机制,避免频繁重绘
|
|
23
|
+
var rafIdRef = useRef(null);
|
|
24
|
+
var isDrawingRef = useRef(false);
|
|
25
|
+
// 内存优化:缓存上次的渲染参数,避免不必要的重绘
|
|
26
|
+
var lastRenderParamsRef = useRef("");
|
|
27
|
+
// 内存优化:缓存Canvas context,避免重复获取
|
|
28
|
+
var canvasContextRef = useRef(null);
|
|
22
29
|
var canvasRef = params.canvasRef, processedDataSource = params.processedDataSource, columnRenderInfos = params.columnRenderInfos, columns = params.columns, state = params.state, scrollState = params.scrollState, rowSelection = params.rowSelection, containerWidth = params.containerWidth, containerHeight = params.containerHeight, headerHeight = params.headerHeight, baseHeaderHeight = params.baseHeaderHeight, rowHeight = params.rowHeight, bordered = params.bordered, striped = params.striped, _a = params.headerAlign, headerAlign = _a === void 0 ? "center" : _a, needVerticalScrollbar = params.needVerticalScrollbar, needHorizontalScrollbar = params.needHorizontalScrollbar, verticalScrollbarTop = params.verticalScrollbarTop, verticalScrollbarHeight = params.verticalScrollbarHeight, horizontalScrollbarLeft = params.horizontalScrollbarLeft, horizontalScrollbarWidth = params.horizontalScrollbarWidth, getRowKey = params.getRowKey, resizeState = params.resizeState, getColumnWidth = params.getColumnWidth, _b = params.RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_WIDTH = _b === void 0 ? 8 : _b, mergeCellMap = params.mergeCellMap, _c = params.hasSummaryRow, hasSummaryRow = _c === void 0 ? false : _c, fixedRowsCount = params.fixedRowsCount, fixedRowsConfig = params.fixedRowsConfig, _d = params.summaryFixed, summaryFixed = _d === void 0 ? false : _d;
|
|
23
30
|
// 判断是否是多级表头
|
|
24
31
|
var maxDepth = useMemo(function () { return getMaxDepth(columns); }, [columns]);
|
|
@@ -1323,157 +1330,245 @@ var useTableRender = function (params) {
|
|
|
1323
1330
|
renderBorder(true);
|
|
1324
1331
|
}
|
|
1325
1332
|
});
|
|
1326
|
-
//
|
|
1327
|
-
var
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1333
|
+
// 绘制表格核心函数
|
|
1334
|
+
var drawTableCore = useMemoizedFn(function () {
|
|
1335
|
+
try {
|
|
1336
|
+
var canvas = canvasRef.current;
|
|
1337
|
+
if (!canvas)
|
|
1338
|
+
return;
|
|
1339
|
+
// 防止重复绘制
|
|
1340
|
+
if (isDrawingRef.current)
|
|
1341
|
+
return;
|
|
1342
|
+
isDrawingRef.current = true;
|
|
1343
|
+
// 内存优化:复用context对象
|
|
1344
|
+
var ctx_1 = canvasContextRef.current;
|
|
1345
|
+
if (!ctx_1 || ctx_1.canvas !== canvas) {
|
|
1346
|
+
ctx_1 = canvas.getContext("2d");
|
|
1347
|
+
if (!ctx_1) {
|
|
1348
|
+
isDrawingRef.current = false;
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
canvasContextRef.current = ctx_1;
|
|
1352
|
+
}
|
|
1353
|
+
// 性能优化:检查是否需要重绘(基于关键参数hash)
|
|
1354
|
+
var renderParamsHash = JSON.stringify({
|
|
1355
|
+
width: containerWidth,
|
|
1356
|
+
height: containerHeight,
|
|
1357
|
+
scrollTop: scrollState.scrollTop,
|
|
1358
|
+
scrollLeft: scrollState.scrollLeft,
|
|
1359
|
+
dataLength: processedDataSource.length,
|
|
1360
|
+
columnsLength: columnRenderInfos.length,
|
|
1361
|
+
hoverRow: state.hoverRowIndex,
|
|
1362
|
+
selection: state.cellSelection,
|
|
1363
|
+
resizing: resizeState === null || resizeState === void 0 ? void 0 : resizeState.isResizing,
|
|
1364
|
+
});
|
|
1365
|
+
// 如果关键参数没有变化,跳过重绘
|
|
1366
|
+
if (lastRenderParamsRef.current === renderParamsHash) {
|
|
1367
|
+
isDrawingRef.current = false;
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
lastRenderParamsRef.current = renderParamsHash;
|
|
1371
|
+
// 设置 canvas 尺寸
|
|
1372
|
+
var displayWidth_1 = containerWidth;
|
|
1373
|
+
var displayHeight_1 = containerHeight;
|
|
1374
|
+
canvas.width = displayWidth_1 * dpr;
|
|
1375
|
+
canvas.height = displayHeight_1 * dpr;
|
|
1376
|
+
canvas.style.width = "".concat(displayWidth_1, "px");
|
|
1377
|
+
canvas.style.height = "".concat(displayHeight_1, "px");
|
|
1378
|
+
ctx_1.scale(dpr, dpr);
|
|
1379
|
+
// 优化文字渲染质量,防止模糊
|
|
1380
|
+
ctx_1.imageSmoothingEnabled = true;
|
|
1381
|
+
ctx_1.imageSmoothingQuality = "high";
|
|
1382
|
+
// 清空画布
|
|
1383
|
+
ctx_1.clearRect(0, 0, displayWidth_1, displayHeight_1);
|
|
1384
|
+
// 绘制背景
|
|
1385
|
+
ctx_1.fillStyle = COLORS.white;
|
|
1386
|
+
ctx_1.fillRect(0, 0, displayWidth_1, displayHeight_1);
|
|
1387
|
+
// 计算固定行的数量(支持多种配置方式)
|
|
1388
|
+
var fixedTopRowsCount_1 = fixedRowsCount || (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.topCount) || 0;
|
|
1389
|
+
var fixedBottomRowsCount_1 = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.bottomCount) || 0;
|
|
1390
|
+
// 计算固定行占用的高度
|
|
1391
|
+
var fixedTopRowsHeight = fixedTopRowsCount_1 * rowHeight;
|
|
1392
|
+
var fixedBottomRowsHeight = fixedBottomRowsCount_1 * rowHeight;
|
|
1393
|
+
var fixedSummaryRowHeight = summaryFixed && hasSummaryRow ? rowHeight : 0;
|
|
1394
|
+
// 计算可视区域的数据区域高度(不包括表头、固定行和固定合计行)
|
|
1395
|
+
var dataAreaHeight = displayHeight_1 -
|
|
1396
|
+
headerHeight -
|
|
1397
|
+
fixedTopRowsHeight -
|
|
1398
|
+
fixedBottomRowsHeight -
|
|
1399
|
+
(needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
|
|
1400
|
+
fixedSummaryRowHeight;
|
|
1401
|
+
// 计算可视区域(从固定行之后开始)
|
|
1402
|
+
var scrollStartRow_1 = fixedTopRowsCount_1 + Math.floor(scrollState.scrollTop / rowHeight);
|
|
1403
|
+
var scrollEndRow_1 = Math.min(fixedTopRowsCount_1 +
|
|
1404
|
+
Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length -
|
|
1405
|
+
fixedBottomRowsCount_1 -
|
|
1406
|
+
(summaryFixed && hasSummaryRow ? 1 : 0) // 如果合计行固定,需要排除它
|
|
1407
|
+
);
|
|
1408
|
+
// 关键修复:向前扩展渲染范围,检查是否有合并单元格延伸到可视区域
|
|
1409
|
+
// 策略:对每一列独立检查,找到该列最靠前的需要渲染的主合并单元格
|
|
1410
|
+
if (mergeCellMap && scrollStartRow_1 > fixedTopRowsCount_1) {
|
|
1411
|
+
var originalStartRow = scrollStartRow_1;
|
|
1412
|
+
var minStartRow = scrollStartRow_1;
|
|
1413
|
+
// 对每一列独立检查
|
|
1414
|
+
for (var colIndex = 0; colIndex < columnRenderInfos.length; colIndex++) {
|
|
1415
|
+
// 向前查找这一列的主合并单元格,最多向前查找20行
|
|
1416
|
+
for (var checkRow = originalStartRow - 1; checkRow >= fixedTopRowsCount_1 && checkRow >= originalStartRow - 20; checkRow--) {
|
|
1417
|
+
var mergeCellKey = "".concat(checkRow, "-").concat(colIndex);
|
|
1418
|
+
var mergeInfo = mergeCellMap.get(mergeCellKey);
|
|
1419
|
+
// 如果找到主单元格(不是被合并的单元格)
|
|
1420
|
+
if (mergeInfo && !mergeInfo.skip && mergeInfo.rowSpan > 1) {
|
|
1421
|
+
// 计算合并单元格的底部行索引
|
|
1422
|
+
var mergeEndRow = checkRow + mergeInfo.rowSpan - 1;
|
|
1423
|
+
// 如果这个合并单元格延伸到可视区域
|
|
1424
|
+
if (mergeEndRow >= originalStartRow) {
|
|
1425
|
+
// 更新最小起始行
|
|
1426
|
+
minStartRow = Math.min(minStartRow, checkRow);
|
|
1427
|
+
}
|
|
1428
|
+
// 找到这一列的主单元格后,停止向前查找该列
|
|
1429
|
+
break;
|
|
1390
1430
|
}
|
|
1391
|
-
//
|
|
1392
|
-
|
|
1431
|
+
// 如果是skip的单元格或没有合并信息,继续向前查找
|
|
1432
|
+
// 因为主单元格可能在更前面
|
|
1393
1433
|
}
|
|
1394
|
-
// 如果是skip的单元格或没有合并信息,继续向前查找
|
|
1395
|
-
// 因为主单元格可能在更前面
|
|
1396
1434
|
}
|
|
1435
|
+
scrollStartRow_1 = minStartRow;
|
|
1397
1436
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
// 1. 绘制可滚动区域的数据行(包括边框)
|
|
1401
|
-
renderWithFixedLayer(ctx,
|
|
1402
|
-
// 渲染行内容
|
|
1403
|
-
function (onlyFixed) {
|
|
1404
|
-
for (var i = scrollStartRow; i < scrollEndRow; i++) {
|
|
1405
|
-
drawRow(ctx, i, displayWidth, displayHeight, onlyFixed);
|
|
1406
|
-
}
|
|
1407
|
-
},
|
|
1408
|
-
// 渲染边框
|
|
1409
|
-
function (onlyFixed) {
|
|
1410
|
-
drawBorders(ctx, scrollStartRow, scrollEndRow, displayWidth, displayHeight, onlyFixed);
|
|
1411
|
-
});
|
|
1412
|
-
// 2. 绘制固定的顶部行(如果有)
|
|
1413
|
-
if (fixedTopRowsCount > 0) {
|
|
1414
|
-
ctx.save();
|
|
1415
|
-
renderWithFixedLayer(ctx,
|
|
1437
|
+
// 1. 绘制可滚动区域的数据行(包括边框)
|
|
1438
|
+
renderWithFixedLayer(ctx_1,
|
|
1416
1439
|
// 渲染行内容
|
|
1417
1440
|
function (onlyFixed) {
|
|
1418
|
-
for (var i =
|
|
1419
|
-
|
|
1441
|
+
for (var i = scrollStartRow_1; i < scrollEndRow_1; i++) {
|
|
1442
|
+
drawRow(ctx_1, i, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1420
1443
|
}
|
|
1421
1444
|
},
|
|
1422
1445
|
// 渲染边框
|
|
1423
1446
|
function (onlyFixed) {
|
|
1424
|
-
|
|
1447
|
+
drawBorders(ctx_1, scrollStartRow_1, scrollEndRow_1, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1425
1448
|
});
|
|
1426
|
-
|
|
1449
|
+
// 2. 绘制固定的顶部行(如果有)
|
|
1450
|
+
if (fixedTopRowsCount_1 > 0) {
|
|
1451
|
+
ctx_1.save();
|
|
1452
|
+
renderWithFixedLayer(ctx_1,
|
|
1453
|
+
// 渲染行内容
|
|
1454
|
+
function (onlyFixed) {
|
|
1455
|
+
for (var i = 0; i < fixedTopRowsCount_1; i++) {
|
|
1456
|
+
drawFixedTopRow(ctx_1, i, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
// 渲染边框
|
|
1460
|
+
function (onlyFixed) {
|
|
1461
|
+
drawFixedTopRowsBorder(ctx_1, fixedTopRowsCount_1, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1462
|
+
});
|
|
1463
|
+
ctx_1.restore();
|
|
1464
|
+
}
|
|
1465
|
+
// 3. 绘制固定的底部行(如果有,不包括合计行)
|
|
1466
|
+
if (fixedBottomRowsCount_1 > 0) {
|
|
1467
|
+
ctx_1.save();
|
|
1468
|
+
var bottomRowsStartIndex_1 = processedDataSource.length -
|
|
1469
|
+
fixedBottomRowsCount_1 -
|
|
1470
|
+
(summaryFixed && hasSummaryRow ? 1 : 0);
|
|
1471
|
+
renderWithFixedLayer(ctx_1,
|
|
1472
|
+
// 渲染行内容
|
|
1473
|
+
function (onlyFixed) {
|
|
1474
|
+
for (var i = 0; i < fixedBottomRowsCount_1; i++) {
|
|
1475
|
+
var rowIndex = bottomRowsStartIndex_1 + i;
|
|
1476
|
+
drawFixedBottomRow(ctx_1, rowIndex, i, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1477
|
+
}
|
|
1478
|
+
},
|
|
1479
|
+
// 渲染边框
|
|
1480
|
+
function (onlyFixed) {
|
|
1481
|
+
drawFixedBottomRowsBorder(ctx_1, fixedBottomRowsCount_1, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1482
|
+
});
|
|
1483
|
+
ctx_1.restore();
|
|
1484
|
+
}
|
|
1485
|
+
// 7. 绘制固定列阴影
|
|
1486
|
+
drawFixedColumns(ctx_1, scrollStartRow_1, scrollEndRow_1, displayWidth_1, displayHeight_1);
|
|
1487
|
+
// 4. 绘制固定合计行(如果启用)
|
|
1488
|
+
if (summaryFixed && hasSummaryRow) {
|
|
1489
|
+
var summaryRowIndex_1 = processedDataSource.length - 1;
|
|
1490
|
+
var summaryRowY_1 = displayHeight_1 -
|
|
1491
|
+
(needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
|
|
1492
|
+
rowHeight;
|
|
1493
|
+
ctx_1.save();
|
|
1494
|
+
renderWithFixedLayer(ctx_1,
|
|
1495
|
+
// 渲染行内容
|
|
1496
|
+
function (onlyFixed) {
|
|
1497
|
+
drawFixedSummaryRow(ctx_1, summaryRowIndex_1, summaryRowY_1, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1498
|
+
},
|
|
1499
|
+
// 渲染边框
|
|
1500
|
+
function (onlyFixed) {
|
|
1501
|
+
drawFixedSummaryRowBorder(ctx_1, summaryRowIndex_1, summaryRowY_1, displayWidth_1, displayHeight_1, onlyFixed);
|
|
1502
|
+
});
|
|
1503
|
+
ctx_1.restore();
|
|
1504
|
+
}
|
|
1505
|
+
// 6. 绘制表头(最后绘制,确保始终在最上层,不会被覆盖)
|
|
1506
|
+
drawHeader(ctx_1, displayWidth_1, displayHeight_1);
|
|
1507
|
+
// 绘制滚动条(最后绘制,在表头之上)
|
|
1508
|
+
drawScrollbars(ctx_1, displayWidth_1, displayHeight_1);
|
|
1509
|
+
// 绘制框选区域
|
|
1510
|
+
if (state.cellSelection) {
|
|
1511
|
+
drawCellSelection(ctx_1, displayWidth_1, displayHeight_1);
|
|
1512
|
+
}
|
|
1513
|
+
// 重置绘制标志
|
|
1514
|
+
isDrawingRef.current = false;
|
|
1427
1515
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1516
|
+
catch (error) {
|
|
1517
|
+
// 错误边界:捕获渲染异常,防止组件崩溃
|
|
1518
|
+
console.error("CanvasTable: 渲染过程中发生异常:", error);
|
|
1519
|
+
// 重置状态,防止卡死
|
|
1520
|
+
isDrawingRef.current = false;
|
|
1521
|
+
// 清理可能的异常状态
|
|
1522
|
+
try {
|
|
1523
|
+
var canvas = canvasRef.current;
|
|
1524
|
+
if (canvas) {
|
|
1525
|
+
var ctx = canvas.getContext("2d");
|
|
1526
|
+
if (ctx) {
|
|
1527
|
+
// 清空画布,显示错误状态
|
|
1528
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1529
|
+
ctx.fillStyle = "#f5f5f5";
|
|
1530
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1531
|
+
// 显示错误提示
|
|
1532
|
+
ctx.fillStyle = "#ff4d4f";
|
|
1533
|
+
ctx.font = "14px Arial";
|
|
1534
|
+
ctx.textAlign = "center";
|
|
1535
|
+
ctx.fillText("表格渲染异常,请检查数据或刷新页面", canvas.width / 2, canvas.height / 2);
|
|
1536
|
+
}
|
|
1440
1537
|
}
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
}
|
|
1448
|
-
// 7. 绘制固定列阴影
|
|
1449
|
-
drawFixedColumns(ctx, scrollStartRow, scrollEndRow, displayWidth, displayHeight);
|
|
1450
|
-
// 4. 绘制固定合计行(如果启用)
|
|
1451
|
-
if (summaryFixed && hasSummaryRow) {
|
|
1452
|
-
var summaryRowIndex_1 = processedDataSource.length - 1;
|
|
1453
|
-
var summaryRowY_1 = displayHeight -
|
|
1454
|
-
(needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
|
|
1455
|
-
rowHeight;
|
|
1456
|
-
ctx.save();
|
|
1457
|
-
renderWithFixedLayer(ctx,
|
|
1458
|
-
// 渲染行内容
|
|
1459
|
-
function (onlyFixed) {
|
|
1460
|
-
drawFixedSummaryRow(ctx, summaryRowIndex_1, summaryRowY_1, displayWidth, displayHeight, onlyFixed);
|
|
1461
|
-
},
|
|
1462
|
-
// 渲染边框
|
|
1463
|
-
function (onlyFixed) {
|
|
1464
|
-
drawFixedSummaryRowBorder(ctx, summaryRowIndex_1, summaryRowY_1, displayWidth, displayHeight, onlyFixed);
|
|
1465
|
-
});
|
|
1466
|
-
ctx.restore();
|
|
1538
|
+
}
|
|
1539
|
+
catch (fallbackError) {
|
|
1540
|
+
console.error("CanvasTable: 错误恢复失败:", fallbackError);
|
|
1541
|
+
}
|
|
1542
|
+
// 可以触发错误回调,通知外部组件
|
|
1543
|
+
// props.onRenderError?.(error);
|
|
1467
1544
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
drawCellSelection(ctx, displayWidth, displayHeight);
|
|
1545
|
+
});
|
|
1546
|
+
// 节流的绘制函数,使用 requestAnimationFrame
|
|
1547
|
+
var drawTable = useMemoizedFn(function () {
|
|
1548
|
+
// 如果已经有pending的绘制请求,取消之前的
|
|
1549
|
+
if (rafIdRef.current !== null) {
|
|
1550
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
1475
1551
|
}
|
|
1552
|
+
// 使用 requestAnimationFrame 进行节流
|
|
1553
|
+
rafIdRef.current = requestAnimationFrame(function () {
|
|
1554
|
+
drawTableCore();
|
|
1555
|
+
rafIdRef.current = null;
|
|
1556
|
+
});
|
|
1476
1557
|
});
|
|
1558
|
+
// 清理函数 - 释放资源
|
|
1559
|
+
useEffect(function () {
|
|
1560
|
+
return function () {
|
|
1561
|
+
// 清理动画帧
|
|
1562
|
+
if (rafIdRef.current !== null) {
|
|
1563
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
1564
|
+
rafIdRef.current = null;
|
|
1565
|
+
}
|
|
1566
|
+
// 清理缓存的引用,释放内存
|
|
1567
|
+
canvasContextRef.current = null;
|
|
1568
|
+
lastRenderParamsRef.current = "";
|
|
1569
|
+
isDrawingRef.current = false;
|
|
1570
|
+
};
|
|
1571
|
+
}, []);
|
|
1477
1572
|
// 监听变化,自动重绘
|
|
1478
1573
|
useEffect(function () {
|
|
1479
1574
|
drawTable();
|
|
@@ -1493,7 +1588,9 @@ var useTableRender = function (params) {
|
|
|
1493
1588
|
columnRenderInfos,
|
|
1494
1589
|
bordered,
|
|
1495
1590
|
striped,
|
|
1496
|
-
resizeState,
|
|
1591
|
+
resizeState === null || resizeState === void 0 ? void 0 : resizeState.isResizing,
|
|
1592
|
+
resizeState === null || resizeState === void 0 ? void 0 : resizeState.resizingColumnIndex,
|
|
1593
|
+
resizeState === null || resizeState === void 0 ? void 0 : resizeState.hoverResizeColumnIndex,
|
|
1497
1594
|
drawTable,
|
|
1498
1595
|
]);
|
|
1499
1596
|
return {
|
package/dist/es/table/hooks.js
CHANGED
|
@@ -390,7 +390,9 @@ var useNumKeys = function (columns) {
|
|
|
390
390
|
var numKeys = useMemo(function () {
|
|
391
391
|
var leafColumns = getLeafColumns(columns);
|
|
392
392
|
return Array.from(new Set(leafColumns
|
|
393
|
-
.filter(function (item) {
|
|
393
|
+
.filter(function (item) {
|
|
394
|
+
return item.precision !== undefined || item.thousand !== undefined;
|
|
395
|
+
})
|
|
394
396
|
.map(function (item) { return item.key; })));
|
|
395
397
|
}, [columns]);
|
|
396
398
|
return numKeys;
|
|
@@ -415,30 +417,66 @@ function useAutoMerge(dataSource, columns, _a) {
|
|
|
415
417
|
.filter(function (item) { return item.isDimensionSum; })
|
|
416
418
|
.map(function (item) { return item.key; });
|
|
417
419
|
}, [columns]);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
);
|
|
428
|
-
return
|
|
420
|
+
// 添加性能监控和异常处理
|
|
421
|
+
var processDataWithOptimization = useCallback(function (sourceData, mergeKeysParam) {
|
|
422
|
+
try {
|
|
423
|
+
// 数据有效性检查
|
|
424
|
+
if (!Array.isArray(sourceData)) {
|
|
425
|
+
console.warn("CanvasTable: 数据源不是数组类型");
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
if (!Array.isArray(mergeKeysParam) || mergeKeysParam.length === 0) {
|
|
429
|
+
console.warn("CanvasTable: 合并字段配置无效");
|
|
430
|
+
return sourceData;
|
|
431
|
+
}
|
|
432
|
+
// 性能检查:数据量过大时启用优化策略
|
|
433
|
+
var dataSize = sourceData.length;
|
|
434
|
+
var mergeKeysCount = mergeKeysParam.length;
|
|
435
|
+
var complexity = dataSize * mergeKeysCount;
|
|
436
|
+
// 时间复杂度过高时的优化策略
|
|
437
|
+
if (complexity > 10000) {
|
|
438
|
+
console.warn("CanvasTable: \u68C0\u6D4B\u5230\u9AD8\u590D\u6742\u5EA6\u6570\u636E\u5904\u7406 (".concat(complexity, ")\uFF0C\u5EFA\u8BAE\u4F18\u5316\u6570\u636E\u91CF\u6216\u51CF\u5C11\u5408\u5E76\u5B57\u6BB5"));
|
|
439
|
+
// 对于超大数据,可以考虑分页处理或虚拟化
|
|
440
|
+
if (dataSize > 5000) {
|
|
441
|
+
console.warn("CanvasTable: \u6570\u636E\u91CF\u8FC7\u5927 (".concat(dataSize, " \u884C)\uFF0C\u6027\u80FD\u53EF\u80FD\u53D7\u5F71\u54CD"));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
var startTime = performance.now();
|
|
445
|
+
var result = flattenRecordsOptimized(__spreadArray([], sourceData, true), mergeKeysParam, dimensionSummaryKeys, summaryKeys, isDimensionDynamic, order, dimensionCustomSumKeys, columns // 添加 columns 参数,支持自定义排序
|
|
446
|
+
);
|
|
447
|
+
var endTime = performance.now();
|
|
448
|
+
var duration = endTime - startTime;
|
|
449
|
+
// 性能监控:超过阈值时警告
|
|
450
|
+
if (duration > 100) {
|
|
451
|
+
console.warn("CanvasTable: \u6570\u636E\u5904\u7406\u8017\u65F6\u8F83\u957F (".concat(duration.toFixed(2), "ms)\uFF0C\u5EFA\u8BAE\u4F18\u5316"));
|
|
429
452
|
}
|
|
453
|
+
// 结果有效性检查
|
|
454
|
+
if (!Array.isArray(result)) {
|
|
455
|
+
console.error("CanvasTable: 数据处理结果异常");
|
|
456
|
+
return sourceData; // 降级返回原始数据
|
|
457
|
+
}
|
|
458
|
+
return result;
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
console.error("CanvasTable: 数据处理过程中发生异常:", error);
|
|
462
|
+
// 异常降级:返回原始数据,避免组件崩溃
|
|
463
|
+
return sourceData;
|
|
430
464
|
}
|
|
431
465
|
}, [
|
|
432
|
-
dataSource,
|
|
433
|
-
isAutoMerge,
|
|
434
|
-
mergeKeys,
|
|
435
466
|
dimensionSummaryKeys,
|
|
436
467
|
summaryKeys,
|
|
437
468
|
isDimensionDynamic,
|
|
438
469
|
order,
|
|
439
470
|
dimensionCustomSumKeys,
|
|
440
|
-
columns,
|
|
471
|
+
columns,
|
|
441
472
|
]);
|
|
473
|
+
var newDataSource = useMemo(function () {
|
|
474
|
+
if (!isAutoMerge)
|
|
475
|
+
return dataSource;
|
|
476
|
+
if (!mergeKeys)
|
|
477
|
+
return dataSource;
|
|
478
|
+
return processDataWithOptimization(dataSource || [], mergeKeys);
|
|
479
|
+
}, [dataSource, isAutoMerge, mergeKeys, processDataWithOptimization]);
|
|
442
480
|
var newColumns = useMemo(function () {
|
|
443
481
|
var _columns = columns;
|
|
444
482
|
// 递归处理多级表头,自动补充render/合并/编辑等逻辑
|