zmdms-webui 2.3.4 → 2.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/canvastable/canvasTable.js +2 -1
- package/dist/es/canvastable/hooks/useHeaderHeight.js +1 -1
- package/dist/es/canvastable/hooks/usePopovers.js +9 -2
- package/dist/es/canvastable/hooks/useProcessedColumns.js +2 -2
- package/dist/es/canvastable/hooks/useSummaryRow.js +26 -26
- package/dist/es/canvastable/hooks/useTableRender.js +119 -31
- package/dist/es/canvastable/interface.d.ts +1 -1
- package/dist/es/canvastable/utils/columnHelpers.js +6 -2
- package/dist/es/canvastable/utils/constants.js +2 -2
- package/package.json +1 -1
|
@@ -39,7 +39,7 @@ import { getTableColumns } from '../table/utils.js';
|
|
|
39
39
|
import DynamicSetting from '../dynamicsetting/dynamicSetting.js';
|
|
40
40
|
|
|
41
41
|
function CanvasTable(props) {
|
|
42
|
-
var _a = props.dataSource, dataSource = _a === void 0 ? [] : _a, _b = props.columns, columns = _b === void 0 ? [] : _b, _c = props.rowKey, rowKey = _c === void 0 ? "id" : _c, _d = props.height, height = _d === void 0 ? 600 : _d, width = props.width, _e = props.rowHeight, rowHeight = _e === void 0 ? 36 : _e, _f = props.headerHeight, headerHeight = _f === void 0 ? 36 : _f, rowSelection = props.rowSelection, onSortChange = props.onSortChange, onFilterChange = props.onFilterChange, onScroll = props.onScroll, onRowClick = props.onRowClick, onColumnResize = props.onColumnResize, _g = props.bordered, bordered = _g === void 0 ? true : _g, _h = props.striped, striped = _h === void 0 ? false : _h, _j = props.emptyText, emptyText = _j === void 0 ? "暂无数据" : _j, style = props.style, className = props.className, _k = props.loading, loading = _k === void 0 ? false : _k, _l = props.isContextMenu, isContextMenu = _l === void 0 ? true : _l, isFullscreenHandle = props.isFullscreenHandle, _m = props.mode, mode = _m === void 0 ? "index" : _m, dynamicKey = props.dynamicKey, dynamicVersion = props.dynamicVersion, customDynamicListHandle = props.customDynamicListHandle, isDimensionDynamic = props.isDimensionDynamic, dimensionCustomSumKeys = props.dimensionCustomSumKeys, isAutoMerge = props.isAutoMerge, _o = props.isIndexMerge, isIndexMerge = _o === void 0 ? true : _o, _p = props.isSelectionMerge, isSelectionMerge = _p === void 0 ? false : _p, _q = props.renderMode, renderMode = _q === void 0 ? "object" : _q, fixedRowsCount = props.fixedRowsCount, fixedRowsConfig = props.fixedRowsConfig, _r = props.summaryFixed, summaryFixed = _r === void 0 ? false : _r, _s = props.isAutoScrollY, isAutoScrollY = _s === void 0 ? false : _s, _t = props.autoScrollYMarginBottom, autoScrollYMarginBottom = _t === void 0 ? 65 : _t, canvasTableId = props.canvasTableId;
|
|
42
|
+
var _a = props.dataSource, dataSource = _a === void 0 ? [] : _a, _b = props.columns, columns = _b === void 0 ? [] : _b, _c = props.rowKey, rowKey = _c === void 0 ? "id" : _c, _d = props.height, height = _d === void 0 ? 600 : _d, width = props.width, _e = props.rowHeight, rowHeight = _e === void 0 ? 36 : _e, _f = props.headerHeight, headerHeight = _f === void 0 ? 36 : _f, rowSelection = props.rowSelection, onSortChange = props.onSortChange, onFilterChange = props.onFilterChange, onScroll = props.onScroll, onRowClick = props.onRowClick, onColumnResize = props.onColumnResize, _g = props.bordered, bordered = _g === void 0 ? true : _g, _h = props.striped, striped = _h === void 0 ? false : _h, _j = props.emptyText, emptyText = _j === void 0 ? "暂无数据" : _j, style = props.style, className = props.className, _k = props.loading, loading = _k === void 0 ? false : _k, _l = props.isContextMenu, isContextMenu = _l === void 0 ? true : _l, isFullscreenHandle = props.isFullscreenHandle, _m = props.mode, mode = _m === void 0 ? "index" : _m, dynamicKey = props.dynamicKey, dynamicVersion = props.dynamicVersion, customDynamicListHandle = props.customDynamicListHandle, isDimensionDynamic = props.isDimensionDynamic, dimensionCustomSumKeys = props.dimensionCustomSumKeys, isAutoMerge = props.isAutoMerge, _o = props.isIndexMerge, isIndexMerge = _o === void 0 ? true : _o, _p = props.isSelectionMerge, isSelectionMerge = _p === void 0 ? false : _p, _q = props.renderMode, renderMode = _q === void 0 ? "object" : _q, fixedRowsCount = props.fixedRowsCount, fixedRowsConfig = props.fixedRowsConfig, _r = props.summaryFixed, summaryFixed = _r === void 0 ? false : _r, _s = props.isAutoScrollY, isAutoScrollY = _s === void 0 ? false : _s, _t = props.autoScrollYMarginBottom, autoScrollYMarginBottom = _t === void 0 ? 65 : _t, canvasTableId = props.canvasTableId, headerWrap = props.headerWrap;
|
|
43
43
|
var canvasRef = useRef(null);
|
|
44
44
|
var containerRef = useRef(null);
|
|
45
45
|
var filterPopoverRef = useRef(null);
|
|
@@ -85,6 +85,7 @@ function CanvasTable(props) {
|
|
|
85
85
|
dynamicKey: dynamicKey,
|
|
86
86
|
renderMode: renderMode,
|
|
87
87
|
dynamicSettingRef: dynamicSettingRef,
|
|
88
|
+
headerWrap: headerWrap,
|
|
88
89
|
});
|
|
89
90
|
// 表格状态管理
|
|
90
91
|
var _w = useTableState({
|
|
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react';
|
|
|
3
3
|
import { getMaxDepth } from '../utils/multiHeaderHelpers.js';
|
|
4
4
|
|
|
5
5
|
var FONT_SIZE = 13;
|
|
6
|
-
var FONT_FAMILY = "
|
|
6
|
+
var FONT_FAMILY = '"Microsoft YaHei", 微软雅黑, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
7
7
|
var LINE_HEIGHT = 20;
|
|
8
8
|
var FILTER_ICON_WIDTH = 15;
|
|
9
9
|
var SORT_ICON_WIDTH = 15;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __assign } from '../../_virtual/_tslib.js';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { useCallback, useEffect, useMemo } from 'react';
|
|
3
|
+
import { useRef, useCallback, useEffect, useMemo } from 'react';
|
|
4
4
|
import 'zmdms-utils';
|
|
5
5
|
import 'dayjs';
|
|
6
6
|
import { findColumnByKey } from '../utils/columnHelpers.js';
|
|
@@ -20,6 +20,8 @@ import '../components/ColumnDynamic.js';
|
|
|
20
20
|
*/
|
|
21
21
|
var useBadgePopover = function (params) {
|
|
22
22
|
var badgePopover = params.badgePopover, tooltip = params.tooltip, scrollTop = params.scrollTop, scrollLeft = params.scrollLeft, setState = params.setState;
|
|
23
|
+
// 使用 ref 记录上一次的滚动位置
|
|
24
|
+
var prevScrollRef = useRef({ scrollTop: scrollTop, scrollLeft: scrollLeft });
|
|
23
25
|
// 关闭badge弹窗
|
|
24
26
|
var handleCloseBadgePopover = useCallback(function () {
|
|
25
27
|
setState(function (prev) { return (__assign(__assign({}, prev), { badgePopover: {
|
|
@@ -30,7 +32,10 @@ var useBadgePopover = function (params) {
|
|
|
30
32
|
}, [setState]);
|
|
31
33
|
// 滚动时关闭badge弹窗和tooltip
|
|
32
34
|
useEffect(function () {
|
|
33
|
-
|
|
35
|
+
// 只有在滚动位置真正变化时才关闭弹窗
|
|
36
|
+
var scrollChanged = prevScrollRef.current.scrollTop !== scrollTop ||
|
|
37
|
+
prevScrollRef.current.scrollLeft !== scrollLeft;
|
|
38
|
+
if (scrollChanged && (badgePopover.visible || tooltip.visible)) {
|
|
34
39
|
setState(function (prev) { return (__assign(__assign({}, prev), { badgePopover: {
|
|
35
40
|
visible: false,
|
|
36
41
|
content: "",
|
|
@@ -42,6 +47,8 @@ var useBadgePopover = function (params) {
|
|
|
42
47
|
cellInfo: null,
|
|
43
48
|
} })); });
|
|
44
49
|
}
|
|
50
|
+
// 更新上一次的滚动位置
|
|
51
|
+
prevScrollRef.current = { scrollTop: scrollTop, scrollLeft: scrollLeft };
|
|
45
52
|
}, [scrollTop, scrollLeft, setState, badgePopover.visible, tooltip.visible]);
|
|
46
53
|
return {
|
|
47
54
|
handleCloseBadgePopover: handleCloseBadgePopover,
|
|
@@ -16,7 +16,7 @@ import '../../node_modules/screenfull/index.js';
|
|
|
16
16
|
import ColumnDynamic from '../components/ColumnDynamic.js';
|
|
17
17
|
|
|
18
18
|
var useProcessedColumns = function (params) {
|
|
19
|
-
var columns = params.columns, dynamicColumns = params.dynamicColumns, mode = params.mode, rowSelection = params.rowSelection, internalColumnWidths = params.internalColumnWidths, dynamicKey = params.dynamicKey, _a = params.renderMode, renderMode = _a === void 0 ? "object" : _a, dynamicSettingRef = params.dynamicSettingRef;
|
|
19
|
+
var columns = params.columns, dynamicColumns = params.dynamicColumns, mode = params.mode, rowSelection = params.rowSelection, internalColumnWidths = params.internalColumnWidths, dynamicKey = params.dynamicKey, _a = params.renderMode, renderMode = _a === void 0 ? "object" : _a, dynamicSettingRef = params.dynamicSettingRef, headerWrap = params.headerWrap;
|
|
20
20
|
return useMemo(function () {
|
|
21
21
|
var cols = __spreadArray([], (dynamicColumns || columns), true);
|
|
22
22
|
// 添加序号列
|
|
@@ -79,7 +79,7 @@ var useProcessedColumns = function (params) {
|
|
|
79
79
|
}
|
|
80
80
|
// 处理render函数
|
|
81
81
|
return cols.map(function (col) {
|
|
82
|
-
return processColumnRender(col, renderMode);
|
|
82
|
+
return processColumnRender(col, renderMode, headerWrap);
|
|
83
83
|
});
|
|
84
84
|
}, [
|
|
85
85
|
dynamicColumns,
|
|
@@ -8,6 +8,7 @@ import 'react/jsx-runtime';
|
|
|
8
8
|
import { plus, exactRound, addThousedSeparator } from 'zmdms-utils';
|
|
9
9
|
import '../../node_modules/exceljs/dist/exceljs.min.js';
|
|
10
10
|
import 'dayjs';
|
|
11
|
+
import { getAllLeafColumns } from '../utils/columnHelpers.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* 合计行计算 Hook
|
|
@@ -15,45 +16,38 @@ import 'dayjs';
|
|
|
15
16
|
var useSummaryRow = function (params) {
|
|
16
17
|
var columns = params.columns, dataSource = params.dataSource;
|
|
17
18
|
var summaryRow = useMemo(function () {
|
|
18
|
-
|
|
19
|
+
// 先扁平化多级表头,获取所有叶子列
|
|
20
|
+
var leafColumns = getAllLeafColumns(columns);
|
|
21
|
+
var hasSummary = leafColumns.some(function (col) { return col.isSummary; });
|
|
19
22
|
if (!hasSummary)
|
|
20
23
|
return null;
|
|
21
24
|
var summaryRecord = {};
|
|
22
25
|
// 找到第一个数据列(非序号列、非选择框列)
|
|
23
26
|
var firstDataColIndex = -1;
|
|
24
|
-
for (var i = 0; i <
|
|
25
|
-
var col =
|
|
27
|
+
for (var i = 0; i < leafColumns.length; i++) {
|
|
28
|
+
var col = leafColumns[i];
|
|
26
29
|
if (col.key !== "__index__" && col.key !== "__selection__") {
|
|
27
30
|
firstDataColIndex = i;
|
|
28
31
|
break;
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
|
|
34
|
+
leafColumns.forEach(function (column, colIndex) {
|
|
32
35
|
var dataIndex = column.dataIndex || column.key;
|
|
33
36
|
if (column.isSummary) {
|
|
34
37
|
var calculatedSum = void 0;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!isNaN(num) &&
|
|
49
|
-
num !== null &&
|
|
50
|
-
num !== undefined &&
|
|
51
|
-
!record[IS_SUMMARY]) {
|
|
52
|
-
sum_1 = plus(sum_1, num);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
calculatedSum = sum_1;
|
|
56
|
-
}
|
|
38
|
+
// 默认计算逻辑:数值求和
|
|
39
|
+
var sum_1 = 0;
|
|
40
|
+
dataSource.forEach(function (record) {
|
|
41
|
+
var value = record[dataIndex];
|
|
42
|
+
var num = typeof value === "string" ? parseFloat(value) : value;
|
|
43
|
+
if (!isNaN(num) &&
|
|
44
|
+
num !== null &&
|
|
45
|
+
num !== undefined &&
|
|
46
|
+
!record[IS_SUMMARY]) {
|
|
47
|
+
sum_1 = plus(sum_1, num);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
calculatedSum = sum_1;
|
|
57
51
|
// 应用格式化(仅在没有自定义回调或回调返回数值时)
|
|
58
52
|
var formattedSum = calculatedSum;
|
|
59
53
|
if (typeof calculatedSum === "number") {
|
|
@@ -74,6 +68,12 @@ var useSummaryRow = function (params) {
|
|
|
74
68
|
summaryRecord[dataIndex] = "";
|
|
75
69
|
}
|
|
76
70
|
});
|
|
71
|
+
leafColumns.forEach(function (column) {
|
|
72
|
+
if (column.totalCalcCallback) {
|
|
73
|
+
var dataIndex = column.dataIndex || column.key;
|
|
74
|
+
summaryRecord[dataIndex] = column.totalCalcCallback(summaryRecord[dataIndex], summaryRecord);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
77
|
summaryRecord[IS_SUMMARY] = true;
|
|
78
78
|
return summaryRecord;
|
|
79
79
|
}, [columns, dataSource]);
|
|
@@ -281,16 +281,42 @@ var useTableRender = function (params) {
|
|
|
281
281
|
// 如果是最末级且有图标,文本左对齐;否则居中
|
|
282
282
|
if (isLastLevel && iconArea.iconsWidth > 0) {
|
|
283
283
|
ctx.textAlign = "left";
|
|
284
|
-
var
|
|
284
|
+
var textX_1 = drawX + 16;
|
|
285
285
|
var textMaxWidth = width - 16 - iconArea.iconsWidth;
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
// 根据 wrap 属性处理文本
|
|
287
|
+
if (column.wrap) {
|
|
288
|
+
// 换行显示(支持\n和自动换行)
|
|
289
|
+
var lines = wrapText(ctx, titleText, textMaxWidth);
|
|
290
|
+
var lineHeight_2 = 20;
|
|
291
|
+
var startY_2 = textY - ((lines.length - 1) * lineHeight_2) / 2;
|
|
292
|
+
lines.forEach(function (line, index) {
|
|
293
|
+
var lineY = startY_2 + index * lineHeight_2;
|
|
294
|
+
ctx.fillText(line, textX_1, lineY);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
var truncated = truncateText(ctx, titleText, textMaxWidth);
|
|
299
|
+
ctx.fillText(truncated, textX_1, textY);
|
|
300
|
+
}
|
|
288
301
|
}
|
|
289
302
|
else {
|
|
290
303
|
ctx.textAlign = "center";
|
|
291
|
-
var
|
|
292
|
-
|
|
293
|
-
|
|
304
|
+
var textX_2 = drawX + width / 2;
|
|
305
|
+
// 根据 wrap 属性处理文本
|
|
306
|
+
if (column.wrap) {
|
|
307
|
+
// 换行显示(支持\n和自动换行)
|
|
308
|
+
var lines = wrapText(ctx, titleText, width - 16);
|
|
309
|
+
var lineHeight_3 = 20;
|
|
310
|
+
var startY_3 = textY - ((lines.length - 1) * lineHeight_3) / 2;
|
|
311
|
+
lines.forEach(function (line, index) {
|
|
312
|
+
var lineY = startY_3 + index * lineHeight_3;
|
|
313
|
+
ctx.fillText(line, textX_2, lineY);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
var truncated = truncateText(ctx, titleText, width - 16);
|
|
318
|
+
ctx.fillText(truncated, textX_2, textY);
|
|
319
|
+
}
|
|
294
320
|
}
|
|
295
321
|
// 绘制筛选图标(仅在最末级表头显示)
|
|
296
322
|
if (iconArea.hasFilter) {
|
|
@@ -370,8 +396,25 @@ var useTableRender = function (params) {
|
|
|
370
396
|
(needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
|
|
371
397
|
fixedBottomHeight -
|
|
372
398
|
fixedSummaryHeight;
|
|
373
|
-
//
|
|
374
|
-
|
|
399
|
+
// 提前检查这一行是否有合并单元格,找出最大的rowSpan
|
|
400
|
+
var maxRowSpanInRow = 1;
|
|
401
|
+
if (mergeCellMap) {
|
|
402
|
+
columnRenderInfos.forEach(function (_, colIndex) {
|
|
403
|
+
var mergeCellKey = "".concat(rowIndex, "-").concat(colIndex);
|
|
404
|
+
var mergeInfo = mergeCellMap.get(mergeCellKey);
|
|
405
|
+
// 只考虑主单元格(不是被合并的单元格)
|
|
406
|
+
if (mergeInfo &&
|
|
407
|
+
!mergeInfo.skip &&
|
|
408
|
+
mergeInfo.rowSpan > maxRowSpanInRow) {
|
|
409
|
+
maxRowSpanInRow = mergeInfo.rowSpan;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
// 计算这一行的实际高度(考虑合并单元格)
|
|
414
|
+
var actualRowHeight = rowHeight * maxRowSpanInRow;
|
|
415
|
+
// 跳过不在可视区域的行
|
|
416
|
+
// 关键:使用合并后的实际高度判断,确保合并单元格的最后一行还在可视区域内时继续渲染
|
|
417
|
+
if (y + actualRowHeight < headerHeight || y >= effectiveHeight) {
|
|
375
418
|
return;
|
|
376
419
|
}
|
|
377
420
|
var recordKey = getRowKey(record, rowIndex);
|
|
@@ -430,7 +473,10 @@ var useTableRender = function (params) {
|
|
|
430
473
|
ctx.beginPath();
|
|
431
474
|
// 裁剪区域:确保不覆盖表头,从 headerHeight 开始
|
|
432
475
|
var clipY = Math.max(y, headerHeight);
|
|
433
|
-
|
|
476
|
+
// 计算单元格实际底部位置,并限制在可视区域内
|
|
477
|
+
var cellBottom = Math.min(y + cellHeight, effectiveHeight);
|
|
478
|
+
// 裁剪高度 = 单元格可见底部 - 裁剪起始位置
|
|
479
|
+
var clipHeight = Math.max(0, cellBottom - clipY);
|
|
434
480
|
// 对于非fixed列,还需要裁剪掉被fixed列遮挡的部分
|
|
435
481
|
var clipX = drawX;
|
|
436
482
|
var clipWidth = width;
|
|
@@ -476,9 +522,12 @@ var useTableRender = function (params) {
|
|
|
476
522
|
else {
|
|
477
523
|
var dataIndex = column.dataIndex || column.key;
|
|
478
524
|
var cellValue = record[dataIndex];
|
|
525
|
+
// 检查是否是合计行
|
|
526
|
+
var isSummaryRow = record[IS_SUMMARY];
|
|
479
527
|
// 检查是否有 render 函数且返回自定义 ReactNode
|
|
480
528
|
var shouldRenderInCanvas = true;
|
|
481
|
-
|
|
529
|
+
// 对于合计行,不应用render函数,直接显示原始数据
|
|
530
|
+
if (column.render && !isSummaryRow) {
|
|
482
531
|
var rendered = column.render(cellValue, record, rowIndex);
|
|
483
532
|
// 如果返回的不是字符串或数字,说明是自定义组件,应该在覆盖层渲染,不在 Canvas 渲染
|
|
484
533
|
if (typeof rendered !== "string" &&
|
|
@@ -491,16 +540,23 @@ var useTableRender = function (params) {
|
|
|
491
540
|
// 只有在需要在 Canvas 中渲染时才绘制文本
|
|
492
541
|
if (shouldRenderInCanvas) {
|
|
493
542
|
// 应用格式化(日期、精度、千分符)
|
|
494
|
-
var cellText =
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
rowIndex: rowIndex,
|
|
498
|
-
renderFn: column.render,
|
|
499
|
-
});
|
|
500
|
-
// 如果没有自定义render,应用格式化
|
|
501
|
-
if (!column.render) {
|
|
543
|
+
var cellText = void 0;
|
|
544
|
+
// 对于合计行,直接使用格式化函数,不应用render
|
|
545
|
+
if (isSummaryRow) {
|
|
502
546
|
cellText = formatCellValue(cellValue, column);
|
|
503
547
|
}
|
|
548
|
+
else {
|
|
549
|
+
cellText = extractCellText({
|
|
550
|
+
cellValue: cellValue,
|
|
551
|
+
record: record,
|
|
552
|
+
rowIndex: rowIndex,
|
|
553
|
+
renderFn: column.render,
|
|
554
|
+
});
|
|
555
|
+
// 如果没有自定义render,应用格式化
|
|
556
|
+
if (!column.render) {
|
|
557
|
+
cellText = formatCellValue(cellValue, column);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
504
560
|
// 绘制文本
|
|
505
561
|
ctx.fillStyle = COLORS.text;
|
|
506
562
|
// 合计行字体加粗
|
|
@@ -509,14 +565,14 @@ var useTableRender = function (params) {
|
|
|
509
565
|
: "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
510
566
|
ctx.textBaseline = "middle";
|
|
511
567
|
var align = column.align || "left";
|
|
512
|
-
var
|
|
568
|
+
var textX_3 = drawX + 16;
|
|
513
569
|
if (align === "center") {
|
|
514
570
|
ctx.textAlign = "center";
|
|
515
|
-
|
|
571
|
+
textX_3 = drawX + width / 2;
|
|
516
572
|
}
|
|
517
573
|
else if (align === "right") {
|
|
518
574
|
ctx.textAlign = "right";
|
|
519
|
-
|
|
575
|
+
textX_3 = drawX + width - 16;
|
|
520
576
|
}
|
|
521
577
|
else {
|
|
522
578
|
ctx.textAlign = "left";
|
|
@@ -528,15 +584,15 @@ var useTableRender = function (params) {
|
|
|
528
584
|
if (wrap) {
|
|
529
585
|
// 换行显示
|
|
530
586
|
var lines = wrapText(ctx, cellText, maxWidth);
|
|
531
|
-
var
|
|
532
|
-
var totalHeight = lines.length *
|
|
587
|
+
var lineHeight_4 = FONT_SIZE + 4; // 行高
|
|
588
|
+
var totalHeight = lines.length * lineHeight_4;
|
|
533
589
|
// 使用合并后的单元格高度计算垂直居中位置
|
|
534
|
-
var
|
|
590
|
+
var startY_4 = y + (cellHeight - totalHeight) / 2 + lineHeight_4 / 2;
|
|
535
591
|
lines.forEach(function (line, lineIndex) {
|
|
536
|
-
var lineY =
|
|
592
|
+
var lineY = startY_4 + lineIndex * lineHeight_4;
|
|
537
593
|
// 只绘制在单元格可见区域内的文本
|
|
538
594
|
if (lineY >= y && lineY <= y + cellHeight) {
|
|
539
|
-
ctx.fillText(line,
|
|
595
|
+
ctx.fillText(line, textX_3, lineY);
|
|
540
596
|
}
|
|
541
597
|
});
|
|
542
598
|
}
|
|
@@ -544,15 +600,13 @@ var useTableRender = function (params) {
|
|
|
544
600
|
// 省略显示 - 使用合并后的单元格高度
|
|
545
601
|
var textY = y + cellHeight / 2;
|
|
546
602
|
var truncatedText = truncateText(ctx, cellText, maxWidth);
|
|
547
|
-
ctx.fillText(truncatedText,
|
|
603
|
+
ctx.fillText(truncatedText, textX_3, textY);
|
|
548
604
|
}
|
|
549
605
|
else {
|
|
550
606
|
// 不处理,直接显示 - 使用合并后的单元格高度
|
|
551
607
|
var textY = y + cellHeight / 2;
|
|
552
|
-
ctx.fillText(cellText,
|
|
608
|
+
ctx.fillText(cellText, textX_3, textY);
|
|
553
609
|
}
|
|
554
|
-
// 绘制角标(跳过合计行)
|
|
555
|
-
var isSummaryRow = hasSummaryRow && rowIndex === processedDataSource.length - 1;
|
|
556
610
|
if (column.badge && !isSummaryRow) {
|
|
557
611
|
var badgeProps = typeof column.badge === "function"
|
|
558
612
|
? column.badge(record)
|
|
@@ -790,7 +844,9 @@ var useTableRender = function (params) {
|
|
|
790
844
|
var cellText = formatCellValue(cellValue, column);
|
|
791
845
|
// 检查是否有render函数且返回自定义ReactNode
|
|
792
846
|
var shouldRenderInCanvas = true;
|
|
793
|
-
|
|
847
|
+
// 对于合计行,不应用render函数,直接显示原始数据
|
|
848
|
+
var isSummaryRowFixed = rowType === "summary";
|
|
849
|
+
if (column.render && !isSummaryRowFixed) {
|
|
794
850
|
var rendered = column.render(cellValue, record, rowIndex);
|
|
795
851
|
// 如果返回的不是字符串或数字,说明是自定义组件,应该在覆盖层渲染,不在 Canvas 渲染
|
|
796
852
|
if (typeof rendered !== "string" &&
|
|
@@ -1160,6 +1216,9 @@ var useTableRender = function (params) {
|
|
|
1160
1216
|
canvas.style.width = "".concat(displayWidth, "px");
|
|
1161
1217
|
canvas.style.height = "".concat(displayHeight, "px");
|
|
1162
1218
|
ctx.scale(dpr, dpr);
|
|
1219
|
+
// 优化文字渲染质量,防止模糊
|
|
1220
|
+
ctx.imageSmoothingEnabled = true;
|
|
1221
|
+
ctx.imageSmoothingQuality = "high";
|
|
1163
1222
|
// 清空画布
|
|
1164
1223
|
ctx.clearRect(0, 0, displayWidth, displayHeight);
|
|
1165
1224
|
// 绘制背景
|
|
@@ -1185,6 +1244,35 @@ var useTableRender = function (params) {
|
|
|
1185
1244
|
Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length - fixedBottomRowsCount
|
|
1186
1245
|
// 注意:不再减去合计行数量,因为合计行的空间已在 dataAreaHeight 中考虑
|
|
1187
1246
|
);
|
|
1247
|
+
// 关键修复:向前扩展渲染范围,检查是否有合并单元格延伸到可视区域
|
|
1248
|
+
// 策略:对每一列独立检查,找到该列最靠前的需要渲染的主合并单元格
|
|
1249
|
+
if (mergeCellMap && scrollStartRow > fixedTopRowsCount) {
|
|
1250
|
+
var originalStartRow = scrollStartRow;
|
|
1251
|
+
var minStartRow = scrollStartRow;
|
|
1252
|
+
// 对每一列独立检查
|
|
1253
|
+
for (var colIndex = 0; colIndex < columnRenderInfos.length; colIndex++) {
|
|
1254
|
+
// 向前查找这一列的主合并单元格,最多向前查找20行
|
|
1255
|
+
for (var checkRow = originalStartRow - 1; checkRow >= fixedTopRowsCount && checkRow >= originalStartRow - 20; checkRow--) {
|
|
1256
|
+
var mergeCellKey = "".concat(checkRow, "-").concat(colIndex);
|
|
1257
|
+
var mergeInfo = mergeCellMap.get(mergeCellKey);
|
|
1258
|
+
// 如果找到主单元格(不是被合并的单元格)
|
|
1259
|
+
if (mergeInfo && !mergeInfo.skip && mergeInfo.rowSpan > 1) {
|
|
1260
|
+
// 计算合并单元格的底部行索引
|
|
1261
|
+
var mergeEndRow = checkRow + mergeInfo.rowSpan - 1;
|
|
1262
|
+
// 如果这个合并单元格延伸到可视区域
|
|
1263
|
+
if (mergeEndRow >= originalStartRow) {
|
|
1264
|
+
// 更新最小起始行
|
|
1265
|
+
minStartRow = Math.min(minStartRow, checkRow);
|
|
1266
|
+
}
|
|
1267
|
+
// 找到这一列的主单元格后,停止向前查找该列
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
1270
|
+
// 如果是skip的单元格或没有合并信息,继续向前查找
|
|
1271
|
+
// 因为主单元格可能在更前面
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
scrollStartRow = minStartRow;
|
|
1275
|
+
}
|
|
1188
1276
|
// 1. 绘制可滚动区域的数据行(包括边框)
|
|
1189
1277
|
renderWithFixedLayer(ctx,
|
|
1190
1278
|
// 渲染行内容
|
|
@@ -135,7 +135,7 @@ interface ICanvasColumnType<RecordType = any> {
|
|
|
135
135
|
* @param values 所有值的数组
|
|
136
136
|
* @returns 计算后的合计值
|
|
137
137
|
*/
|
|
138
|
-
totalCalcCallback?: (value: any, values: any
|
|
138
|
+
totalCalcCallback?: (value: any, values: any) => any;
|
|
139
139
|
/**
|
|
140
140
|
* 开启合并的字段,按其他字段维度合并
|
|
141
141
|
*/
|
|
@@ -24,9 +24,13 @@ var findColumnByKey = function (columns, key) {
|
|
|
24
24
|
/**
|
|
25
25
|
* 递归处理列的render函数(转换为统一的调用格式)
|
|
26
26
|
*/
|
|
27
|
-
var processColumnRender = function (column, renderMode) {
|
|
27
|
+
var processColumnRender = function (column, renderMode, headerWrap) {
|
|
28
28
|
if (renderMode === void 0) { renderMode = "object"; }
|
|
29
29
|
var processedColumn = __assign({}, column);
|
|
30
|
+
if (headerWrap) {
|
|
31
|
+
processedColumn.wrap =
|
|
32
|
+
processedColumn.wrap === undefined ? headerWrap : processedColumn.wrap;
|
|
33
|
+
}
|
|
30
34
|
// 处理当前列的render函数
|
|
31
35
|
if (column.render) {
|
|
32
36
|
var render_1 = column.render;
|
|
@@ -40,7 +44,7 @@ var processColumnRender = function (column, renderMode) {
|
|
|
40
44
|
// 递归处理children
|
|
41
45
|
if (column.children && Array.isArray(column.children)) {
|
|
42
46
|
processedColumn.children = column.children.map(function (child) {
|
|
43
|
-
return processColumnRender(child, renderMode);
|
|
47
|
+
return processColumnRender(child, renderMode, headerWrap);
|
|
44
48
|
});
|
|
45
49
|
}
|
|
46
50
|
return processedColumn;
|
|
@@ -33,8 +33,8 @@ var COLORS = {
|
|
|
33
33
|
checkboxDisabled: "#d9d9d9",
|
|
34
34
|
};
|
|
35
35
|
// 字体配置
|
|
36
|
-
var FONT_FAMILY = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto';
|
|
37
|
-
var FONT_SIZE = 13;
|
|
36
|
+
var FONT_FAMILY = '"Microsoft YaHei", 微软雅黑, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
37
|
+
var FONT_SIZE = 13; // 增大1px,提升清晰度
|
|
38
38
|
// 图标尺寸
|
|
39
39
|
var CHECKBOX_SIZE = 16;
|
|
40
40
|
var SORT_ICON_SIZE = 6;
|