zmdms-webui 2.3.5 → 2.3.7
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 +134 -41
- package/dist/es/canvastable/components/CanvasTableMenu.js +1 -2
- package/dist/es/canvastable/hooks/useHeaderHeight.js +18 -19
- package/dist/es/canvastable/hooks/useProcessedColumns.js +17 -7
- package/dist/es/canvastable/hooks/useSummaryRow.js +4 -11
- package/dist/es/canvastable/hooks/useTableInteraction.js +5 -3
- package/dist/es/canvastable/hooks/useTableRender.js +198 -74
- package/dist/es/canvastable/interface.d.ts +93 -5
- package/dist/es/canvastable/utils/columnHelpers.js +6 -2
- package/dist/es/canvastable/utils/constants.js +11 -3
- package/dist/es/canvastable/utils/multiHeaderHelpers.js +119 -1
- package/dist/es/canvastable/utils/tableCalculations.js +51 -11
- package/dist/index.build.d.ts +1 -1
- package/package.json +1 -1
|
@@ -8,9 +8,9 @@ import 'react/jsx-runtime';
|
|
|
8
8
|
import 'zmdms-utils';
|
|
9
9
|
import '../../node_modules/exceljs/dist/exceljs.min.js';
|
|
10
10
|
import 'dayjs';
|
|
11
|
-
import { COLORS, FONT_SIZE, FONT_FAMILY, SCROLLBAR_SIZE, SCROLLBAR_PADDING } from '../utils/constants.js';
|
|
11
|
+
import { COLORS, FONT_WEIGHT, FONT_SIZE, FONT_FAMILY, SCROLLBAR_SIZE, SCROLLBAR_PADDING } from '../utils/constants.js';
|
|
12
12
|
import { drawCheckbox, wrapText, truncateText, drawFilterIcon, drawSortIcon } from '../utils/canvasDrawHelpers.js';
|
|
13
|
-
import { getMaxDepth, flattenHeaders, getLeafColumns } from '../utils/multiHeaderHelpers.js';
|
|
13
|
+
import { getMaxDepth, flattenHeaders, calculateLayerHeights, getLeafColumns } from '../utils/multiHeaderHelpers.js';
|
|
14
14
|
import { extractCellText } from '../utils/cellHelpers.js';
|
|
15
15
|
import { formatCellValue } from '../utils/formatHelpers.js';
|
|
16
16
|
import { calculateSelectionState, calculateIconArea } from '../utils/interactionHelpers.js';
|
|
@@ -19,12 +19,21 @@ import { calculateSelectionState, calculateIconArea } from '../utils/interaction
|
|
|
19
19
|
* 表格渲染 Hook
|
|
20
20
|
*/
|
|
21
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.summaryFixed, summaryFixed = _c === void 0 ? false : _c;
|
|
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, baseHeaderHeight = params.baseHeaderHeight, 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.summaryFixed, summaryFixed = _c === void 0 ? false : _c;
|
|
23
23
|
// 判断是否是多级表头
|
|
24
24
|
var maxDepth = useMemo(function () { return getMaxDepth(columns); }, [columns]);
|
|
25
25
|
var isMultiHeader = maxDepth > 1;
|
|
26
26
|
// 扁平化多级表头(如果需要)
|
|
27
27
|
var flattenedHeaderRows = useMemo(function () { return (isMultiHeader ? flattenHeaders(columns) : []); }, [columns, isMultiHeader]);
|
|
28
|
+
// 计算多级表头每一层的高度(考虑换行)
|
|
29
|
+
// 使用 baseHeaderHeight 或默认计算出的基础高度
|
|
30
|
+
var _d = useMemo(function () {
|
|
31
|
+
var maxDepth = getMaxDepth(columns);
|
|
32
|
+
// 如果提供了 baseHeaderHeight,使用它;否则根据 headerHeight 和深度计算
|
|
33
|
+
var baseHeight = baseHeaderHeight ||
|
|
34
|
+
(maxDepth > 1 ? Math.floor(headerHeight / maxDepth) : headerHeight);
|
|
35
|
+
return calculateLayerHeights(columns, baseHeight, columnRenderInfos);
|
|
36
|
+
}, [columns, headerHeight, baseHeaderHeight, columnRenderInfos]), layerHeights = _d.layerHeights, layerStartY = _d.layerStartY;
|
|
28
37
|
var dpr = window.devicePixelRatio || 1;
|
|
29
38
|
// ==================== 通用辅助函数 ====================
|
|
30
39
|
/**
|
|
@@ -45,7 +54,7 @@ var useTableRender = function (params) {
|
|
|
45
54
|
*/
|
|
46
55
|
var calculateTextX = function (drawX, actualWidth, align, padding) {
|
|
47
56
|
if (align === void 0) { align = "left"; }
|
|
48
|
-
if (padding === void 0) { padding =
|
|
57
|
+
if (padding === void 0) { padding = 5; }
|
|
49
58
|
if (align === "center") {
|
|
50
59
|
return drawX + actualWidth / 2;
|
|
51
60
|
}
|
|
@@ -63,8 +72,6 @@ var useTableRender = function (params) {
|
|
|
63
72
|
};
|
|
64
73
|
// 绘制角标的辅助函数(三角形标记,类似Excel批注)
|
|
65
74
|
var drawBadge = useMemoizedFn(function (ctx, badge, cellX, cellY, cellWidth, cellHeight) {
|
|
66
|
-
if (!badge.text)
|
|
67
|
-
return;
|
|
68
75
|
var triangleSize = 12; // 三角形边长
|
|
69
76
|
var position = badge.position || "top-right";
|
|
70
77
|
var color = badge.color || "#ff4d4f";
|
|
@@ -120,9 +127,8 @@ var useTableRender = function (params) {
|
|
|
120
127
|
ctx.fillRect(drawX, 0, width, headerHeight);
|
|
121
128
|
// 表头文本
|
|
122
129
|
ctx.fillStyle = COLORS.text;
|
|
123
|
-
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
130
|
+
ctx.font = "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
124
131
|
ctx.textBaseline = "middle";
|
|
125
|
-
var textX = drawX + 16;
|
|
126
132
|
var textY = headerHeight / 2;
|
|
127
133
|
// 绘制选择框
|
|
128
134
|
if (column.key === "__selection__") {
|
|
@@ -135,26 +141,65 @@ var useTableRender = function (params) {
|
|
|
135
141
|
else {
|
|
136
142
|
// 计算图标占用的宽度
|
|
137
143
|
var iconArea = calculateIconArea(column, width);
|
|
138
|
-
//
|
|
139
|
-
var
|
|
144
|
+
// 确定表头对齐方式,优先使用列的 headerAlign,其次是 align,最后是默认值
|
|
145
|
+
var headerAlign = column.headerAlign || column.align || "center";
|
|
140
146
|
// 绘制文本(如果 title 是 React 元素,跳过Canvas渲染,在覆盖层中渲染)
|
|
141
147
|
if (!isValidElement(column.title)) {
|
|
142
148
|
var titleText = String(column.title || "");
|
|
149
|
+
// 根据对齐方式和图标情况设置文本位置
|
|
150
|
+
var textX_1;
|
|
151
|
+
var textMaxWidth = void 0;
|
|
152
|
+
if (iconArea.iconsWidth > 0) {
|
|
153
|
+
// 有图标时,根据headerAlign调整文本位置和最大宽度
|
|
154
|
+
if (headerAlign === "right") {
|
|
155
|
+
ctx.textAlign = "right";
|
|
156
|
+
textX_1 = drawX + width - 5 - iconArea.iconsWidth;
|
|
157
|
+
textMaxWidth = width - 10 - iconArea.iconsWidth;
|
|
158
|
+
}
|
|
159
|
+
else if (headerAlign === "center") {
|
|
160
|
+
ctx.textAlign = "center";
|
|
161
|
+
textX_1 = drawX + (width - iconArea.iconsWidth) / 2;
|
|
162
|
+
textMaxWidth = width - 10 - iconArea.iconsWidth;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
ctx.textAlign = "left";
|
|
166
|
+
textX_1 = drawX + 5;
|
|
167
|
+
textMaxWidth = width - 5 - iconArea.iconsWidth;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// 没有图标时,根据headerAlign设置对齐方式
|
|
172
|
+
if (headerAlign === "left") {
|
|
173
|
+
ctx.textAlign = "left";
|
|
174
|
+
textX_1 = drawX + 5;
|
|
175
|
+
textMaxWidth = width - 10;
|
|
176
|
+
}
|
|
177
|
+
else if (headerAlign === "right") {
|
|
178
|
+
ctx.textAlign = "right";
|
|
179
|
+
textX_1 = drawX + width - 5;
|
|
180
|
+
textMaxWidth = width - 10;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
ctx.textAlign = "center";
|
|
184
|
+
textX_1 = drawX + width / 2;
|
|
185
|
+
textMaxWidth = width - 10;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
143
188
|
// 根据 ellipsis 和 wrap 属性处理文本
|
|
144
189
|
if (column.wrap) {
|
|
145
190
|
// 换行显示(支持\n和自动换行)
|
|
146
191
|
var lines = wrapText(ctx, titleText, textMaxWidth);
|
|
147
|
-
var lineHeight_1 =
|
|
192
|
+
var lineHeight_1 = FONT_SIZE + 5; // 字体大小 + 行间距5px
|
|
148
193
|
var startY_1 = textY - ((lines.length - 1) * lineHeight_1) / 2;
|
|
149
194
|
lines.forEach(function (line, index) {
|
|
150
195
|
var lineY = startY_1 + index * lineHeight_1;
|
|
151
|
-
ctx.fillText(line,
|
|
196
|
+
ctx.fillText(line, textX_1, lineY);
|
|
152
197
|
});
|
|
153
198
|
}
|
|
154
199
|
else {
|
|
155
200
|
// 默认截断显示
|
|
156
201
|
var truncatedText = truncateText(ctx, titleText, textMaxWidth);
|
|
157
|
-
ctx.fillText(truncatedText,
|
|
202
|
+
ctx.fillText(truncatedText, textX_1, textY);
|
|
158
203
|
}
|
|
159
204
|
}
|
|
160
205
|
// 绘制筛选图标(最右侧,距离右边5px)
|
|
@@ -171,6 +216,7 @@ var useTableRender = function (params) {
|
|
|
171
216
|
drawSortIcon(ctx, iconX, textY, column.key === state.sortField ? state.sortOrder : null);
|
|
172
217
|
}
|
|
173
218
|
}
|
|
219
|
+
ctx.textAlign = "left"; // 重置对齐方式
|
|
174
220
|
ctx.restore();
|
|
175
221
|
// 绘制表头列之间的边框
|
|
176
222
|
if (bordered) {
|
|
@@ -205,7 +251,8 @@ var useTableRender = function (params) {
|
|
|
205
251
|
}
|
|
206
252
|
});
|
|
207
253
|
// 绘制多级表头单元格
|
|
208
|
-
var drawMultiHeaderCell = useMemoizedFn(function (ctx, headerCell,
|
|
254
|
+
var drawMultiHeaderCell = useMemoizedFn(function (ctx, headerCell, layerHeight, y, displayWidth, currentDepth, totalCellHeight, // 单元格的总高度(考虑rowSpan)
|
|
255
|
+
onlyFixed // 是否只绘制fixed列
|
|
209
256
|
) {
|
|
210
257
|
if (onlyFixed === void 0) { onlyFixed = false; }
|
|
211
258
|
var column = headerCell.column, colSpan = headerCell.colSpan, rowSpan = headerCell.rowSpan, colIndex = headerCell.colIndex;
|
|
@@ -245,19 +292,19 @@ var useTableRender = function (params) {
|
|
|
245
292
|
}
|
|
246
293
|
// 绘制单元格背景
|
|
247
294
|
ctx.fillStyle = COLORS.headerBg;
|
|
248
|
-
ctx.fillRect(drawX, y, width,
|
|
295
|
+
ctx.fillRect(drawX, y, width, totalCellHeight);
|
|
249
296
|
// 绘制边框
|
|
250
297
|
if (bordered) {
|
|
251
298
|
ctx.strokeStyle = COLORS.border;
|
|
252
299
|
ctx.lineWidth = 1;
|
|
253
|
-
ctx.strokeRect(drawX, y, width,
|
|
300
|
+
ctx.strokeRect(drawX, y, width, totalCellHeight);
|
|
254
301
|
}
|
|
255
302
|
// 判断是否是最末级表头(当前行+rowSpan 是否等于最大深度)
|
|
256
303
|
var isLastLevel = currentDepth + rowSpan === maxDepth;
|
|
257
304
|
// 判断是否是特殊列(选择框列、序号列等不需要排序和筛选的列)
|
|
258
305
|
var isSpecialColumn = column.key === "__selection__" || column.key === "__index__";
|
|
259
306
|
// 绘制单元格内容
|
|
260
|
-
var textY = y +
|
|
307
|
+
var textY = y + totalCellHeight / 2;
|
|
261
308
|
// 绘制选择框(只在第一行显示全选框)
|
|
262
309
|
if (column.key === "__selection__" && currentDepth === 0) {
|
|
263
310
|
// 计算选中状态
|
|
@@ -269,7 +316,7 @@ var useTableRender = function (params) {
|
|
|
269
316
|
else {
|
|
270
317
|
// 绘制文本
|
|
271
318
|
ctx.fillStyle = COLORS.text;
|
|
272
|
-
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
319
|
+
ctx.font = "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
273
320
|
ctx.textBaseline = "middle";
|
|
274
321
|
if (!isValidElement(column.title)) {
|
|
275
322
|
var titleText = String(column.title || "");
|
|
@@ -278,44 +325,73 @@ var useTableRender = function (params) {
|
|
|
278
325
|
var iconArea = shouldShowIcons
|
|
279
326
|
? calculateIconArea(column, width)
|
|
280
327
|
: { hasOrder: false, hasFilter: false, iconsWidth: 0 };
|
|
281
|
-
//
|
|
328
|
+
// 确定表头对齐方式,优先使用列的 headerAlign,其次是 align,最后是默认值
|
|
329
|
+
var headerAlign = column.headerAlign || column.align || "center";
|
|
330
|
+
// 如果是最末级且有图标,需要调整对齐方式
|
|
282
331
|
if (isLastLevel && iconArea.iconsWidth > 0) {
|
|
283
|
-
|
|
284
|
-
var
|
|
285
|
-
var textMaxWidth =
|
|
332
|
+
// 有图标时,根据headerAlign调整文本位置和最大宽度
|
|
333
|
+
var textX_2;
|
|
334
|
+
var textMaxWidth = void 0;
|
|
335
|
+
if (headerAlign === "right") {
|
|
336
|
+
ctx.textAlign = "right";
|
|
337
|
+
textX_2 = drawX + width - 5 - iconArea.iconsWidth;
|
|
338
|
+
textMaxWidth = width - 10 - iconArea.iconsWidth;
|
|
339
|
+
}
|
|
340
|
+
else if (headerAlign === "center") {
|
|
341
|
+
ctx.textAlign = "center";
|
|
342
|
+
textX_2 = drawX + (width - iconArea.iconsWidth) / 2;
|
|
343
|
+
textMaxWidth = width - 10 - iconArea.iconsWidth;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
ctx.textAlign = "left";
|
|
347
|
+
textX_2 = drawX + 5;
|
|
348
|
+
textMaxWidth = width - 5 - iconArea.iconsWidth;
|
|
349
|
+
}
|
|
286
350
|
// 根据 wrap 属性处理文本
|
|
287
351
|
if (column.wrap) {
|
|
288
352
|
// 换行显示(支持\n和自动换行)
|
|
289
353
|
var lines = wrapText(ctx, titleText, textMaxWidth);
|
|
290
|
-
var lineHeight_2 =
|
|
354
|
+
var lineHeight_2 = FONT_SIZE + 5; // 字体大小 + 行间距5px
|
|
291
355
|
var startY_2 = textY - ((lines.length - 1) * lineHeight_2) / 2;
|
|
292
356
|
lines.forEach(function (line, index) {
|
|
293
357
|
var lineY = startY_2 + index * lineHeight_2;
|
|
294
|
-
ctx.fillText(line,
|
|
358
|
+
ctx.fillText(line, textX_2, lineY);
|
|
295
359
|
});
|
|
296
360
|
}
|
|
297
361
|
else {
|
|
298
362
|
var truncated = truncateText(ctx, titleText, textMaxWidth);
|
|
299
|
-
ctx.fillText(truncated,
|
|
363
|
+
ctx.fillText(truncated, textX_2, textY);
|
|
300
364
|
}
|
|
301
365
|
}
|
|
302
366
|
else {
|
|
303
|
-
|
|
304
|
-
var
|
|
367
|
+
// 没有图标时,根据headerAlign设置对齐方式
|
|
368
|
+
var textX_3;
|
|
369
|
+
if (headerAlign === "left") {
|
|
370
|
+
ctx.textAlign = "left";
|
|
371
|
+
textX_3 = drawX + 5;
|
|
372
|
+
}
|
|
373
|
+
else if (headerAlign === "right") {
|
|
374
|
+
ctx.textAlign = "right";
|
|
375
|
+
textX_3 = drawX + width - 5;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
ctx.textAlign = "center";
|
|
379
|
+
textX_3 = drawX + width / 2;
|
|
380
|
+
}
|
|
305
381
|
// 根据 wrap 属性处理文本
|
|
306
382
|
if (column.wrap) {
|
|
307
383
|
// 换行显示(支持\n和自动换行)
|
|
308
|
-
var lines = wrapText(ctx, titleText, width -
|
|
309
|
-
var lineHeight_3 =
|
|
384
|
+
var lines = wrapText(ctx, titleText, width - 10); // 左右各5px
|
|
385
|
+
var lineHeight_3 = FONT_SIZE + 5; // 字体大小 + 行间距5px
|
|
310
386
|
var startY_3 = textY - ((lines.length - 1) * lineHeight_3) / 2;
|
|
311
387
|
lines.forEach(function (line, index) {
|
|
312
388
|
var lineY = startY_3 + index * lineHeight_3;
|
|
313
|
-
ctx.fillText(line,
|
|
389
|
+
ctx.fillText(line, textX_3, lineY);
|
|
314
390
|
});
|
|
315
391
|
}
|
|
316
392
|
else {
|
|
317
|
-
var truncated = truncateText(ctx, titleText, width -
|
|
318
|
-
ctx.fillText(truncated,
|
|
393
|
+
var truncated = truncateText(ctx, titleText, width - 10); // 左右各5px
|
|
394
|
+
ctx.fillText(truncated, textX_3, textY);
|
|
319
395
|
}
|
|
320
396
|
}
|
|
321
397
|
// 绘制筛选图标(仅在最末级表头显示)
|
|
@@ -341,20 +417,31 @@ var useTableRender = function (params) {
|
|
|
341
417
|
ctx.fillStyle = COLORS.headerBg;
|
|
342
418
|
ctx.fillRect(0, 0, displayWidth, headerHeight);
|
|
343
419
|
if (isMultiHeader) {
|
|
344
|
-
//
|
|
345
|
-
var rowHeight_1 = headerHeight / maxDepth;
|
|
420
|
+
// 多级表头渲染,使用计算出的每一层高度
|
|
346
421
|
// 先绘制非fixed列
|
|
347
422
|
flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
|
|
348
|
-
var y = rowIndex
|
|
423
|
+
var y = layerStartY[rowIndex];
|
|
424
|
+
var currentLayerHeight = layerHeights[rowIndex];
|
|
349
425
|
headerRow.forEach(function (headerCell) {
|
|
350
|
-
|
|
426
|
+
// 计算单元格的总高度(如果跨越多层)
|
|
427
|
+
var totalCellHeight = 0;
|
|
428
|
+
for (var i = rowIndex; i < rowIndex + headerCell.rowSpan; i++) {
|
|
429
|
+
totalCellHeight += layerHeights[i];
|
|
430
|
+
}
|
|
431
|
+
drawMultiHeaderCell(ctx, headerCell, currentLayerHeight, y, displayWidth, rowIndex, totalCellHeight, false);
|
|
351
432
|
});
|
|
352
433
|
});
|
|
353
434
|
// 再绘制fixed列(覆盖在非fixed列之上)
|
|
354
435
|
flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
|
|
355
|
-
var y = rowIndex
|
|
436
|
+
var y = layerStartY[rowIndex];
|
|
437
|
+
var currentLayerHeight = layerHeights[rowIndex];
|
|
356
438
|
headerRow.forEach(function (headerCell) {
|
|
357
|
-
|
|
439
|
+
// 计算单元格的总高度(如果跨越多层)
|
|
440
|
+
var totalCellHeight = 0;
|
|
441
|
+
for (var i = rowIndex; i < rowIndex + headerCell.rowSpan; i++) {
|
|
442
|
+
totalCellHeight += layerHeights[i];
|
|
443
|
+
}
|
|
444
|
+
drawMultiHeaderCell(ctx, headerCell, currentLayerHeight, y, displayWidth, rowIndex, totalCellHeight, true);
|
|
358
445
|
});
|
|
359
446
|
});
|
|
360
447
|
}
|
|
@@ -514,10 +601,30 @@ var useTableRender = function (params) {
|
|
|
514
601
|
}
|
|
515
602
|
// 单元格内容
|
|
516
603
|
if (column.key === "__selection__") {
|
|
517
|
-
|
|
518
|
-
var
|
|
519
|
-
|
|
520
|
-
|
|
604
|
+
// 检查是否是合计行
|
|
605
|
+
var isSummaryRow = record[IS_SUMMARY];
|
|
606
|
+
if (!isSummaryRow) {
|
|
607
|
+
// 非合计行显示选择框
|
|
608
|
+
var checkboxProps = ((_a = rowSelection === null || rowSelection === void 0 ? void 0 : rowSelection.getCheckboxProps) === null || _a === void 0 ? void 0 : _a.call(rowSelection, record)) || {};
|
|
609
|
+
var disabled = checkboxProps.disabled || false;
|
|
610
|
+
// 对于合并单元格,使用合并后的单元格高度进行垂直居中
|
|
611
|
+
drawCheckbox(ctx, drawX + width / 2, y + cellHeight / 2, isSelected, disabled, false);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
// 合计行选择框列显示文本(比如"合计")
|
|
615
|
+
var dataIndex = column.dataIndex || column.key;
|
|
616
|
+
var cellValue = record[dataIndex];
|
|
617
|
+
var cellText = formatCellValue(cellValue, column);
|
|
618
|
+
if (cellText) {
|
|
619
|
+
ctx.fillStyle = COLORS.text;
|
|
620
|
+
ctx.font = "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
621
|
+
ctx.textBaseline = "middle";
|
|
622
|
+
ctx.textAlign = "center";
|
|
623
|
+
var textX = drawX + width / 2;
|
|
624
|
+
var textY = y + cellHeight / 2;
|
|
625
|
+
ctx.fillText(cellText, textX, textY);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
521
628
|
}
|
|
522
629
|
else {
|
|
523
630
|
var dataIndex = column.dataIndex || column.key;
|
|
@@ -562,29 +669,43 @@ var useTableRender = function (params) {
|
|
|
562
669
|
// 合计行字体加粗
|
|
563
670
|
ctx.font = record[IS_SUMMARY]
|
|
564
671
|
? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
|
|
565
|
-
: "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
672
|
+
: "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
566
673
|
ctx.textBaseline = "middle";
|
|
567
674
|
var align = column.align || "left";
|
|
568
|
-
var
|
|
675
|
+
var textX_4 = drawX + 5;
|
|
569
676
|
if (align === "center") {
|
|
570
677
|
ctx.textAlign = "center";
|
|
571
|
-
|
|
678
|
+
textX_4 = drawX + width / 2;
|
|
572
679
|
}
|
|
573
680
|
else if (align === "right") {
|
|
574
681
|
ctx.textAlign = "right";
|
|
575
|
-
|
|
682
|
+
textX_4 = drawX + width - 5;
|
|
576
683
|
}
|
|
577
684
|
else {
|
|
578
685
|
ctx.textAlign = "left";
|
|
579
686
|
}
|
|
580
687
|
// 处理文本显示(ellipsis 或 wrap)
|
|
581
|
-
var maxWidth = width -
|
|
688
|
+
var maxWidth = width - 10; // 左右各5px
|
|
582
689
|
var ellipsis = column.ellipsis !== false; // 默认为true
|
|
583
690
|
var wrap = column.wrap === true; // 默认为false
|
|
584
691
|
if (wrap) {
|
|
585
|
-
//
|
|
586
|
-
var
|
|
587
|
-
var
|
|
692
|
+
// 换行显示,最多显示2行
|
|
693
|
+
var allLines = wrapText(ctx, cellText, maxWidth);
|
|
694
|
+
var maxLines = 2; // 限制最多显示2行
|
|
695
|
+
var lines = allLines.slice(0, maxLines);
|
|
696
|
+
// 如果超过2行,在第2行末尾强制添加省略号
|
|
697
|
+
if (allLines.length > maxLines && lines.length === maxLines) {
|
|
698
|
+
// 确保第2行有足够空间显示省略号
|
|
699
|
+
var lastLine = lines[maxLines - 1];
|
|
700
|
+
var ellipsisText = "...";
|
|
701
|
+
// 如果最后一行加上省略号会超出宽度,需要截断
|
|
702
|
+
while (lastLine.length > 0 &&
|
|
703
|
+
ctx.measureText(lastLine + ellipsisText).width > maxWidth) {
|
|
704
|
+
lastLine = lastLine.slice(0, -1);
|
|
705
|
+
}
|
|
706
|
+
lines[maxLines - 1] = lastLine + ellipsisText;
|
|
707
|
+
}
|
|
708
|
+
var lineHeight_4 = FONT_SIZE + 5; // 字体大小 + 行间距5px
|
|
588
709
|
var totalHeight = lines.length * lineHeight_4;
|
|
589
710
|
// 使用合并后的单元格高度计算垂直居中位置
|
|
590
711
|
var startY_4 = y + (cellHeight - totalHeight) / 2 + lineHeight_4 / 2;
|
|
@@ -592,7 +713,7 @@ var useTableRender = function (params) {
|
|
|
592
713
|
var lineY = startY_4 + lineIndex * lineHeight_4;
|
|
593
714
|
// 只绘制在单元格可见区域内的文本
|
|
594
715
|
if (lineY >= y && lineY <= y + cellHeight) {
|
|
595
|
-
ctx.fillText(line,
|
|
716
|
+
ctx.fillText(line, textX_4, lineY);
|
|
596
717
|
}
|
|
597
718
|
});
|
|
598
719
|
}
|
|
@@ -600,12 +721,12 @@ var useTableRender = function (params) {
|
|
|
600
721
|
// 省略显示 - 使用合并后的单元格高度
|
|
601
722
|
var textY = y + cellHeight / 2;
|
|
602
723
|
var truncatedText = truncateText(ctx, cellText, maxWidth);
|
|
603
|
-
ctx.fillText(truncatedText,
|
|
724
|
+
ctx.fillText(truncatedText, textX_4, textY);
|
|
604
725
|
}
|
|
605
726
|
else {
|
|
606
727
|
// 不处理,直接显示 - 使用合并后的单元格高度
|
|
607
728
|
var textY = y + cellHeight / 2;
|
|
608
|
-
ctx.fillText(cellText,
|
|
729
|
+
ctx.fillText(cellText, textX_4, textY);
|
|
609
730
|
}
|
|
610
731
|
if (column.badge && !isSummaryRow) {
|
|
611
732
|
var badgeProps = typeof column.badge === "function"
|
|
@@ -814,28 +935,30 @@ var useTableRender = function (params) {
|
|
|
814
935
|
ctx.fillRect(drawX, fixedY, actualWidth, rowHeight);
|
|
815
936
|
// 处理序号列
|
|
816
937
|
if (column.key === "__index__") {
|
|
817
|
-
//
|
|
818
|
-
if (rowType
|
|
938
|
+
// 非合计行显示序号
|
|
939
|
+
if (rowType !== "summary") {
|
|
940
|
+
var indexText = String(rowIndex + 1);
|
|
941
|
+
ctx.fillStyle = COLORS.text;
|
|
942
|
+
ctx.font = "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
943
|
+
ctx.textBaseline = "middle";
|
|
944
|
+
setTextAlign(ctx, "center");
|
|
945
|
+
var textX = drawX + actualWidth / 2;
|
|
946
|
+
var textY = fixedY + rowHeight / 2;
|
|
947
|
+
ctx.fillText(indexText, textX, textY);
|
|
819
948
|
return;
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
823
|
-
ctx.textBaseline = "middle";
|
|
824
|
-
setTextAlign(ctx, "center");
|
|
825
|
-
var textX = drawX + actualWidth / 2;
|
|
826
|
-
var textY = fixedY + rowHeight / 2;
|
|
827
|
-
ctx.fillText(indexText, textX, textY);
|
|
828
|
-
return;
|
|
949
|
+
}
|
|
950
|
+
// 合计行序号列继续执行后面的逻辑,可能显示"合计"文字
|
|
829
951
|
}
|
|
830
952
|
// 处理选择框列
|
|
831
953
|
if (column.key === "__selection__") {
|
|
832
|
-
//
|
|
833
|
-
if (rowType
|
|
954
|
+
// 非合计行显示选择框
|
|
955
|
+
if (rowType !== "summary") {
|
|
956
|
+
var checkboxX = drawX + actualWidth / 2;
|
|
957
|
+
var textY = fixedY + rowHeight / 2;
|
|
958
|
+
drawCheckbox(ctx, checkboxX, textY, isSelected, false, false);
|
|
834
959
|
return;
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
drawCheckbox(ctx, checkboxX, textY, isSelected, false, false);
|
|
838
|
-
return;
|
|
960
|
+
}
|
|
961
|
+
// 合计行选择框列继续执行后面的逻辑,可能显示"合计"文字
|
|
839
962
|
}
|
|
840
963
|
// 获取单元格值
|
|
841
964
|
var dataIndex = column.dataIndex || column.key;
|
|
@@ -863,11 +986,11 @@ var useTableRender = function (params) {
|
|
|
863
986
|
ctx.font =
|
|
864
987
|
rowType === "summary"
|
|
865
988
|
? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
|
|
866
|
-
: "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
989
|
+
: "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
867
990
|
ctx.textBaseline = "middle";
|
|
868
991
|
var align = column.align || "left";
|
|
869
992
|
setTextAlign(ctx, align);
|
|
870
|
-
var padding =
|
|
993
|
+
var padding = 5;
|
|
871
994
|
var maxWidth = actualWidth - padding * 2;
|
|
872
995
|
var textY = fixedY + rowHeight / 2;
|
|
873
996
|
var textX = calculateTextX(drawX, actualWidth, align, padding);
|
|
@@ -1241,8 +1364,9 @@ var useTableRender = function (params) {
|
|
|
1241
1364
|
// 计算可视区域(从固定行之后开始)
|
|
1242
1365
|
var scrollStartRow = fixedTopRowsCount + Math.floor(scrollState.scrollTop / rowHeight);
|
|
1243
1366
|
var scrollEndRow = Math.min(fixedTopRowsCount +
|
|
1244
|
-
Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length -
|
|
1245
|
-
|
|
1367
|
+
Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length -
|
|
1368
|
+
fixedBottomRowsCount -
|
|
1369
|
+
(summaryFixed && hasSummaryRow ? 1 : 0) // 如果合计行固定,需要排除它
|
|
1246
1370
|
);
|
|
1247
1371
|
// 关键修复:向前扩展渲染范围,检查是否有合并单元格延伸到可视区域
|
|
1248
1372
|
// 策略:对每一列独立检查,找到该列最靠前的需要渲染的主合并单元格
|
|
@@ -36,6 +36,10 @@ interface ICanvasColumnType<RecordType = any> {
|
|
|
36
36
|
* 对齐方式
|
|
37
37
|
*/
|
|
38
38
|
align?: "left" | "center" | "right";
|
|
39
|
+
/**
|
|
40
|
+
* 表头对齐方式(如果不设置,使用全局 headerAlign 或 align)
|
|
41
|
+
*/
|
|
42
|
+
headerAlign?: "left" | "center" | "right";
|
|
39
43
|
/**
|
|
40
44
|
* 是否固定列
|
|
41
45
|
*/
|
|
@@ -182,13 +186,13 @@ interface ICanvasColumnType<RecordType = any> {
|
|
|
182
186
|
type ICanvasColumnsType<RecordType = any> = ICanvasColumnType<RecordType>[];
|
|
183
187
|
interface IBadgeProps {
|
|
184
188
|
/**
|
|
185
|
-
*
|
|
189
|
+
* 角标提示文本(鼠标悬停时显示)
|
|
186
190
|
*/
|
|
187
|
-
|
|
191
|
+
title?: string;
|
|
188
192
|
/**
|
|
189
|
-
*
|
|
193
|
+
* 角标颜色
|
|
190
194
|
*/
|
|
191
|
-
|
|
195
|
+
color?: string;
|
|
192
196
|
/**
|
|
193
197
|
* 角标位置
|
|
194
198
|
*/
|
|
@@ -249,6 +253,10 @@ interface ICanvasTableProps<RecordType = any> {
|
|
|
249
253
|
* 表头换行
|
|
250
254
|
*/
|
|
251
255
|
headerWrap?: boolean;
|
|
256
|
+
/**
|
|
257
|
+
* 表头对齐方式(默认居中)
|
|
258
|
+
*/
|
|
259
|
+
headerAlign?: "left" | "center" | "right";
|
|
252
260
|
/**
|
|
253
261
|
* 勾选配置
|
|
254
262
|
*/
|
|
@@ -394,6 +402,86 @@ interface ICanvasTableProps<RecordType = any> {
|
|
|
394
402
|
* 表格id(用于自动高度计算时定位元素)
|
|
395
403
|
*/
|
|
396
404
|
canvasTableId?: string;
|
|
405
|
+
/**
|
|
406
|
+
* 表格ref句柄,用于暴露表格的一些方法
|
|
407
|
+
*/
|
|
408
|
+
tableRefHandle?: React__default.Ref<ICanvasTableRefHandle>;
|
|
409
|
+
/**
|
|
410
|
+
* 导出excel的一些配置
|
|
411
|
+
*/
|
|
412
|
+
exportExcelConfig?: {
|
|
413
|
+
topDescription?: string;
|
|
414
|
+
time?: string;
|
|
415
|
+
topDescriptionRowHeight?: number;
|
|
416
|
+
fileName: string;
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* CanvasTable 暴露的方法
|
|
421
|
+
*/
|
|
422
|
+
interface ICanvasTableRefHandle {
|
|
423
|
+
/**
|
|
424
|
+
* 获取当前表格的配置信息(动态列配置)
|
|
425
|
+
*/
|
|
426
|
+
getDynamicList: () => any[];
|
|
427
|
+
/**
|
|
428
|
+
* 动态列配置重置
|
|
429
|
+
*/
|
|
430
|
+
onResetDynamicList: () => void;
|
|
431
|
+
/**
|
|
432
|
+
* 动态列配置设置
|
|
433
|
+
*/
|
|
434
|
+
onSetDynamicList: (list: any[]) => void;
|
|
435
|
+
/**
|
|
436
|
+
* 导出Excel
|
|
437
|
+
*/
|
|
438
|
+
exportExcel?: (fileName: string, config?: {
|
|
439
|
+
topDescription?: string;
|
|
440
|
+
time?: string;
|
|
441
|
+
topDescriptionRowHeight?: number;
|
|
442
|
+
}) => void;
|
|
443
|
+
/**
|
|
444
|
+
* 获取表格容器
|
|
445
|
+
*/
|
|
446
|
+
getContainer: () => HTMLDivElement | null;
|
|
447
|
+
/**
|
|
448
|
+
* 全屏展示表格
|
|
449
|
+
*/
|
|
450
|
+
toggleFullScreen: () => void;
|
|
451
|
+
/**
|
|
452
|
+
* 获取选中的行keys
|
|
453
|
+
*/
|
|
454
|
+
getSelectedRowKeys: () => React__default.Key[];
|
|
455
|
+
/**
|
|
456
|
+
* 设置选中的行keys
|
|
457
|
+
*/
|
|
458
|
+
setSelectedRowKeys: (keys: React__default.Key[]) => void;
|
|
459
|
+
/**
|
|
460
|
+
* 滚动到指定位置
|
|
461
|
+
*/
|
|
462
|
+
scrollTo: (params: {
|
|
463
|
+
x?: number;
|
|
464
|
+
y?: number;
|
|
465
|
+
}) => void;
|
|
466
|
+
/**
|
|
467
|
+
* 获取当前排序状态
|
|
468
|
+
*/
|
|
469
|
+
getSortState: () => {
|
|
470
|
+
field: string | null;
|
|
471
|
+
order: SortOrder;
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* 获取当前过滤状态
|
|
475
|
+
*/
|
|
476
|
+
getFilterState: () => Record<string, any[]>;
|
|
477
|
+
/**
|
|
478
|
+
* 清除排序
|
|
479
|
+
*/
|
|
480
|
+
clearSort: () => void;
|
|
481
|
+
/**
|
|
482
|
+
* 清除过滤
|
|
483
|
+
*/
|
|
484
|
+
clearFilter: () => void;
|
|
397
485
|
}
|
|
398
486
|
|
|
399
|
-
export { ColumnDataType, FilterConfig, IBadgeProps, ICanvasColumnType, ICanvasColumnsType, ICanvasRowSelection, ICanvasTableProps, SortOrder };
|
|
487
|
+
export { ColumnDataType, FilterConfig, IBadgeProps, ICanvasColumnType, ICanvasColumnsType, ICanvasRowSelection, ICanvasTableProps, ICanvasTableRefHandle, SortOrder };
|
|
@@ -24,13 +24,17 @@ var findColumnByKey = function (columns, key) {
|
|
|
24
24
|
/**
|
|
25
25
|
* 递归处理列的render函数(转换为统一的调用格式)
|
|
26
26
|
*/
|
|
27
|
-
var processColumnRender = function (column, renderMode, headerWrap) {
|
|
27
|
+
var processColumnRender = function (column, renderMode, headerWrap, headerAlign) {
|
|
28
28
|
if (renderMode === void 0) { renderMode = "object"; }
|
|
29
29
|
var processedColumn = __assign({}, column);
|
|
30
30
|
if (headerWrap) {
|
|
31
31
|
processedColumn.wrap =
|
|
32
32
|
processedColumn.wrap === undefined ? headerWrap : processedColumn.wrap;
|
|
33
33
|
}
|
|
34
|
+
// 应用表头对齐方式(仅当列没有设置 headerAlign 或 align 时)
|
|
35
|
+
if (headerAlign && !processedColumn.headerAlign && !processedColumn.align) {
|
|
36
|
+
processedColumn.headerAlign = headerAlign;
|
|
37
|
+
}
|
|
34
38
|
// 处理当前列的render函数
|
|
35
39
|
if (column.render) {
|
|
36
40
|
var render_1 = column.render;
|
|
@@ -44,7 +48,7 @@ var processColumnRender = function (column, renderMode, headerWrap) {
|
|
|
44
48
|
// 递归处理children
|
|
45
49
|
if (column.children && Array.isArray(column.children)) {
|
|
46
50
|
processedColumn.children = column.children.map(function (child) {
|
|
47
|
-
return processColumnRender(child, renderMode, headerWrap);
|
|
51
|
+
return processColumnRender(child, renderMode, headerWrap, headerAlign);
|
|
48
52
|
});
|
|
49
53
|
}
|
|
50
54
|
return processedColumn;
|