zmdms-webui 2.3.9 → 2.4.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/es/canvastable/canvasTable.js +3 -2
- package/dist/es/canvastable/hooks/useScroll.js +154 -37
- package/dist/es/canvastable/hooks/useSummaryRow.js +5 -1
- package/dist/es/canvastable/hooks/useTableInteraction.js +54 -20
- 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({
|
|
@@ -289,6 +289,7 @@ function CanvasTable(props) {
|
|
|
289
289
|
containerHeight: containerHeight,
|
|
290
290
|
scrollbarSize: SCROLLBAR_SIZE,
|
|
291
291
|
canvasRef: canvasRef,
|
|
292
|
+
containerRef: containerRef,
|
|
292
293
|
totalHeight: totalHeight,
|
|
293
294
|
totalWidth: totalWidth,
|
|
294
295
|
// 使用scrollbarMetrics的显示相关属性(需要随滚动更新)
|
|
@@ -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,17 +97,35 @@ 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
|
-
|
|
111
|
-
|
|
118
|
+
// 支持 Shift + 鼠标滚轮进行水平滚动
|
|
119
|
+
if (e.shiftKey) {
|
|
120
|
+
// 按住 Shift 键时,将垂直滚动转换为水平滚动
|
|
121
|
+
pendingDeltaRef.current.deltaX += e.deltaY;
|
|
122
|
+
pendingDeltaRef.current.deltaY += 0;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// 正常滚动逻辑
|
|
126
|
+
pendingDeltaRef.current.deltaX += e.deltaX;
|
|
127
|
+
pendingDeltaRef.current.deltaY += e.deltaY;
|
|
128
|
+
}
|
|
112
129
|
// 如果已经有pending的动画帧,不需要再次请求
|
|
113
130
|
if (rafIdRef.current !== null) {
|
|
114
131
|
return;
|
|
@@ -134,39 +151,18 @@ var useTableScroll = function (params) {
|
|
|
134
151
|
});
|
|
135
152
|
});
|
|
136
153
|
};
|
|
137
|
-
|
|
154
|
+
container.addEventListener("wheel", handleNativeContainerWheel, {
|
|
155
|
+
passive: false,
|
|
156
|
+
});
|
|
138
157
|
return function () {
|
|
139
|
-
|
|
158
|
+
container.removeEventListener("wheel", handleNativeContainerWheel);
|
|
140
159
|
// 清理pending的动画帧
|
|
141
160
|
if (rafIdRef.current !== null) {
|
|
142
161
|
cancelAnimationFrame(rafIdRef.current);
|
|
143
162
|
rafIdRef.current = null;
|
|
144
163
|
}
|
|
145
164
|
};
|
|
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]);
|
|
165
|
+
}, [containerRef, maxScrollTop, maxScrollLeft, onScroll, setScrollState]);
|
|
170
166
|
// 全局鼠标事件处理(拖拽滚动条)
|
|
171
167
|
useEffect(function () {
|
|
172
168
|
var handleGlobalMouseUp = function () {
|
|
@@ -191,7 +187,128 @@ var useTableScroll = function (params) {
|
|
|
191
187
|
setScrollState: setScrollState,
|
|
192
188
|
};
|
|
193
189
|
};
|
|
194
|
-
|
|
190
|
+
var useSelectionBoundaryScroll = function (params) {
|
|
191
|
+
var containerRef = params.containerRef, isSelecting = params.isSelecting,
|
|
192
|
+
// scrollState,
|
|
193
|
+
setScrollState = params.setScrollState, maxScrollTop = params.maxScrollTop, maxScrollLeft = params.maxScrollLeft, onScroll = params.onScroll, getCellFromPosition = params.getCellFromPosition, updateSelection = params.updateSelection, columnRenderInfos = params.columnRenderInfos;
|
|
194
|
+
var scrollIntervalRef = useRef(null);
|
|
195
|
+
var currentMousePosRef = useRef(null);
|
|
196
|
+
var SCROLL_BOUNDARY_SIZE = 50; // 触发滚动的边界距离
|
|
197
|
+
var SCROLL_SPEED = 10; // 每次滚动的距离
|
|
198
|
+
// 检查边界滚动
|
|
199
|
+
var checkBoundaryScroll = useMemo(function () {
|
|
200
|
+
return function (mouseX, mouseY) {
|
|
201
|
+
if (!containerRef.current || !isSelecting)
|
|
202
|
+
return;
|
|
203
|
+
var rect = containerRef.current.getBoundingClientRect();
|
|
204
|
+
var containerWidth = rect.width;
|
|
205
|
+
var containerHeight = rect.height;
|
|
206
|
+
var scrollDeltaX = 0;
|
|
207
|
+
var scrollDeltaY = 0;
|
|
208
|
+
// 检查水平边界
|
|
209
|
+
if (mouseX < SCROLL_BOUNDARY_SIZE) {
|
|
210
|
+
// 接近左边界,向左滚动
|
|
211
|
+
scrollDeltaX = -SCROLL_SPEED;
|
|
212
|
+
}
|
|
213
|
+
else if (mouseX > containerWidth - SCROLL_BOUNDARY_SIZE) {
|
|
214
|
+
// 接近右边界,向右滚动
|
|
215
|
+
scrollDeltaX = SCROLL_SPEED;
|
|
216
|
+
}
|
|
217
|
+
// 检查垂直边界
|
|
218
|
+
if (mouseY < SCROLL_BOUNDARY_SIZE) {
|
|
219
|
+
// 接近顶部边界,向上滚动
|
|
220
|
+
scrollDeltaY = -SCROLL_SPEED;
|
|
221
|
+
}
|
|
222
|
+
else if (mouseY > containerHeight - SCROLL_BOUNDARY_SIZE) {
|
|
223
|
+
// 接近底部边界,向下滚动
|
|
224
|
+
scrollDeltaY = SCROLL_SPEED;
|
|
225
|
+
}
|
|
226
|
+
// 保存当前鼠标位置(用于滚动时更新框选)
|
|
227
|
+
currentMousePosRef.current = { x: mouseX, y: mouseY };
|
|
228
|
+
// 如果需要滚动
|
|
229
|
+
if (scrollDeltaX !== 0 || scrollDeltaY !== 0) {
|
|
230
|
+
// 清除之前的定时器
|
|
231
|
+
if (scrollIntervalRef.current) {
|
|
232
|
+
clearInterval(scrollIntervalRef.current);
|
|
233
|
+
}
|
|
234
|
+
// 开始持续滚动
|
|
235
|
+
scrollIntervalRef.current = setInterval(function () {
|
|
236
|
+
setScrollState(function (prev) {
|
|
237
|
+
var _a;
|
|
238
|
+
var newScrollLeft = Math.max(0, Math.min(maxScrollLeft, prev.scrollLeft + scrollDeltaX));
|
|
239
|
+
var newScrollTop = Math.max(0, Math.min(maxScrollTop, prev.scrollTop + scrollDeltaY));
|
|
240
|
+
if (newScrollLeft !== prev.scrollLeft ||
|
|
241
|
+
newScrollTop !== prev.scrollTop) {
|
|
242
|
+
onScroll === null || onScroll === void 0 ? void 0 : onScroll(newScrollLeft, newScrollTop);
|
|
243
|
+
// 滚动后检查鼠标位置的单元格并更新框选
|
|
244
|
+
if (getCellFromPosition &&
|
|
245
|
+
updateSelection &&
|
|
246
|
+
columnRenderInfos &&
|
|
247
|
+
currentMousePosRef.current) {
|
|
248
|
+
var _b = currentMousePosRef.current, x = _b.x, y = _b.y;
|
|
249
|
+
var cell = getCellFromPosition(x, y);
|
|
250
|
+
if (cell) {
|
|
251
|
+
// 检查是否在选择框列,如果是,则不更新框选
|
|
252
|
+
var isSelectionColumn = ((_a = columnRenderInfos[cell.col]) === null || _a === void 0 ? void 0 : _a.column.key) === "__selection__";
|
|
253
|
+
if (!isSelectionColumn) {
|
|
254
|
+
updateSelection(cell);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return __assign(__assign({}, prev), { scrollLeft: newScrollLeft, scrollTop: newScrollTop });
|
|
259
|
+
}
|
|
260
|
+
return prev;
|
|
261
|
+
});
|
|
262
|
+
}, 50); // 每50毫秒滚动一次
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
// 停止滚动
|
|
266
|
+
if (scrollIntervalRef.current) {
|
|
267
|
+
clearInterval(scrollIntervalRef.current);
|
|
268
|
+
scrollIntervalRef.current = null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}, [
|
|
273
|
+
containerRef,
|
|
274
|
+
isSelecting,
|
|
275
|
+
setScrollState,
|
|
276
|
+
maxScrollTop,
|
|
277
|
+
maxScrollLeft,
|
|
278
|
+
onScroll,
|
|
279
|
+
getCellFromPosition,
|
|
280
|
+
updateSelection,
|
|
281
|
+
columnRenderInfos,
|
|
282
|
+
]);
|
|
283
|
+
// 停止边界滚动
|
|
284
|
+
var stopBoundaryScroll = useMemo(function () {
|
|
285
|
+
return function () {
|
|
286
|
+
if (scrollIntervalRef.current) {
|
|
287
|
+
clearInterval(scrollIntervalRef.current);
|
|
288
|
+
scrollIntervalRef.current = null;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}, []);
|
|
292
|
+
// 当框选结束时停止滚动
|
|
293
|
+
useEffect(function () {
|
|
294
|
+
if (!isSelecting) {
|
|
295
|
+
stopBoundaryScroll();
|
|
296
|
+
}
|
|
297
|
+
}, [isSelecting, stopBoundaryScroll]);
|
|
298
|
+
// 组件卸载时清理定时器
|
|
299
|
+
useEffect(function () {
|
|
300
|
+
return function () {
|
|
301
|
+
if (scrollIntervalRef.current) {
|
|
302
|
+
clearInterval(scrollIntervalRef.current);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}, []);
|
|
306
|
+
return {
|
|
307
|
+
checkBoundaryScroll: checkBoundaryScroll,
|
|
308
|
+
stopBoundaryScroll: stopBoundaryScroll,
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
// ==================== Hook 4: 滚动位置重置 ====================
|
|
195
312
|
/**
|
|
196
313
|
* 滚动位置重置 Hook
|
|
197
314
|
*
|
|
@@ -249,4 +366,4 @@ var useScrollReset = function (params) {
|
|
|
249
366
|
]);
|
|
250
367
|
};
|
|
251
368
|
|
|
252
|
-
export { useScrollReset, useScrollbarMetrics, useTableScroll };
|
|
369
|
+
export { useScrollReset, useScrollbarMetrics, useSelectionBoundaryScroll, useTableScroll };
|
|
@@ -22,6 +22,8 @@ var useSummaryRow = function (params) {
|
|
|
22
22
|
if (!hasSummary)
|
|
23
23
|
return null;
|
|
24
24
|
var summaryRecord = {};
|
|
25
|
+
// 原始值,为转为千分符的
|
|
26
|
+
var summaryOriginalValues = {};
|
|
25
27
|
// 找到第一个列(包括序号列、选择框列)
|
|
26
28
|
var firstColIndex = leafColumns.length > 0 ? 0 : -1;
|
|
27
29
|
leafColumns.forEach(function (column, colIndex) {
|
|
@@ -43,9 +45,11 @@ var useSummaryRow = function (params) {
|
|
|
43
45
|
calculatedSum = sum_1;
|
|
44
46
|
// 应用格式化(仅在没有自定义回调或回调返回数值时)
|
|
45
47
|
var formattedSum = calculatedSum;
|
|
48
|
+
summaryOriginalValues[dataIndex] = calculatedSum;
|
|
46
49
|
if (typeof calculatedSum === "number") {
|
|
47
50
|
if (column.precision !== undefined) {
|
|
48
51
|
formattedSum = exactRound(calculatedSum, column.precision);
|
|
52
|
+
summaryOriginalValues[dataIndex] = calculatedSum;
|
|
49
53
|
}
|
|
50
54
|
if (column.thousand) {
|
|
51
55
|
formattedSum = addThousedSeparator(formattedSum);
|
|
@@ -64,7 +68,7 @@ var useSummaryRow = function (params) {
|
|
|
64
68
|
leafColumns.forEach(function (column) {
|
|
65
69
|
if (column.totalCalcCallback) {
|
|
66
70
|
var dataIndex = column.dataIndex || column.key;
|
|
67
|
-
summaryRecord[dataIndex] = column.totalCalcCallback(
|
|
71
|
+
summaryRecord[dataIndex] = column.totalCalcCallback(summaryOriginalValues[dataIndex], summaryOriginalValues);
|
|
68
72
|
}
|
|
69
73
|
});
|
|
70
74
|
summaryRecord[IS_SUMMARY] = true;
|
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
import { useSelectionBoundaryScroll } from './useScroll.js';
|
|
4
5
|
import { FONT_SIZE, FONT_FAMILY } from '../utils/constants.js';
|
|
5
6
|
import { isTextTruncated } from '../utils/canvasDrawHelpers.js';
|
|
6
7
|
import { getMaxDepth, flattenHeaders, getLeafColumns } from '../utils/multiHeaderHelpers.js';
|
|
@@ -13,7 +14,7 @@ import { calculateSelectionState, toggleSelectAll, handleSortClick, calculateIco
|
|
|
13
14
|
* 表格交互事件处理 Hook
|
|
14
15
|
*/
|
|
15
16
|
var useTableInteraction = function (params) {
|
|
16
|
-
var state = params.state, setState = params.setState, scrollState = params.scrollState, setScrollState = params.setScrollState, processedDataSource = params.processedDataSource, columnRenderInfos = params.columnRenderInfos, _a = params.columns, columns = _a === void 0 ? [] : _a, rowSelection = params.rowSelection, rowHeight = params.rowHeight, headerHeight = params.headerHeight, containerWidth = params.containerWidth, containerHeight = params.containerHeight, scrollbarSize = params.scrollbarSize, canvasRef = params.canvasRef, needVerticalScrollbar = params.needVerticalScrollbar, needHorizontalScrollbar = params.needHorizontalScrollbar, verticalScrollbarTop = params.verticalScrollbarTop, verticalScrollbarHeight = params.verticalScrollbarHeight, horizontalScrollbarLeft = params.horizontalScrollbarLeft, horizontalScrollbarWidth = params.horizontalScrollbarWidth, maxScrollTop = params.maxScrollTop, maxScrollLeft = params.maxScrollLeft, totalHeight = params.totalHeight, totalWidth = params.totalWidth, dataAreaHeight = params.dataAreaHeight, onSortChange = params.onSortChange, onRowClick = params.onRowClick, getRowKey = params.getRowKey, startSelection = params.startSelection, updateSelection = params.updateSelection, extendSelection = params.extendSelection, checkResizeHandle = params.checkResizeHandle, startResize = params.startResize, updateResize = params.updateResize, endResize = params.endResize, setHoverResizeColumn = params.setHoverResizeColumn, resizeState = params.resizeState, _b = params.hasSummaryRow, hasSummaryRow = _b === void 0 ? false : _b, mergeCellMap = params.mergeCellMap, menuShow = params.menuShow, fixedRowsCount = params.fixedRowsCount, fixedRowsConfig = params.fixedRowsConfig, summaryFixed = params.summaryFixed;
|
|
17
|
+
var state = params.state, setState = params.setState, scrollState = params.scrollState, setScrollState = params.setScrollState, processedDataSource = params.processedDataSource, columnRenderInfos = params.columnRenderInfos, _a = params.columns, columns = _a === void 0 ? [] : _a, rowSelection = params.rowSelection, rowHeight = params.rowHeight, headerHeight = params.headerHeight, containerWidth = params.containerWidth, containerHeight = params.containerHeight, scrollbarSize = params.scrollbarSize, canvasRef = params.canvasRef, containerRef = params.containerRef, needVerticalScrollbar = params.needVerticalScrollbar, needHorizontalScrollbar = params.needHorizontalScrollbar, verticalScrollbarTop = params.verticalScrollbarTop, verticalScrollbarHeight = params.verticalScrollbarHeight, horizontalScrollbarLeft = params.horizontalScrollbarLeft, horizontalScrollbarWidth = params.horizontalScrollbarWidth, maxScrollTop = params.maxScrollTop, maxScrollLeft = params.maxScrollLeft, totalHeight = params.totalHeight, totalWidth = params.totalWidth, dataAreaHeight = params.dataAreaHeight, onSortChange = params.onSortChange, onRowClick = params.onRowClick, getRowKey = params.getRowKey, startSelection = params.startSelection, updateSelection = params.updateSelection, extendSelection = params.extendSelection, checkResizeHandle = params.checkResizeHandle, startResize = params.startResize, updateResize = params.updateResize, endResize = params.endResize, setHoverResizeColumn = params.setHoverResizeColumn, resizeState = params.resizeState, _b = params.hasSummaryRow, hasSummaryRow = _b === void 0 ? false : _b, mergeCellMap = params.mergeCellMap, menuShow = params.menuShow, fixedRowsCount = params.fixedRowsCount, fixedRowsConfig = params.fixedRowsConfig, summaryFixed = params.summaryFixed;
|
|
17
18
|
// 获取鼠标位置对应的单元格
|
|
18
19
|
var getCellFromPosition = useMemoizedFn(function (x, y) {
|
|
19
20
|
var fixedTopRowsCount = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.topCount) || fixedRowsCount || 0;
|
|
@@ -464,6 +465,8 @@ var useTableInteraction = function (params) {
|
|
|
464
465
|
updateSelection(cell);
|
|
465
466
|
}
|
|
466
467
|
}
|
|
468
|
+
// 检查边界滚动 - 当鼠标接近容器边界时自动滚动
|
|
469
|
+
checkBoundaryScroll(x, y);
|
|
467
470
|
return;
|
|
468
471
|
}
|
|
469
472
|
// 检测列宽调整手柄hover状态
|
|
@@ -612,6 +615,8 @@ var useTableInteraction = function (params) {
|
|
|
612
615
|
// 结束框选
|
|
613
616
|
if (state.isSelecting) {
|
|
614
617
|
setState(function (prev) { return (__assign(__assign({}, prev), { isSelecting: false })); });
|
|
618
|
+
// 停止边界滚动
|
|
619
|
+
stopBoundaryScroll();
|
|
615
620
|
}
|
|
616
621
|
});
|
|
617
622
|
// 检查目标元素是否是Canvas内部渲染的元素
|
|
@@ -739,6 +744,15 @@ var useTableInteraction = function (params) {
|
|
|
739
744
|
// 显示右键菜单
|
|
740
745
|
menuShow({ event: e });
|
|
741
746
|
});
|
|
747
|
+
// 使用ref存储最新的状态和函数引用,避免闭包问题
|
|
748
|
+
var stateRef = useRef(state);
|
|
749
|
+
stateRef.current = state;
|
|
750
|
+
var scrollStateRef = useRef(scrollState);
|
|
751
|
+
scrollStateRef.current = scrollState;
|
|
752
|
+
var resizeStateRef = useRef(resizeState);
|
|
753
|
+
resizeStateRef.current = resizeState;
|
|
754
|
+
var columnRenderInfosRef = useRef(columnRenderInfos);
|
|
755
|
+
columnRenderInfosRef.current = columnRenderInfos;
|
|
742
756
|
// 在document上监听拖拽事件和框选事件,以支持鼠标移出canvas后继续操作
|
|
743
757
|
useEffect(function () {
|
|
744
758
|
var isDragging = scrollState.isDraggingVertical ||
|
|
@@ -756,27 +770,32 @@ var useTableInteraction = function (params) {
|
|
|
756
770
|
var rect = canvas.getBoundingClientRect();
|
|
757
771
|
var x = e.clientX - rect.left;
|
|
758
772
|
var y = e.clientY - rect.top;
|
|
773
|
+
// 使用ref获取最新状态,避免闭包问题
|
|
774
|
+
var currentScrollState = scrollStateRef.current;
|
|
775
|
+
var currentResizeState = resizeStateRef.current;
|
|
776
|
+
var currentState = stateRef.current;
|
|
759
777
|
// 处理列宽调整拖拽
|
|
760
|
-
if ((
|
|
778
|
+
if ((currentResizeState === null || currentResizeState === void 0 ? void 0 : currentResizeState.isResizing) && updateResize) {
|
|
761
779
|
updateResize(x);
|
|
762
780
|
return;
|
|
763
781
|
}
|
|
764
782
|
// 处理垂直滚动条拖拽
|
|
765
|
-
if (
|
|
766
|
-
handleVerticalScrollDrag(y -
|
|
783
|
+
if (currentScrollState.isDraggingVertical) {
|
|
784
|
+
handleVerticalScrollDrag(y - currentScrollState.dragStartY);
|
|
767
785
|
return;
|
|
768
786
|
}
|
|
769
787
|
// 处理水平滚动条拖拽
|
|
770
|
-
if (
|
|
771
|
-
handleHorizontalScrollDrag(x -
|
|
788
|
+
if (currentScrollState.isDraggingHorizontal) {
|
|
789
|
+
handleHorizontalScrollDrag(x - currentScrollState.dragStartX);
|
|
772
790
|
return;
|
|
773
791
|
}
|
|
774
792
|
// 处理框选(在canvas外也能继续框选)
|
|
775
|
-
if (
|
|
793
|
+
if (currentState.isSelecting) {
|
|
776
794
|
var cell = getCellFromPosition(x, y);
|
|
777
795
|
if (cell) {
|
|
778
796
|
// 检查是否在选择框列,如果是,则不更新框选
|
|
779
|
-
var isSelectionColumn = ((_a =
|
|
797
|
+
var isSelectionColumn = ((_a = columnRenderInfosRef.current[cell.col]) === null || _a === void 0 ? void 0 : _a.column.key) ===
|
|
798
|
+
"__selection__";
|
|
780
799
|
if (!isSelectionColumn) {
|
|
781
800
|
updateSelection(cell);
|
|
782
801
|
}
|
|
@@ -784,13 +803,18 @@ var useTableInteraction = function (params) {
|
|
|
784
803
|
}
|
|
785
804
|
};
|
|
786
805
|
var handleDocumentMouseUp = function () {
|
|
806
|
+
// 使用ref获取最新状态
|
|
807
|
+
var currentResizeState = resizeStateRef.current;
|
|
808
|
+
var currentState = stateRef.current;
|
|
787
809
|
// 结束列宽调整
|
|
788
|
-
if ((
|
|
810
|
+
if ((currentResizeState === null || currentResizeState === void 0 ? void 0 : currentResizeState.isResizing) && endResize) {
|
|
789
811
|
endResize();
|
|
790
812
|
}
|
|
791
813
|
// 结束框选
|
|
792
|
-
if (
|
|
814
|
+
if (currentState.isSelecting) {
|
|
793
815
|
setState(function (prev) { return (__assign(__assign({}, prev), { isSelecting: false })); });
|
|
816
|
+
// 停止边界滚动
|
|
817
|
+
stopBoundaryScroll();
|
|
794
818
|
}
|
|
795
819
|
setScrollState(function (prev) { return (__assign(__assign({}, prev), { isDraggingVertical: false, isDraggingHorizontal: false })); });
|
|
796
820
|
};
|
|
@@ -800,22 +824,32 @@ var useTableInteraction = function (params) {
|
|
|
800
824
|
document.removeEventListener("mousemove", handleDocumentMouseMove);
|
|
801
825
|
document.removeEventListener("mouseup", handleDocumentMouseUp);
|
|
802
826
|
};
|
|
827
|
+
// 优化:减少依赖项,避免频繁重注册事件监听器
|
|
803
828
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
804
829
|
}, [
|
|
805
830
|
scrollState.isDraggingVertical,
|
|
806
831
|
scrollState.isDraggingHorizontal,
|
|
807
832
|
resizeState === null || resizeState === void 0 ? void 0 : resizeState.isResizing,
|
|
808
833
|
state.isSelecting,
|
|
809
|
-
|
|
810
|
-
endResize,
|
|
811
|
-
|
|
812
|
-
handleHorizontalScrollDrag,
|
|
813
|
-
updateSelection,
|
|
814
|
-
setState,
|
|
815
|
-
getCellFromPosition,
|
|
816
|
-
columnRenderInfos,
|
|
817
|
-
canvasRef,
|
|
834
|
+
// 移除函数依赖,通过 useMemoizedFn 和 useRef 来稳定引用
|
|
835
|
+
// updateResize, endResize, handleVerticalScrollDrag, handleHorizontalScrollDrag,
|
|
836
|
+
// updateSelection, setState, getCellFromPosition, columnRenderInfos,
|
|
818
837
|
]);
|
|
838
|
+
// 边界滚动Hook - 用于框选时的自动滚动
|
|
839
|
+
var _c = useSelectionBoundaryScroll({
|
|
840
|
+
containerRef: containerRef,
|
|
841
|
+
isSelecting: state.isSelecting,
|
|
842
|
+
scrollState: scrollState,
|
|
843
|
+
setScrollState: setScrollState,
|
|
844
|
+
maxScrollTop: maxScrollTop,
|
|
845
|
+
maxScrollLeft: maxScrollLeft,
|
|
846
|
+
onScroll: function (scrollLeft, scrollTop) {
|
|
847
|
+
// 边界滚动时的回调,可以在这里添加额外的逻辑
|
|
848
|
+
},
|
|
849
|
+
getCellFromPosition: getCellFromPosition,
|
|
850
|
+
updateSelection: updateSelection,
|
|
851
|
+
columnRenderInfos: columnRenderInfos,
|
|
852
|
+
}), checkBoundaryScroll = _c.checkBoundaryScroll, stopBoundaryScroll = _c.stopBoundaryScroll;
|
|
819
853
|
return {
|
|
820
854
|
handleCanvasMouseDown: handleCanvasMouseDown,
|
|
821
855
|
handleCanvasMouseMove: handleCanvasMouseMove,
|
|
@@ -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/合并/编辑等逻辑
|