zmdms-webui 2.3.0 → 2.3.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.
Files changed (69) hide show
  1. package/dist/es/canvastable/canvasTable.js +367 -0
  2. package/dist/es/canvastable/components/BadgePopover.js +27 -0
  3. package/dist/es/canvastable/components/CanvasTableMenu.js +74 -0
  4. package/dist/es/canvastable/components/CellOverlay.js +49 -0
  5. package/dist/es/canvastable/components/ColumnDynamic.js +12 -0
  6. package/dist/es/canvastable/components/EmptyPlaceholder.js +20 -0
  7. package/dist/es/canvastable/components/FilterPopover.js +274 -0
  8. package/dist/es/canvastable/components/HeaderOverlay.js +22 -0
  9. package/dist/es/canvastable/components/Tooltip.js +27 -0
  10. package/dist/es/canvastable/hooks/useClickOutside.js +30 -0
  11. package/dist/es/canvastable/hooks/useColumnResize.js +130 -0
  12. package/dist/es/canvastable/hooks/useContainerSize.js +40 -0
  13. package/dist/es/canvastable/hooks/useCopyToClipboard.js +150 -0
  14. package/dist/es/canvastable/hooks/useHeaderHeight.js +103 -0
  15. package/dist/es/canvastable/hooks/useMergeCells.js +111 -0
  16. package/dist/es/canvastable/hooks/useOverlays.js +364 -0
  17. package/dist/es/canvastable/hooks/usePopovers.js +93 -0
  18. package/dist/es/canvastable/hooks/useProcessedColumns.js +94 -0
  19. package/dist/es/canvastable/hooks/useScroll.js +251 -0
  20. package/dist/es/canvastable/hooks/useSummaryRow.js +81 -0
  21. package/dist/es/canvastable/hooks/useTableInteraction.js +804 -0
  22. package/dist/es/canvastable/hooks/useTableRender.js +1289 -0
  23. package/dist/es/canvastable/hooks/useTableSelection.js +57 -0
  24. package/dist/es/canvastable/hooks/useTableState.js +218 -0
  25. package/dist/es/canvastable/index.js +5 -0
  26. package/dist/es/canvastable/utils/canvasDrawHelpers.js +156 -0
  27. package/dist/es/canvastable/utils/cellHelpers.js +121 -0
  28. package/dist/es/canvastable/utils/columnHelpers.js +67 -0
  29. package/dist/es/canvastable/utils/constants.js +42 -0
  30. package/dist/es/canvastable/utils/formatHelpers.js +60 -0
  31. package/dist/es/canvastable/utils/interactionHelpers.js +176 -0
  32. package/dist/es/canvastable/utils/multiHeaderHelpers.js +82 -0
  33. package/dist/es/canvastable/utils/tableCalculations.js +100 -0
  34. package/dist/index.es.css +1 -1
  35. package/dist/less/components/CanvasTable/style/index.less +106 -0
  36. package/package.json +1 -1
  37. package/dist/es/cascader/index.css +0 -1
  38. package/dist/es/collapse/index.css +0 -1
  39. package/dist/es/container/index.css +0 -1
  40. package/dist/es/datepicker/index.css +0 -1
  41. package/dist/es/descriptions/index.css +0 -1
  42. package/dist/es/detaillist/index.css +0 -1
  43. package/dist/es/differences/index.css +0 -1
  44. package/dist/es/dynamicsetting/index.css +0 -1
  45. package/dist/es/electronsignatures/index.css +0 -1
  46. package/dist/es/footer/index.css +0 -1
  47. package/dist/es/form/index.css +0 -1
  48. package/dist/es/formitem/index.css +0 -1
  49. package/dist/es/input/index.css +0 -1
  50. package/dist/es/inputnumber/index.css +0 -1
  51. package/dist/es/leftcontent/index.css +0 -1
  52. package/dist/es/message/index.css +0 -1
  53. package/dist/es/microloading/index.css +0 -1
  54. package/dist/es/modal/index.css +0 -1
  55. package/dist/es/notauthpage/index.css +0 -0
  56. package/dist/es/notroutepage/index.css +0 -0
  57. package/dist/es/pagination/index.css +0 -1
  58. package/dist/es/placeholder/index.css +0 -1
  59. package/dist/es/print/index.css +0 -1
  60. package/dist/es/select/index.css +0 -1
  61. package/dist/es/table/index.css +0 -1
  62. package/dist/es/tabs/index.css +0 -1
  63. package/dist/es/tag/index.css +0 -1
  64. package/dist/es/title/index.css +0 -1
  65. package/dist/es/tree/index.css +0 -1
  66. package/dist/es/treeselect/index.css +0 -1
  67. package/dist/es/uploadlist/index.css +0 -1
  68. package/dist/es/watermark/index.css +0 -1
  69. package/dist/es/zttransfer/index.css +0 -1
@@ -0,0 +1,1289 @@
1
+ import { useMemo, isValidElement, useEffect } from 'react';
2
+ import { useMemoizedFn } from 'ahooks';
3
+ import { IS_SUMMARY } from '../../table/constant.js';
4
+ import '../../_virtual/_tslib.js';
5
+ import 'lodash/isEqual';
6
+ import '../../node_modules/immutability-helper/index.js';
7
+ import 'react/jsx-runtime';
8
+ import 'zmdms-utils';
9
+ import '../../node_modules/exceljs/dist/exceljs.min.js';
10
+ import 'dayjs';
11
+ import { COLORS, FONT_SIZE, FONT_FAMILY, SCROLLBAR_SIZE, SCROLLBAR_PADDING } from '../utils/constants.js';
12
+ import { drawCheckbox, wrapText, truncateText, drawFilterIcon, drawSortIcon } from '../utils/canvasDrawHelpers.js';
13
+ import { getMaxDepth, flattenHeaders, getLeafColumns } from '../utils/multiHeaderHelpers.js';
14
+ import { extractCellText } from '../utils/cellHelpers.js';
15
+ import { formatCellValue } from '../utils/formatHelpers.js';
16
+ import { calculateSelectionState, calculateIconArea } from '../utils/interactionHelpers.js';
17
+
18
+ /**
19
+ * 表格渲染 Hook
20
+ */
21
+ var useTableRender = function (params) {
22
+ 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, rowHeight = params.rowHeight, bordered = params.bordered, striped = params.striped, 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, _a = params.RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_WIDTH = _a === void 0 ? 8 : _a, mergeCellMap = params.mergeCellMap, _b = params.hasSummaryRow, hasSummaryRow = _b === void 0 ? false : _b, fixedRowsCount = params.fixedRowsCount, fixedRowsConfig = params.fixedRowsConfig, _c = params.fixedSummaryRow, fixedSummaryRow = _c === void 0 ? false : _c;
23
+ // 判断是否是多级表头
24
+ var maxDepth = useMemo(function () { return getMaxDepth(columns); }, [columns]);
25
+ var isMultiHeader = maxDepth > 1;
26
+ // 扁平化多级表头(如果需要)
27
+ var flattenedHeaderRows = useMemo(function () { return (isMultiHeader ? flattenHeaders(columns) : []); }, [columns, isMultiHeader]);
28
+ var dpr = window.devicePixelRatio || 1;
29
+ // ==================== 通用辅助函数 ====================
30
+ /**
31
+ * 计算固定列的右边界
32
+ */
33
+ var calculateFixedColumnsRightEdge = useMemoizedFn(function () {
34
+ var rightEdge = 0;
35
+ columnRenderInfos.forEach(function (info, index) {
36
+ if (info.fixed) {
37
+ var actualWidth = getColumnWidth ? getColumnWidth(index) : info.width;
38
+ rightEdge = Math.max(rightEdge, info.fixedLeft + actualWidth);
39
+ }
40
+ });
41
+ return rightEdge;
42
+ });
43
+ /**
44
+ * 计算文本X坐标(根据对齐方式)
45
+ */
46
+ var calculateTextX = function (drawX, actualWidth, align, padding) {
47
+ if (align === void 0) { align = "left"; }
48
+ if (padding === void 0) { padding = 16; }
49
+ if (align === "center") {
50
+ return drawX + actualWidth / 2;
51
+ }
52
+ else if (align === "right") {
53
+ return drawX + actualWidth - padding;
54
+ }
55
+ return drawX + padding;
56
+ };
57
+ /**
58
+ * 设置Canvas文本对齐方式
59
+ */
60
+ var setTextAlign = function (ctx, align) {
61
+ if (align === void 0) { align = "left"; }
62
+ ctx.textAlign = align;
63
+ };
64
+ // 绘制角标的辅助函数(三角形标记,类似Excel批注)
65
+ var drawBadge = useMemoizedFn(function (ctx, badge, cellX, cellY, cellWidth, cellHeight) {
66
+ if (!badge.text)
67
+ return;
68
+ var triangleSize = 12; // 三角形边长
69
+ var position = badge.position || "top-right";
70
+ var color = badge.color || "#ff4d4f";
71
+ ctx.fillStyle = color;
72
+ ctx.beginPath();
73
+ // 根据位置绘制不同方向的三角形
74
+ switch (position) {
75
+ case "top-left":
76
+ // 左上角三角形
77
+ ctx.moveTo(cellX, cellY);
78
+ ctx.lineTo(cellX + triangleSize, cellY);
79
+ ctx.lineTo(cellX, cellY + triangleSize);
80
+ break;
81
+ case "top-right":
82
+ // 右上角三角形(默认)
83
+ ctx.moveTo(cellX + cellWidth, cellY);
84
+ ctx.lineTo(cellX + cellWidth - triangleSize, cellY);
85
+ ctx.lineTo(cellX + cellWidth, cellY + triangleSize);
86
+ break;
87
+ case "bottom-left":
88
+ // 左下角三角形
89
+ ctx.moveTo(cellX, cellY + cellHeight);
90
+ ctx.lineTo(cellX, cellY + cellHeight - triangleSize);
91
+ ctx.lineTo(cellX + triangleSize, cellY + cellHeight);
92
+ break;
93
+ case "bottom-right":
94
+ // 右下角三角形
95
+ ctx.moveTo(cellX + cellWidth, cellY + cellHeight);
96
+ ctx.lineTo(cellX + cellWidth, cellY + cellHeight - triangleSize);
97
+ ctx.lineTo(cellX + cellWidth - triangleSize, cellY + cellHeight);
98
+ break;
99
+ }
100
+ ctx.closePath();
101
+ ctx.fill();
102
+ });
103
+ // 绘制表头单元格的辅助函数
104
+ var drawHeaderCell = useMemoizedFn(function (ctx, info, index, displayWidth, displayHeight) {
105
+ var column = info.column, x = info.x, fixed = info.fixed, fixedLeft = info.fixedLeft;
106
+ // 使用 getColumnWidth 获取列宽(考虑拖拽时的临时宽度)
107
+ var width = getColumnWidth ? getColumnWidth(index) : info.width;
108
+ var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
109
+ // 跳过不在可视区域的列
110
+ if (!fixed && (drawX + width < 0 || drawX > displayWidth)) {
111
+ return;
112
+ }
113
+ // 绘制表头单元格
114
+ ctx.save();
115
+ ctx.beginPath();
116
+ ctx.rect(drawX, 0, width, headerHeight);
117
+ ctx.clip();
118
+ // 绘制单元格背景
119
+ ctx.fillStyle = COLORS.headerBg;
120
+ ctx.fillRect(drawX, 0, width, headerHeight);
121
+ // 表头文本
122
+ ctx.fillStyle = COLORS.text;
123
+ ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
124
+ ctx.textBaseline = "middle";
125
+ var textX = drawX + 16;
126
+ var textY = headerHeight / 2;
127
+ // 绘制选择框
128
+ if (column.key === "__selection__") {
129
+ // 计算选中状态
130
+ var selectionState = calculateSelectionState(processedDataSource, state.selectedRowKeys, getRowKey, rowSelection === null || rowSelection === void 0 ? void 0 : rowSelection.getCheckboxProps);
131
+ // 居中绘制复选框
132
+ var checkboxX = drawX + width / 2;
133
+ drawCheckbox(ctx, checkboxX, textY, selectionState.isAllSelected, false, selectionState.isIndeterminate);
134
+ }
135
+ else {
136
+ // 计算图标占用的宽度
137
+ var iconArea = calculateIconArea(column, width);
138
+ // 计算文本可用宽度(左边距16px + 图标宽度 )
139
+ var textMaxWidth = width - 16 - iconArea.iconsWidth;
140
+ // 绘制文本(如果 title 是 React 元素,跳过Canvas渲染,在覆盖层中渲染)
141
+ if (!isValidElement(column.title)) {
142
+ var titleText = String(column.title || "");
143
+ // 根据 ellipsis 和 wrap 属性处理文本
144
+ if (column.wrap) {
145
+ // 换行显示(支持\n和自动换行)
146
+ var lines = wrapText(ctx, titleText, textMaxWidth);
147
+ var lineHeight_1 = 20;
148
+ var startY_1 = textY - ((lines.length - 1) * lineHeight_1) / 2;
149
+ lines.forEach(function (line, index) {
150
+ var lineY = startY_1 + index * lineHeight_1;
151
+ ctx.fillText(line, textX, lineY);
152
+ });
153
+ }
154
+ else {
155
+ // 默认截断显示
156
+ var truncatedText = truncateText(ctx, titleText, textMaxWidth);
157
+ ctx.fillText(truncatedText, textX, textY);
158
+ }
159
+ }
160
+ // 绘制筛选图标(最右侧,距离右边5px)
161
+ if (iconArea.hasFilter) {
162
+ var iconX = drawX + width - 10; // 右边距5px + 图标中心偏移5px
163
+ var hasFilterValue = state.filters[column.key] && state.filters[column.key].length > 0;
164
+ drawFilterIcon(ctx, iconX, textY, hasFilterValue);
165
+ }
166
+ // 绘制排序图标(筛选图标左边,间距4px)
167
+ if (iconArea.hasOrder) {
168
+ // 如果有筛选:右边距5px + 筛选10px + 间距4px = 19px,所以图标中心在 width - 24
169
+ // 如果无筛选:右边距5px + 图标中心5px = 10px,所以图标中心在 width - 10
170
+ var iconX = drawX + width - (iconArea.hasFilter ? 24 : 10);
171
+ drawSortIcon(ctx, iconX, textY, column.key === state.sortField ? state.sortOrder : null);
172
+ }
173
+ }
174
+ ctx.restore();
175
+ // 绘制表头列之间的边框
176
+ if (bordered) {
177
+ ctx.strokeStyle = COLORS.border;
178
+ ctx.lineWidth = 2;
179
+ ctx.beginPath();
180
+ ctx.moveTo(drawX + width, 0);
181
+ ctx.lineTo(drawX + width, headerHeight);
182
+ ctx.stroke();
183
+ }
184
+ // 绘制列宽调整手柄(跳过选择框列)
185
+ if (column.key !== "__selection__" && resizeState) {
186
+ var isHover = resizeState.hoverResizeColumnIndex === index;
187
+ var isResizing = resizeState.resizingColumnIndex === index;
188
+ if (isHover || isResizing) {
189
+ var handleX = drawX + width - RESIZE_HANDLE_WIDTH / 2;
190
+ // 绘制调整手柄背景
191
+ ctx.fillStyle = isResizing ? "#1890ff" : "rgba(24, 144, 255, 0.3)";
192
+ ctx.fillRect(handleX, 0, RESIZE_HANDLE_WIDTH, headerHeight);
193
+ // 如果正在拖拽,绘制虚线
194
+ if (isResizing) {
195
+ ctx.strokeStyle = "#1890ff";
196
+ ctx.lineWidth = 1;
197
+ ctx.setLineDash([4, 4]);
198
+ ctx.beginPath();
199
+ ctx.moveTo(drawX + width, 0);
200
+ ctx.lineTo(drawX + width, displayHeight);
201
+ ctx.stroke();
202
+ ctx.setLineDash([]);
203
+ }
204
+ }
205
+ }
206
+ });
207
+ // 绘制多级表头单元格
208
+ var drawMultiHeaderCell = useMemoizedFn(function (ctx, headerCell, rowHeight, y, displayWidth, currentDepth, onlyFixed // 是否只绘制fixed列
209
+ ) {
210
+ if (onlyFixed === void 0) { onlyFixed = false; }
211
+ var column = headerCell.column, colSpan = headerCell.colSpan, rowSpan = headerCell.rowSpan, colIndex = headerCell.colIndex;
212
+ // 根据列索引计算x位置和宽度,并判断是否是fixed列
213
+ var x = 0;
214
+ var width = 0;
215
+ var isFixed = false;
216
+ var fixedLeft = 0;
217
+ var leafColumns = getLeafColumns(columns);
218
+ // 找到对应的叶子列并计算位置
219
+ for (var i = 0; i < leafColumns.length; i++) {
220
+ var leafInfo = columnRenderInfos[i];
221
+ if (i < colIndex) {
222
+ x += getColumnWidth ? getColumnWidth(i) : leafInfo.width;
223
+ }
224
+ else if (i < colIndex + colSpan) {
225
+ width += getColumnWidth ? getColumnWidth(i) : leafInfo.width;
226
+ // 如果跨越的列中有任何一个是fixed的,整个合并单元格都视为fixed
227
+ if (leafInfo.fixed) {
228
+ isFixed = true;
229
+ if (i === colIndex) {
230
+ fixedLeft = leafInfo.fixedLeft;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ // 根据 onlyFixed 参数过滤要绘制的列
236
+ if (onlyFixed && !isFixed)
237
+ return; // 只绘制fixed列时,跳过非fixed列
238
+ if (!onlyFixed && isFixed)
239
+ return; // 只绘制非fixed列时,跳过fixed列
240
+ // 应用横向滚动(fixed列不滚动)
241
+ var drawX = isFixed ? fixedLeft : x - scrollState.scrollLeft;
242
+ // 跳过不在可视区域的列(fixed列始终绘制)
243
+ if (!isFixed && (drawX + width < 0 || drawX > displayWidth)) {
244
+ return;
245
+ }
246
+ // 绘制单元格背景
247
+ ctx.fillStyle = COLORS.headerBg;
248
+ ctx.fillRect(drawX, y, width, rowHeight * rowSpan);
249
+ // 绘制边框
250
+ if (bordered) {
251
+ ctx.strokeStyle = COLORS.border;
252
+ ctx.lineWidth = 1;
253
+ ctx.strokeRect(drawX, y, width, rowHeight * rowSpan);
254
+ }
255
+ // 判断是否是最末级表头(当前行+rowSpan 是否等于最大深度)
256
+ var isLastLevel = currentDepth + rowSpan === maxDepth;
257
+ // 判断是否是特殊列(选择框列、序号列等不需要排序和筛选的列)
258
+ var isSpecialColumn = column.key === "__selection__" || column.key === "__index__";
259
+ // 绘制单元格内容
260
+ var textY = y + (rowHeight * rowSpan) / 2;
261
+ // 绘制选择框(只在第一行显示全选框)
262
+ if (column.key === "__selection__" && currentDepth === 0) {
263
+ // 计算选中状态
264
+ var selectionState = calculateSelectionState(processedDataSource, state.selectedRowKeys, getRowKey, rowSelection === null || rowSelection === void 0 ? void 0 : rowSelection.getCheckboxProps);
265
+ // 居中绘制复选框
266
+ var checkboxX = drawX + width / 2;
267
+ drawCheckbox(ctx, checkboxX, textY, selectionState.isAllSelected, false, selectionState.isIndeterminate);
268
+ }
269
+ else {
270
+ // 绘制文本
271
+ ctx.fillStyle = COLORS.text;
272
+ ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
273
+ ctx.textBaseline = "middle";
274
+ if (!isValidElement(column.title)) {
275
+ var titleText = String(column.title || "");
276
+ // 计算图标占用的宽度(仅在最末级表头且非特殊列时显示)
277
+ var shouldShowIcons = isLastLevel && !isSpecialColumn;
278
+ var iconArea = shouldShowIcons
279
+ ? calculateIconArea(column, width)
280
+ : { hasOrder: false, hasFilter: false, iconsWidth: 0 };
281
+ // 如果是最末级且有图标,文本左对齐;否则居中
282
+ if (isLastLevel && iconArea.iconsWidth > 0) {
283
+ ctx.textAlign = "left";
284
+ var textX = drawX + 16;
285
+ var textMaxWidth = width - 16 - iconArea.iconsWidth;
286
+ var truncated = truncateText(ctx, titleText, textMaxWidth);
287
+ ctx.fillText(truncated, textX, textY);
288
+ }
289
+ else {
290
+ ctx.textAlign = "center";
291
+ var textX = drawX + width / 2;
292
+ var truncated = truncateText(ctx, titleText, width - 16);
293
+ ctx.fillText(truncated, textX, textY);
294
+ }
295
+ // 绘制筛选图标(仅在最末级表头显示)
296
+ if (iconArea.hasFilter) {
297
+ var iconX = drawX + width - 10; // 右边距5px + 图标中心偏移5px
298
+ var hasFilterValue = state.filters[column.key] && state.filters[column.key].length > 0;
299
+ drawFilterIcon(ctx, iconX, textY, hasFilterValue);
300
+ }
301
+ // 绘制排序图标(仅在最末级表头显示)
302
+ if (iconArea.hasOrder) {
303
+ // 如果有筛选:右边距5px + 筛选10px + 间距4px = 19px,所以图标中心在 width - 24
304
+ // 如果无筛选:右边距5px + 图标中心5px = 10px,所以图标中心在 width - 10
305
+ var iconX = drawX + width - (iconArea.hasFilter ? 24 : 10);
306
+ drawSortIcon(ctx, iconX, textY, column.key === state.sortField ? state.sortOrder : null);
307
+ }
308
+ }
309
+ }
310
+ ctx.textAlign = "left"; // 重置对齐方式
311
+ });
312
+ // 绘制表头
313
+ var drawHeader = useMemoizedFn(function (ctx, displayWidth, displayHeight) {
314
+ // 表头背景
315
+ ctx.fillStyle = COLORS.headerBg;
316
+ ctx.fillRect(0, 0, displayWidth, headerHeight);
317
+ if (isMultiHeader) {
318
+ // 多级表头渲染
319
+ var rowHeight_1 = headerHeight / maxDepth;
320
+ // 先绘制非fixed列
321
+ flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
322
+ var y = rowIndex * rowHeight_1;
323
+ headerRow.forEach(function (headerCell) {
324
+ drawMultiHeaderCell(ctx, headerCell, rowHeight_1, y, displayWidth, rowIndex, false);
325
+ });
326
+ });
327
+ // 再绘制fixed列(覆盖在非fixed列之上)
328
+ flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
329
+ var y = rowIndex * rowHeight_1;
330
+ headerRow.forEach(function (headerCell) {
331
+ drawMultiHeaderCell(ctx, headerCell, rowHeight_1, y, displayWidth, rowIndex, true);
332
+ });
333
+ });
334
+ }
335
+ else {
336
+ // 单层表头渲染(原有逻辑)
337
+ // 先绘制非fixed列
338
+ columnRenderInfos.forEach(function (info, index) {
339
+ if (!info.fixed) {
340
+ drawHeaderCell(ctx, info, index, displayWidth, displayHeight);
341
+ }
342
+ });
343
+ // 再绘制fixed列(覆盖在非fixed列之上)
344
+ columnRenderInfos.forEach(function (info, index) {
345
+ if (info.fixed) {
346
+ drawHeaderCell(ctx, info, index, displayWidth, displayHeight);
347
+ }
348
+ });
349
+ }
350
+ });
351
+ // 绘制数据行
352
+ var drawRow = useMemoizedFn(function (ctx, rowIndex, displayWidth, displayHeight, onlyFixed // 是否只绘制fixed列
353
+ ) {
354
+ if (onlyFixed === void 0) { onlyFixed = false; }
355
+ var record = processedDataSource[rowIndex];
356
+ if (!record)
357
+ return;
358
+ // 计算固定行数量
359
+ var fixedTopCount = fixedRowsCount || (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.topCount) || 0;
360
+ // 计算Y坐标:对于固定行之后的行,需要加上固定行的高度并减去滚动偏移
361
+ var y = headerHeight +
362
+ fixedTopCount * rowHeight +
363
+ (rowIndex - fixedTopCount) * rowHeight -
364
+ scrollState.scrollTop;
365
+ // 计算实际可用的渲染区域高度(考虑固定底部行和合计行)
366
+ var fixedBottomRowsCount = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.bottomCount) || 0;
367
+ var fixedBottomHeight = fixedBottomRowsCount * rowHeight;
368
+ var fixedSummaryHeight = fixedSummaryRow && hasSummaryRow ? rowHeight : 0;
369
+ var effectiveHeight = displayHeight -
370
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
371
+ fixedBottomHeight -
372
+ fixedSummaryHeight;
373
+ // 跳过不在可视区域的行,但允许部分可见的行
374
+ if (y + rowHeight < headerHeight || y >= effectiveHeight) {
375
+ return;
376
+ }
377
+ var recordKey = getRowKey(record, rowIndex);
378
+ var isSelected = state.selectedRowKeys.includes(recordKey);
379
+ var isHover = state.hoverRowIndex === rowIndex;
380
+ // 计算fixed列的右边界,用于裁剪非fixed列
381
+ var fixedColumnsRightEdge = 0;
382
+ if (!onlyFixed) {
383
+ columnRenderInfos.forEach(function (info) {
384
+ if (info.fixed) {
385
+ var actualWidth = getColumnWidth
386
+ ? getColumnWidth(columnRenderInfos.indexOf(info))
387
+ : info.width;
388
+ var rightEdge = info.fixedLeft + actualWidth;
389
+ fixedColumnsRightEdge = Math.max(fixedColumnsRightEdge, rightEdge);
390
+ }
391
+ });
392
+ }
393
+ // 绘制单元格
394
+ columnRenderInfos.forEach(function (info, colIndex) {
395
+ var _a;
396
+ var column = info.column, x = info.x, fixed = info.fixed, fixedLeft = info.fixedLeft;
397
+ // 根据 onlyFixed 参数过滤要绘制的列
398
+ if (onlyFixed && !fixed)
399
+ return; // 只绘制fixed列时,跳过非fixed列
400
+ if (!onlyFixed && fixed)
401
+ return; // 只绘制非fixed列时,跳过fixed列
402
+ // 检查合并单元格
403
+ var mergeCellKey = "".concat(rowIndex, "-").concat(colIndex);
404
+ var mergeInfo = mergeCellMap === null || mergeCellMap === void 0 ? void 0 : mergeCellMap.get(mergeCellKey);
405
+ // 如果是被合并的单元格,跳过渲染
406
+ if (mergeInfo && mergeInfo.skip) {
407
+ return;
408
+ }
409
+ // 使用 getColumnWidth 获取列宽(考虑拖拽时的临时宽度)
410
+ var width = getColumnWidth ? getColumnWidth(colIndex) : info.width;
411
+ var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
412
+ // 跳过不在可视区域的列
413
+ if (!fixed && (drawX + width < 0 || drawX > displayWidth)) {
414
+ return;
415
+ }
416
+ // 对于非fixed列,检查是否被fixed列遮挡,如果完全被遮挡则跳过绘制
417
+ if (!fixed &&
418
+ fixedColumnsRightEdge > 0 &&
419
+ drawX < fixedColumnsRightEdge) {
420
+ // 如果非fixed列完全在fixed列的左侧(被完全遮挡),则跳过
421
+ if (drawX + width <= fixedColumnsRightEdge) {
422
+ return;
423
+ }
424
+ }
425
+ // 计算单元格高度(考虑合并)
426
+ var cellHeight = mergeInfo && mergeInfo.rowSpan > 1
427
+ ? rowHeight * mergeInfo.rowSpan
428
+ : rowHeight;
429
+ ctx.save();
430
+ ctx.beginPath();
431
+ // 裁剪区域:确保不覆盖表头,从 headerHeight 开始
432
+ var clipY = Math.max(y, headerHeight);
433
+ var clipHeight = Math.min(cellHeight, y + cellHeight - headerHeight);
434
+ // 对于非fixed列,还需要裁剪掉被fixed列遮挡的部分
435
+ var clipX = drawX;
436
+ var clipWidth = width;
437
+ if (!fixed &&
438
+ fixedColumnsRightEdge > 0 &&
439
+ drawX < fixedColumnsRightEdge) {
440
+ // 调整裁剪区域,去掉被fixed列遮挡的部分
441
+ var overlap = fixedColumnsRightEdge - drawX;
442
+ if (overlap < width) {
443
+ clipX = fixedColumnsRightEdge;
444
+ clipWidth = width - overlap;
445
+ }
446
+ }
447
+ ctx.rect(clipX, clipY, clipWidth, clipHeight);
448
+ ctx.clip();
449
+ // 绘制单元格背景
450
+ if (clipHeight > 0 && clipWidth > 0) {
451
+ if (isSelected) {
452
+ ctx.fillStyle = COLORS.selectedBg;
453
+ }
454
+ else if (record[IS_SUMMARY]) {
455
+ // 合计行使用表头背景色
456
+ ctx.fillStyle = COLORS.headerBg;
457
+ }
458
+ else if (isHover) {
459
+ ctx.fillStyle = COLORS.hoverBg;
460
+ }
461
+ else if (striped && rowIndex % 2 === 1) {
462
+ ctx.fillStyle = COLORS.stripedBg;
463
+ }
464
+ else {
465
+ ctx.fillStyle = COLORS.white;
466
+ }
467
+ ctx.fillRect(clipX, clipY, clipWidth, clipHeight);
468
+ }
469
+ // 单元格内容
470
+ if (column.key === "__selection__") {
471
+ var checkboxProps = ((_a = rowSelection === null || rowSelection === void 0 ? void 0 : rowSelection.getCheckboxProps) === null || _a === void 0 ? void 0 : _a.call(rowSelection, record)) || {};
472
+ var disabled = checkboxProps.disabled || false;
473
+ // 对于合并单元格,使用合并后的单元格高度进行垂直居中
474
+ drawCheckbox(ctx, drawX + width / 2, y + cellHeight / 2, isSelected, disabled, false);
475
+ }
476
+ else {
477
+ var dataIndex = column.dataIndex || column.key;
478
+ var cellValue = record[dataIndex];
479
+ // 检查是否有 render 函数且返回自定义 ReactNode
480
+ var shouldRenderInCanvas = true;
481
+ if (column.render) {
482
+ var rendered = column.render(cellValue, record, rowIndex);
483
+ // 如果返回的不是字符串或数字,说明是自定义组件,应该在覆盖层渲染,不在 Canvas 渲染
484
+ if (typeof rendered !== "string" &&
485
+ typeof rendered !== "number" &&
486
+ rendered !== null &&
487
+ rendered !== undefined) {
488
+ shouldRenderInCanvas = false;
489
+ }
490
+ }
491
+ // 只有在需要在 Canvas 中渲染时才绘制文本
492
+ if (shouldRenderInCanvas) {
493
+ // 应用格式化(日期、精度、千分符)
494
+ var cellText = extractCellText({
495
+ cellValue: cellValue,
496
+ record: record,
497
+ rowIndex: rowIndex,
498
+ renderFn: column.render,
499
+ });
500
+ // 如果没有自定义render,应用格式化
501
+ if (!column.render) {
502
+ cellText = formatCellValue(cellValue, column);
503
+ }
504
+ // 绘制文本
505
+ ctx.fillStyle = COLORS.text;
506
+ ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
507
+ ctx.textBaseline = "middle";
508
+ var align = column.align || "left";
509
+ var textX_1 = drawX + 16;
510
+ if (align === "center") {
511
+ ctx.textAlign = "center";
512
+ textX_1 = drawX + width / 2;
513
+ }
514
+ else if (align === "right") {
515
+ ctx.textAlign = "right";
516
+ textX_1 = drawX + width - 16;
517
+ }
518
+ else {
519
+ ctx.textAlign = "left";
520
+ }
521
+ // 处理文本显示(ellipsis 或 wrap)
522
+ var maxWidth = width - 32;
523
+ var ellipsis = column.ellipsis !== false; // 默认为true
524
+ var wrap = column.wrap === true; // 默认为false
525
+ if (wrap) {
526
+ // 换行显示
527
+ var lines = wrapText(ctx, cellText, maxWidth);
528
+ var lineHeight_2 = FONT_SIZE + 4; // 行高
529
+ var totalHeight = lines.length * lineHeight_2;
530
+ // 使用合并后的单元格高度计算垂直居中位置
531
+ var startY_2 = y + (cellHeight - totalHeight) / 2 + lineHeight_2 / 2;
532
+ lines.forEach(function (line, lineIndex) {
533
+ var lineY = startY_2 + lineIndex * lineHeight_2;
534
+ // 只绘制在单元格可见区域内的文本
535
+ if (lineY >= y && lineY <= y + cellHeight) {
536
+ ctx.fillText(line, textX_1, lineY);
537
+ }
538
+ });
539
+ }
540
+ else if (ellipsis) {
541
+ // 省略显示 - 使用合并后的单元格高度
542
+ var textY = y + cellHeight / 2;
543
+ var truncatedText = truncateText(ctx, cellText, maxWidth);
544
+ ctx.fillText(truncatedText, textX_1, textY);
545
+ }
546
+ else {
547
+ // 不处理,直接显示 - 使用合并后的单元格高度
548
+ var textY = y + cellHeight / 2;
549
+ ctx.fillText(cellText, textX_1, textY);
550
+ }
551
+ // 绘制角标(跳过合计行)
552
+ var isSummaryRow = hasSummaryRow && rowIndex === processedDataSource.length - 1;
553
+ if (column.badge && !isSummaryRow) {
554
+ var badgeProps = typeof column.badge === "function"
555
+ ? column.badge(record)
556
+ : column.badge;
557
+ if (badgeProps) {
558
+ // 使用原始的单元格坐标,而不是裁剪后的坐标
559
+ drawBadge(ctx, badgeProps, drawX, y, width, cellHeight);
560
+ }
561
+ }
562
+ }
563
+ }
564
+ ctx.restore();
565
+ });
566
+ });
567
+ // 绘制固定列阴影和边界
568
+ var drawFixedColumns = useMemoizedFn(function (ctx, startRow, endRow, displayWidth, displayHeight) {
569
+ var fixedColumns = columnRenderInfos.filter(function (info) { return info.fixed; });
570
+ if (fixedColumns.length === 0)
571
+ return;
572
+ // 计算固定列的实际右边界(考虑列宽调整)
573
+ var fixedRightEdge = 0;
574
+ fixedColumns.forEach(function (info, index) {
575
+ var actualWidth = getColumnWidth
576
+ ? getColumnWidth(columnRenderInfos.indexOf(info))
577
+ : info.width;
578
+ var rightEdge = info.fixedLeft + actualWidth;
579
+ fixedRightEdge = Math.max(fixedRightEdge, rightEdge);
580
+ });
581
+ // 绘制固定列阴影(只在有横向滚动时显示)
582
+ if (scrollState.scrollLeft > 0 && fixedRightEdge < displayWidth) {
583
+ var shadowWidth = 12; // 增加阴影宽度,使边界更明显
584
+ var gradient = ctx.createLinearGradient(fixedRightEdge, 0, fixedRightEdge + shadowWidth, 0);
585
+ gradient.addColorStop(0, "rgba(0, 0, 0, 0.12)"); // 加深阴影
586
+ gradient.addColorStop(0.6, "rgba(0, 0, 0, 0.04)");
587
+ gradient.addColorStop(1, "rgba(0, 0, 0, 0)");
588
+ ctx.fillStyle = gradient;
589
+ ctx.fillRect(fixedRightEdge, headerHeight, shadowWidth, displayHeight - headerHeight);
590
+ }
591
+ // 绘制固定列右边界线(增强边界视觉效果)
592
+ if (fixedRightEdge > 0 && fixedRightEdge < displayWidth) {
593
+ ctx.strokeStyle = "rgba(0, 0, 0, 0.06)"; // 淡边界线
594
+ ctx.lineWidth = 1;
595
+ ctx.beginPath();
596
+ ctx.moveTo(fixedRightEdge, headerHeight);
597
+ ctx.lineTo(fixedRightEdge, displayHeight);
598
+ ctx.stroke();
599
+ }
600
+ });
601
+ // 绘制边框
602
+ var drawBorders = useMemoizedFn(function (ctx, startRow, endRow, displayWidth, displayHeight, onlyFixed // 是否只绘制fixed列的边框
603
+ ) {
604
+ if (onlyFixed === void 0) { onlyFixed = false; }
605
+ ctx.strokeStyle = COLORS.border;
606
+ ctx.lineWidth = 1;
607
+ // 绘制表头底部边框(只在非fixed时绘制一次,使用2px更明显)
608
+ if (!onlyFixed) {
609
+ ctx.lineWidth = 1;
610
+ ctx.beginPath();
611
+ ctx.moveTo(0, headerHeight);
612
+ ctx.lineTo(displayWidth, headerHeight);
613
+ ctx.stroke();
614
+ ctx.lineWidth = 1; // 恢复为1px
615
+ }
616
+ // 绘制垂直边框(只绘制body部分,从headerHeight开始,表头边框在drawHeaderCell中绘制)
617
+ columnRenderInfos.forEach(function (info, colIndex) {
618
+ var x = info.x, width = info.width, fixed = info.fixed, fixedLeft = info.fixedLeft;
619
+ // 根据 onlyFixed 参数过滤要绘制边框的列
620
+ if (onlyFixed && !fixed)
621
+ return;
622
+ if (!onlyFixed && fixed)
623
+ return;
624
+ // 使用 getColumnWidth 获取列宽(考虑拖拽时的临时宽度)
625
+ var actualWidth = getColumnWidth ? getColumnWidth(colIndex) : width;
626
+ var drawX = fixed
627
+ ? fixedLeft + actualWidth
628
+ : x + actualWidth - scrollState.scrollLeft;
629
+ if (drawX >= 0 && drawX <= displayWidth) {
630
+ ctx.beginPath();
631
+ ctx.moveTo(drawX, headerHeight);
632
+ ctx.lineTo(drawX, displayHeight);
633
+ ctx.stroke();
634
+ }
635
+ });
636
+ var _loop_1 = function (i) {
637
+ var y = headerHeight + i * rowHeight - scrollState.scrollTop;
638
+ if (y >= headerHeight && y <= displayHeight) {
639
+ // 分段绘制,只绘制当前范围(fixed或非fixed)的边框
640
+ columnRenderInfos.forEach(function (info, colIndex) {
641
+ // 根据 onlyFixed 参数过滤
642
+ if (onlyFixed && !info.fixed)
643
+ return;
644
+ if (!onlyFixed && info.fixed)
645
+ return;
646
+ var mergeCellKey = "".concat(i, "-").concat(colIndex);
647
+ var mergeInfo = mergeCellMap === null || mergeCellMap === void 0 ? void 0 : mergeCellMap.get(mergeCellKey);
648
+ // 只有被合并的单元格(skip=true)才跳过绘制上边框
649
+ // 合并单元格的起始行应该正常绘制上边框
650
+ if (mergeInfo && mergeInfo.skip) {
651
+ return;
652
+ }
653
+ var x = info.x, width = info.width, fixed = info.fixed, fixedLeft = info.fixedLeft;
654
+ var actualWidth = getColumnWidth
655
+ ? getColumnWidth(colIndex)
656
+ : width;
657
+ var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
658
+ // 只绘制可见区域的边框
659
+ if (!fixed && (drawX + actualWidth < 0 || drawX > displayWidth)) {
660
+ return;
661
+ }
662
+ ctx.beginPath();
663
+ ctx.moveTo(Math.max(drawX, 0), y);
664
+ ctx.lineTo(Math.min(drawX + actualWidth, displayWidth), y);
665
+ ctx.stroke();
666
+ });
667
+ }
668
+ };
669
+ // 绘制水平边框
670
+ for (var i = startRow; i <= endRow; i++) {
671
+ _loop_1(i);
672
+ }
673
+ // 为合并单元格绘制底部边框
674
+ if (mergeCellMap && mergeCellMap.size > 0) {
675
+ mergeCellMap.forEach(function (mergeInfo, key) {
676
+ if (!mergeInfo.skip && mergeInfo.rowSpan > 1) {
677
+ var rowIndex = mergeInfo.rowIndex, colIndex = mergeInfo.colIndex, rowSpan = mergeInfo.rowSpan;
678
+ // 只绘制在可视范围内的
679
+ if (rowIndex >= startRow && rowIndex <= endRow) {
680
+ var columnInfo = columnRenderInfos[colIndex];
681
+ if (!columnInfo)
682
+ return;
683
+ // 根据 onlyFixed 参数过滤
684
+ if (onlyFixed && !columnInfo.fixed)
685
+ return;
686
+ if (!onlyFixed && columnInfo.fixed)
687
+ return;
688
+ var x = columnInfo.x, width = columnInfo.width, fixed = columnInfo.fixed, fixedLeft = columnInfo.fixedLeft;
689
+ var actualWidth = getColumnWidth
690
+ ? getColumnWidth(colIndex)
691
+ : width;
692
+ var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
693
+ // 绘制合并单元格的底边框
694
+ var bottomY = headerHeight +
695
+ (rowIndex + rowSpan) * rowHeight -
696
+ scrollState.scrollTop;
697
+ if (bottomY >= headerHeight && bottomY <= displayHeight) {
698
+ ctx.beginPath();
699
+ ctx.moveTo(drawX, bottomY);
700
+ ctx.lineTo(drawX + actualWidth, bottomY);
701
+ ctx.stroke();
702
+ }
703
+ }
704
+ }
705
+ });
706
+ }
707
+ });
708
+ /**
709
+ * 通用的固定行绘制函数
710
+ * @param yCalculator - Y坐标计算函数
711
+ * @param rowType - 行类型:'top' | 'bottom' | 'summary'
712
+ */
713
+ var drawFixedRow = useMemoizedFn(function (ctx, rowIndex, yCalculator, displayWidth, displayHeight, onlyFixed, rowType) {
714
+ if (onlyFixed === void 0) { onlyFixed = false; }
715
+ if (rowType === void 0) { rowType = "top"; }
716
+ var record = processedDataSource[rowIndex];
717
+ if (!record)
718
+ return;
719
+ var fixedY = yCalculator(rowIndex);
720
+ ctx.save();
721
+ var recordKey = getRowKey(record, rowIndex);
722
+ var isSelected = state.selectedRowKeys.includes(recordKey);
723
+ var isHover = state.hoverRowIndex === rowIndex;
724
+ columnRenderInfos.forEach(function (info, colIndex) {
725
+ var _a;
726
+ var column = info.column, x = info.x, width = info.width, fixed = info.fixed, fixedLeft = info.fixedLeft;
727
+ // 根据 onlyFixed 参数过滤
728
+ if (onlyFixed && !fixed)
729
+ return;
730
+ if (!onlyFixed && fixed)
731
+ return;
732
+ var actualWidth = getColumnWidth ? getColumnWidth(colIndex) : width;
733
+ var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
734
+ // 跳过不可见的列
735
+ if (!fixed && (drawX + actualWidth < 0 || drawX > displayWidth)) {
736
+ return;
737
+ }
738
+ // 绘制背景
739
+ var bgColor = COLORS.white;
740
+ // 如果有自定义样式
741
+ if ((_a = fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.style) === null || _a === void 0 ? void 0 : _a.backgroundColor) {
742
+ bgColor = fixedRowsConfig.style.backgroundColor;
743
+ }
744
+ else {
745
+ // 使用默认逻辑(斑马纹、选中、hover)
746
+ if (isSelected) {
747
+ bgColor = COLORS.selectedBg;
748
+ }
749
+ else if (isHover) {
750
+ bgColor = COLORS.hoverBg;
751
+ }
752
+ else if (striped && rowIndex % 2 === 1) {
753
+ bgColor = COLORS.stripedBg;
754
+ }
755
+ }
756
+ ctx.fillStyle = bgColor;
757
+ ctx.fillRect(drawX, fixedY, actualWidth, rowHeight);
758
+ // 处理序号列
759
+ if (column.key === "__index__") {
760
+ // 合计行序号列显示空
761
+ if (rowType === "summary")
762
+ return;
763
+ var indexText = String(rowIndex + 1);
764
+ ctx.fillStyle = COLORS.text;
765
+ ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
766
+ ctx.textBaseline = "middle";
767
+ setTextAlign(ctx, "center");
768
+ var textX = drawX + actualWidth / 2;
769
+ var textY = fixedY + rowHeight / 2;
770
+ ctx.fillText(indexText, textX, textY);
771
+ return;
772
+ }
773
+ // 处理选择框列
774
+ if (column.key === "__selection__") {
775
+ // 合计行选择框列显示空
776
+ if (rowType === "summary")
777
+ return;
778
+ var checkboxX = drawX + actualWidth / 2;
779
+ var textY = fixedY + rowHeight / 2;
780
+ drawCheckbox(ctx, checkboxX, textY, isSelected, false, false);
781
+ return;
782
+ }
783
+ // 获取单元格值
784
+ var dataIndex = column.dataIndex || column.key;
785
+ var cellValue = record[dataIndex];
786
+ // 渲染单元格内容
787
+ var cellText = formatCellValue(cellValue, column);
788
+ // 检查是否有render函数且返回自定义ReactNode
789
+ var shouldRenderInCanvas = true;
790
+ if (column.render) {
791
+ var rendered = column.render(cellValue, record, rowIndex);
792
+ // 如果返回的不是字符串或数字,说明是自定义组件,应该在覆盖层渲染,不在 Canvas 渲染
793
+ if (typeof rendered !== "string" &&
794
+ typeof rendered !== "number" &&
795
+ rendered !== null &&
796
+ rendered !== undefined) {
797
+ shouldRenderInCanvas = false;
798
+ }
799
+ }
800
+ // 只有在需要在Canvas中渲染时才绘制文本
801
+ if (cellText && shouldRenderInCanvas) {
802
+ ctx.fillStyle = COLORS.text;
803
+ // 合计行字体加粗
804
+ ctx.font =
805
+ rowType === "summary"
806
+ ? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
807
+ : "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
808
+ ctx.textBaseline = "middle";
809
+ var align = column.align || "left";
810
+ setTextAlign(ctx, align);
811
+ var padding = 16;
812
+ var maxWidth = actualWidth - padding * 2;
813
+ var textY = fixedY + rowHeight / 2;
814
+ var textX = calculateTextX(drawX, actualWidth, align, padding);
815
+ var truncatedText = truncateText(ctx, cellText, maxWidth);
816
+ ctx.fillText(truncatedText, textX, textY);
817
+ }
818
+ });
819
+ ctx.restore();
820
+ });
821
+ // 绘制固定顶部行(在固定位置)
822
+ var drawFixedTopRow = useMemoizedFn(function (ctx, rowIndex, displayWidth, displayHeight, onlyFixed) {
823
+ if (onlyFixed === void 0) { onlyFixed = false; }
824
+ drawFixedRow(ctx, rowIndex, function (idx) { return headerHeight + idx * rowHeight; }, displayWidth, displayHeight, onlyFixed, "top");
825
+ });
826
+ /**
827
+ * 通用的固定行边框绘制函数
828
+ * @param startYCalculator - 起始Y坐标计算函数
829
+ * @param borderType - 边框类型:'normal' | 'summary'(summary会加粗)
830
+ */
831
+ var drawFixedRowsBorder = useMemoizedFn(function (ctx, rowCount, startYCalculator, displayWidth, displayHeight, onlyFixed, borderType) {
832
+ if (onlyFixed === void 0) { onlyFixed = false; }
833
+ if (borderType === void 0) { borderType = "normal"; }
834
+ ctx.save();
835
+ ctx.strokeStyle = COLORS.border;
836
+ ctx.lineWidth = borderType === "summary" ? 2 : 1; // 合计行边框加粗
837
+ var startY = startYCalculator();
838
+ // 计算固定列的右边界
839
+ var fixedColumnsRightEdge = 0;
840
+ if (onlyFixed) {
841
+ fixedColumnsRightEdge = calculateFixedColumnsRightEdge();
842
+ }
843
+ // 绘制水平边框
844
+ for (var i = 0; i <= rowCount; i++) {
845
+ var y = startY + i * rowHeight;
846
+ ctx.beginPath();
847
+ if (onlyFixed) {
848
+ // 固定列区域:只绘制固定列宽度的水平边框
849
+ ctx.moveTo(0, y);
850
+ ctx.lineTo(fixedColumnsRightEdge, y);
851
+ }
852
+ else {
853
+ // 非固定列区域:绘制整行水平边框
854
+ ctx.moveTo(0, y);
855
+ ctx.lineTo(displayWidth, y);
856
+ }
857
+ ctx.stroke();
858
+ }
859
+ // 绘制垂直边框
860
+ ctx.lineWidth = 1; // 垂直边框始终为1px
861
+ columnRenderInfos.forEach(function (info, colIndex) {
862
+ var x = info.x, width = info.width, fixed = info.fixed, fixedLeft = info.fixedLeft;
863
+ // 根据 onlyFixed 参数过滤要绘制边框的列
864
+ if (onlyFixed && !fixed)
865
+ return;
866
+ if (!onlyFixed && fixed)
867
+ return;
868
+ var actualWidth = getColumnWidth ? getColumnWidth(colIndex) : width;
869
+ var drawX = fixed
870
+ ? fixedLeft + actualWidth
871
+ : x + actualWidth - scrollState.scrollLeft;
872
+ if (drawX >= 0 && drawX <= displayWidth) {
873
+ ctx.beginPath();
874
+ ctx.moveTo(drawX, startY);
875
+ ctx.lineTo(drawX, startY + rowCount * rowHeight);
876
+ ctx.stroke();
877
+ }
878
+ });
879
+ ctx.restore();
880
+ });
881
+ // 绘制固定顶部行的边框
882
+ var drawFixedTopRowsBorder = useMemoizedFn(function (ctx, rowCount, displayWidth, displayHeight, onlyFixed) {
883
+ if (onlyFixed === void 0) { onlyFixed = false; }
884
+ drawFixedRowsBorder(ctx, rowCount, function () { return headerHeight; }, displayWidth, displayHeight, onlyFixed, "normal");
885
+ });
886
+ // 绘制固定底部行(在固定位置)
887
+ var drawFixedBottomRow = useMemoizedFn(function (ctx, rowIndex, relativeIndex, displayWidth, displayHeight, onlyFixed) {
888
+ if (onlyFixed === void 0) { onlyFixed = false; }
889
+ drawFixedRow(ctx, rowIndex, function () {
890
+ var fixedSummaryHeight = fixedSummaryRow && hasSummaryRow ? rowHeight : 0;
891
+ return (displayHeight -
892
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
893
+ fixedSummaryHeight -
894
+ ((fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.bottomCount) || 0) * rowHeight +
895
+ relativeIndex * rowHeight);
896
+ }, displayWidth, displayHeight, onlyFixed, "bottom");
897
+ });
898
+ // 绘制固定底部行的边框
899
+ var drawFixedBottomRowsBorder = useMemoizedFn(function (ctx, rowCount, displayWidth, displayHeight, onlyFixed) {
900
+ if (onlyFixed === void 0) { onlyFixed = false; }
901
+ drawFixedRowsBorder(ctx, rowCount, function () {
902
+ var fixedSummaryHeight = fixedSummaryRow && hasSummaryRow ? rowHeight : 0;
903
+ return (displayHeight -
904
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
905
+ fixedSummaryHeight -
906
+ rowCount * rowHeight);
907
+ }, displayWidth, displayHeight, onlyFixed, "normal");
908
+ });
909
+ // 绘制固定合计行(在固定位置)
910
+ var drawFixedSummaryRow = useMemoizedFn(function (ctx, rowIndex, fixedY, displayWidth, displayHeight, onlyFixed) {
911
+ if (onlyFixed === void 0) { onlyFixed = false; }
912
+ drawFixedRow(ctx, rowIndex, function () { return fixedY; }, displayWidth, displayHeight, onlyFixed, "summary");
913
+ });
914
+ // 绘制固定合计行的边框
915
+ var drawFixedSummaryRowBorder = useMemoizedFn(function (ctx, rowIndex, fixedY, displayWidth, displayHeight, onlyFixed) {
916
+ if (onlyFixed === void 0) { onlyFixed = false; }
917
+ drawFixedRowsBorder(ctx, 1, // 合计行只有一行
918
+ function () { return fixedY; }, displayWidth, displayHeight, onlyFixed, "summary" // 使用summary类型,边框会加粗
919
+ );
920
+ });
921
+ // 绘制框选区域
922
+ var drawCellSelection = useMemoizedFn(function (ctx, displayWidth, displayHeight) {
923
+ if (!state.cellSelection)
924
+ return;
925
+ var _a = state.cellSelection, startRow = _a.startRow, endRow = _a.endRow, startCol = _a.startCol, endCol = _a.endCol;
926
+ // 计算选择区域的边界
927
+ var minRow = Math.min(startRow, endRow);
928
+ var maxRow = Math.max(startRow, endRow);
929
+ var minCol = Math.min(startCol, endCol);
930
+ var maxCol = Math.max(startCol, endCol);
931
+ // 计算fixed列的右边界(用于裁剪非fixed列的选中效果)
932
+ var fixedColumnsRightEdge = 0;
933
+ columnRenderInfos.forEach(function (info) {
934
+ if (info.fixed) {
935
+ var rightEdge = info.fixedLeft + info.width;
936
+ fixedColumnsRightEdge = Math.max(fixedColumnsRightEdge, rightEdge);
937
+ }
938
+ });
939
+ // 记录已绘制的合并单元格,避免重复绘制
940
+ var drawnMergeCells = new Set();
941
+ // 记录选区的边界坐标
942
+ var selectionTop = Infinity;
943
+ var selectionBottom = -Infinity;
944
+ var selectionLeft = Infinity;
945
+ var selectionRight = -Infinity;
946
+ // 计算固定行的范围
947
+ var fixedTopRowsCount = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.topCount) || fixedRowsCount || 0;
948
+ var fixedBottomRowsCount = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.bottomCount) || 0;
949
+ var bottomRowsStartIndex = processedDataSource.length -
950
+ fixedBottomRowsCount -
951
+ (fixedSummaryRow && hasSummaryRow ? 1 : 0);
952
+ var summaryRowIndex = hasSummaryRow
953
+ ? processedDataSource.length - 1
954
+ : -1;
955
+ // 绘制选中的单元格背景
956
+ for (var row = minRow; row <= maxRow; row++) {
957
+ // row=-1 表示表头行
958
+ var y = void 0;
959
+ var cellHeight = void 0;
960
+ var isFixedRow = false;
961
+ if (row === -1) {
962
+ // 表头行
963
+ y = 0;
964
+ cellHeight = headerHeight;
965
+ }
966
+ else if (row < fixedTopRowsCount) {
967
+ // 固定顶部行
968
+ y = headerHeight + row * rowHeight;
969
+ cellHeight = rowHeight;
970
+ isFixedRow = true;
971
+ }
972
+ else if (fixedBottomRowsCount > 0 &&
973
+ row >= bottomRowsStartIndex &&
974
+ row < bottomRowsStartIndex + fixedBottomRowsCount) {
975
+ // 固定底部行
976
+ var relativeIndex = row - bottomRowsStartIndex;
977
+ var fixedSummaryHeight = fixedSummaryRow && hasSummaryRow ? rowHeight : 0;
978
+ y =
979
+ displayHeight -
980
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
981
+ fixedSummaryHeight -
982
+ fixedBottomRowsCount * rowHeight +
983
+ relativeIndex * rowHeight;
984
+ cellHeight = rowHeight;
985
+ isFixedRow = true;
986
+ }
987
+ else if (fixedSummaryRow && row === summaryRowIndex) {
988
+ // 固定合计行
989
+ var summaryRowY = displayHeight -
990
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
991
+ rowHeight;
992
+ y = summaryRowY;
993
+ cellHeight = rowHeight;
994
+ isFixedRow = true;
995
+ }
996
+ else {
997
+ // 普通数据行
998
+ // 需要调整行索引,减去固定顶部行的数量
999
+ var adjustedRow = row - fixedTopRowsCount;
1000
+ y =
1001
+ headerHeight +
1002
+ fixedTopRowsCount * rowHeight +
1003
+ adjustedRow * rowHeight -
1004
+ scrollState.scrollTop;
1005
+ cellHeight = rowHeight;
1006
+ // 数据行的可见性检查
1007
+ if (y + cellHeight < headerHeight + fixedTopRowsCount * rowHeight ||
1008
+ y > displayHeight)
1009
+ continue;
1010
+ }
1011
+ for (var col = minCol; col <= maxCol; col++) {
1012
+ var columnInfo = columnRenderInfos[col];
1013
+ if (!columnInfo)
1014
+ continue;
1015
+ // 表头行不处理合并单元格
1016
+ if (row !== -1) {
1017
+ // 检查是否是合并单元格
1018
+ var mergeCellKey = "".concat(row, "-").concat(col);
1019
+ var mergeInfo = mergeCellMap === null || mergeCellMap === void 0 ? void 0 : mergeCellMap.get(mergeCellKey);
1020
+ // 如果是被合并的单元格,跳过(由主单元格统一绘制)
1021
+ if (mergeInfo && mergeInfo.skip) {
1022
+ continue;
1023
+ }
1024
+ // 如果是合并单元格的主单元格,检查是否已绘制
1025
+ if (mergeInfo && !mergeInfo.skip) {
1026
+ var mergeKey = "".concat(mergeInfo.rowIndex, "-").concat(mergeInfo.colIndex);
1027
+ if (drawnMergeCells.has(mergeKey)) {
1028
+ continue;
1029
+ }
1030
+ drawnMergeCells.add(mergeKey);
1031
+ }
1032
+ // 计算单元格高度(考虑合并)
1033
+ if (mergeInfo && mergeInfo.rowSpan > 1) {
1034
+ cellHeight = rowHeight * mergeInfo.rowSpan;
1035
+ }
1036
+ }
1037
+ var x = columnInfo.x, width = columnInfo.width, fixed = columnInfo.fixed, fixedLeft = columnInfo.fixedLeft;
1038
+ var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
1039
+ var drawWidth = width;
1040
+ if (!fixed && (drawX + width < 0 || drawX > displayWidth))
1041
+ continue;
1042
+ // 对于非fixed列,需要裁剪掉被fixed列遮挡的部分
1043
+ if (!fixed && drawX < fixedColumnsRightEdge) {
1044
+ var overlap = fixedColumnsRightEdge - drawX;
1045
+ if (overlap >= width) {
1046
+ // 完全被遮挡,跳过
1047
+ continue;
1048
+ }
1049
+ // 部分被遮挡,裁剪掉被遮挡的部分
1050
+ drawX = fixedColumnsRightEdge;
1051
+ drawWidth = width - overlap;
1052
+ }
1053
+ // 绘制选中背景
1054
+ ctx.fillStyle = COLORS.selectionBg;
1055
+ var clipY = void 0;
1056
+ var clipHeight = void 0;
1057
+ if (row === -1 || isFixedRow) {
1058
+ // 表头行或固定行,直接使用y和height
1059
+ clipY = y;
1060
+ clipHeight = cellHeight;
1061
+ }
1062
+ else {
1063
+ // 普通数据行,需要裁剪到可见区域(避免被表头或固定行遮挡)
1064
+ var minVisibleY = headerHeight + fixedTopRowsCount * rowHeight;
1065
+ clipY = Math.max(y, minVisibleY);
1066
+ var actualBottom = y + cellHeight;
1067
+ clipHeight = Math.max(0, Math.min(actualBottom - clipY, displayHeight - clipY));
1068
+ }
1069
+ if (clipHeight > 0) {
1070
+ ctx.fillRect(drawX, clipY, drawWidth, clipHeight);
1071
+ // 更新选区边界
1072
+ selectionTop = Math.min(selectionTop, clipY);
1073
+ selectionBottom = Math.max(selectionBottom, clipY + clipHeight);
1074
+ selectionLeft = Math.min(selectionLeft, drawX);
1075
+ selectionRight = Math.max(selectionRight, drawX + drawWidth);
1076
+ }
1077
+ }
1078
+ }
1079
+ // 绘制整个选区的外边框
1080
+ if (selectionLeft !== Infinity && selectionTop !== Infinity) {
1081
+ ctx.strokeStyle = COLORS.primary;
1082
+ ctx.lineWidth = 2;
1083
+ ctx.strokeRect(selectionLeft, selectionTop, selectionRight - selectionLeft, selectionBottom - selectionTop);
1084
+ }
1085
+ });
1086
+ // 绘制滚动条
1087
+ var drawScrollbars = useMemoizedFn(function (ctx, displayWidth, displayHeight) {
1088
+ var scrollbarSize = SCROLLBAR_SIZE;
1089
+ var scrollbarPadding = SCROLLBAR_PADDING;
1090
+ // 绘制垂直滚动条
1091
+ if (needVerticalScrollbar) {
1092
+ var scrollbarX = displayWidth - scrollbarSize;
1093
+ var scrollbarY = headerHeight;
1094
+ var scrollbarHeight = needHorizontalScrollbar
1095
+ ? displayHeight - scrollbarSize - headerHeight
1096
+ : displayHeight - headerHeight;
1097
+ // 滚动条轨道
1098
+ ctx.fillStyle = COLORS.scrollbarTrack;
1099
+ ctx.fillRect(scrollbarX, scrollbarY, scrollbarSize, scrollbarHeight);
1100
+ // 滚动条滑块
1101
+ ctx.fillStyle = scrollState.isDraggingVertical
1102
+ ? COLORS.scrollbarThumbActive
1103
+ : COLORS.scrollbarThumb;
1104
+ ctx.fillRect(scrollbarX + scrollbarPadding, verticalScrollbarTop + headerHeight + scrollbarPadding, scrollbarSize - scrollbarPadding * 2, verticalScrollbarHeight - scrollbarPadding * 2);
1105
+ }
1106
+ // 绘制水平滚动条
1107
+ if (needHorizontalScrollbar) {
1108
+ var scrollbarX = 0;
1109
+ var scrollbarY = displayHeight - scrollbarSize;
1110
+ var scrollbarWidth = needVerticalScrollbar
1111
+ ? displayWidth - scrollbarSize
1112
+ : displayWidth;
1113
+ // 滚动条轨道
1114
+ ctx.fillStyle = COLORS.scrollbarTrack;
1115
+ ctx.fillRect(scrollbarX, scrollbarY, scrollbarWidth, scrollbarSize);
1116
+ // 滚动条滑块
1117
+ ctx.fillStyle = scrollState.isDraggingHorizontal
1118
+ ? COLORS.scrollbarThumbActive
1119
+ : COLORS.scrollbarThumb;
1120
+ ctx.fillRect(horizontalScrollbarLeft + scrollbarPadding, scrollbarY + scrollbarPadding, horizontalScrollbarWidth - scrollbarPadding * 2, scrollbarSize - scrollbarPadding * 2);
1121
+ }
1122
+ // 绘制滚动条交叉处
1123
+ if (needVerticalScrollbar && needHorizontalScrollbar) {
1124
+ ctx.fillStyle = COLORS.scrollbarTrack;
1125
+ ctx.fillRect(displayWidth - scrollbarSize, displayHeight - scrollbarSize, scrollbarSize, scrollbarSize);
1126
+ }
1127
+ });
1128
+ /**
1129
+ * 通用渲染函数:先绘制非fixed,再绘制fixed(包括边框)
1130
+ * 遵循绘制顺序:非fixed内容 -> 非fixed边框 -> fixed内容 -> fixed边框
1131
+ */
1132
+ var renderWithFixedLayer = useMemoizedFn(function (ctx, renderContent, renderBorder) {
1133
+ // 1. 绘制非fixed层
1134
+ renderContent(false);
1135
+ if (bordered && renderBorder) {
1136
+ renderBorder(false);
1137
+ }
1138
+ // 2. 绘制fixed层(覆盖在非fixed之上)
1139
+ renderContent(true);
1140
+ if (bordered && renderBorder) {
1141
+ renderBorder(true);
1142
+ }
1143
+ });
1144
+ // 绘制表格
1145
+ var drawTable = useMemoizedFn(function () {
1146
+ var canvas = canvasRef.current;
1147
+ if (!canvas)
1148
+ return;
1149
+ var ctx = canvas.getContext("2d");
1150
+ if (!ctx)
1151
+ return;
1152
+ // 设置 canvas 尺寸
1153
+ var displayWidth = containerWidth;
1154
+ var displayHeight = containerHeight;
1155
+ canvas.width = displayWidth * dpr;
1156
+ canvas.height = displayHeight * dpr;
1157
+ canvas.style.width = "".concat(displayWidth, "px");
1158
+ canvas.style.height = "".concat(displayHeight, "px");
1159
+ ctx.scale(dpr, dpr);
1160
+ // 清空画布
1161
+ ctx.clearRect(0, 0, displayWidth, displayHeight);
1162
+ // 绘制背景
1163
+ ctx.fillStyle = COLORS.white;
1164
+ ctx.fillRect(0, 0, displayWidth, displayHeight);
1165
+ // 计算固定行的数量(支持多种配置方式)
1166
+ var fixedTopRowsCount = fixedRowsCount || (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.topCount) || 0;
1167
+ var fixedBottomRowsCount = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.bottomCount) || 0;
1168
+ // 计算固定行占用的高度
1169
+ var fixedTopRowsHeight = fixedTopRowsCount * rowHeight;
1170
+ var fixedBottomRowsHeight = fixedBottomRowsCount * rowHeight;
1171
+ var fixedSummaryRowHeight = fixedSummaryRow && hasSummaryRow ? rowHeight : 0;
1172
+ // 计算可视区域的数据区域高度(不包括表头、固定行和固定合计行)
1173
+ var dataAreaHeight = displayHeight -
1174
+ headerHeight -
1175
+ fixedTopRowsHeight -
1176
+ fixedBottomRowsHeight -
1177
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
1178
+ fixedSummaryRowHeight;
1179
+ // 计算可视区域(从固定行之后开始)
1180
+ var scrollStartRow = fixedTopRowsCount + Math.floor(scrollState.scrollTop / rowHeight);
1181
+ var scrollEndRow = Math.min(fixedTopRowsCount +
1182
+ Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length - fixedBottomRowsCount
1183
+ // 注意:不再减去合计行数量,因为合计行的空间已在 dataAreaHeight 中考虑
1184
+ );
1185
+ // 1. 绘制可滚动区域的数据行(包括边框)
1186
+ renderWithFixedLayer(ctx,
1187
+ // 渲染行内容
1188
+ function (onlyFixed) {
1189
+ for (var i = scrollStartRow; i < scrollEndRow; i++) {
1190
+ drawRow(ctx, i, displayWidth, displayHeight, onlyFixed);
1191
+ }
1192
+ },
1193
+ // 渲染边框
1194
+ function (onlyFixed) {
1195
+ drawBorders(ctx, scrollStartRow, scrollEndRow, displayWidth, displayHeight, onlyFixed);
1196
+ });
1197
+ // 2. 绘制固定的顶部行(如果有)
1198
+ if (fixedTopRowsCount > 0) {
1199
+ ctx.save();
1200
+ renderWithFixedLayer(ctx,
1201
+ // 渲染行内容
1202
+ function (onlyFixed) {
1203
+ for (var i = 0; i < fixedTopRowsCount; i++) {
1204
+ drawFixedTopRow(ctx, i, displayWidth, displayHeight, onlyFixed);
1205
+ }
1206
+ },
1207
+ // 渲染边框
1208
+ function (onlyFixed) {
1209
+ drawFixedTopRowsBorder(ctx, fixedTopRowsCount, displayWidth, displayHeight, onlyFixed);
1210
+ });
1211
+ ctx.restore();
1212
+ }
1213
+ // 3. 绘制固定的底部行(如果有,不包括合计行)
1214
+ if (fixedBottomRowsCount > 0) {
1215
+ ctx.save();
1216
+ var bottomRowsStartIndex_1 = processedDataSource.length -
1217
+ fixedBottomRowsCount -
1218
+ (fixedSummaryRow && hasSummaryRow ? 1 : 0);
1219
+ renderWithFixedLayer(ctx,
1220
+ // 渲染行内容
1221
+ function (onlyFixed) {
1222
+ for (var i = 0; i < fixedBottomRowsCount; i++) {
1223
+ var rowIndex = bottomRowsStartIndex_1 + i;
1224
+ drawFixedBottomRow(ctx, rowIndex, i, displayWidth, displayHeight, onlyFixed);
1225
+ }
1226
+ },
1227
+ // 渲染边框
1228
+ function (onlyFixed) {
1229
+ drawFixedBottomRowsBorder(ctx, fixedBottomRowsCount, displayWidth, displayHeight, onlyFixed);
1230
+ });
1231
+ ctx.restore();
1232
+ }
1233
+ // 7. 绘制固定列阴影
1234
+ drawFixedColumns(ctx, scrollStartRow, scrollEndRow, displayWidth, displayHeight);
1235
+ // 4. 绘制固定合计行(如果启用)
1236
+ if (fixedSummaryRow && hasSummaryRow) {
1237
+ var summaryRowIndex_1 = processedDataSource.length - 1;
1238
+ var summaryRowY_1 = displayHeight -
1239
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
1240
+ rowHeight;
1241
+ ctx.save();
1242
+ renderWithFixedLayer(ctx,
1243
+ // 渲染行内容
1244
+ function (onlyFixed) {
1245
+ drawFixedSummaryRow(ctx, summaryRowIndex_1, summaryRowY_1, displayWidth, displayHeight, onlyFixed);
1246
+ },
1247
+ // 渲染边框
1248
+ function (onlyFixed) {
1249
+ drawFixedSummaryRowBorder(ctx, summaryRowIndex_1, summaryRowY_1, displayWidth, displayHeight, onlyFixed);
1250
+ });
1251
+ ctx.restore();
1252
+ }
1253
+ // 6. 绘制表头(最后绘制,确保始终在最上层,不会被覆盖)
1254
+ drawHeader(ctx, displayWidth, displayHeight);
1255
+ // 绘制滚动条(最后绘制,在表头之上)
1256
+ drawScrollbars(ctx, displayWidth, displayHeight);
1257
+ // 绘制框选区域
1258
+ if (state.cellSelection) {
1259
+ drawCellSelection(ctx, displayWidth, displayHeight);
1260
+ }
1261
+ });
1262
+ // 监听变化,自动重绘
1263
+ useEffect(function () {
1264
+ drawTable();
1265
+ }, [
1266
+ containerWidth,
1267
+ containerHeight,
1268
+ scrollState.scrollTop,
1269
+ scrollState.scrollLeft,
1270
+ scrollState.isDraggingVertical,
1271
+ scrollState.isDraggingHorizontal,
1272
+ state.hoverRowIndex,
1273
+ state.selectedRowKeys,
1274
+ state.sortField,
1275
+ state.sortOrder,
1276
+ state.cellSelection,
1277
+ processedDataSource,
1278
+ columnRenderInfos,
1279
+ bordered,
1280
+ striped,
1281
+ resizeState,
1282
+ drawTable,
1283
+ ]);
1284
+ return {
1285
+ drawTable: drawTable,
1286
+ };
1287
+ };
1288
+
1289
+ export { useTableRender };