zmdms-webui 2.3.4 → 2.3.6
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 +28 -19
- package/dist/es/canvastable/hooks/useHeaderHeight.js +16 -12
- 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/useTableInteraction.js +5 -3
- package/dist/es/canvastable/hooks/useTableRender.js +168 -60
- package/dist/es/canvastable/interface.d.ts +5 -5
- package/dist/es/canvastable/utils/columnHelpers.js +6 -2
- package/dist/es/canvastable/utils/constants.js +3 -2
- package/dist/es/canvastable/utils/multiHeaderHelpers.js +123 -1
- package/dist/es/canvastable/utils/tableCalculations.js +42 -11
- 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);
|
|
@@ -68,11 +68,6 @@ function CanvasTable(props) {
|
|
|
68
68
|
}), defaultDynamicList = _u.defaultDynamicList, currentDynamicList = _u.currentDynamicList, onCurrentListChange = _u.onCurrentListChange, dynamicSettingRef = _u.dynamicSettingRef;
|
|
69
69
|
// 根据动态列配置处理columns
|
|
70
70
|
var dynamicColumns = useMemo(function () { return getTableColumns(columns, currentDynamicList); }, [columns, currentDynamicList]).columns;
|
|
71
|
-
// 计算表头动态高度(支持wrap换行)
|
|
72
|
-
var calculatedHeaderHeight = useHeaderHeight({
|
|
73
|
-
columns: (dynamicColumns || columns),
|
|
74
|
-
headerHeight: headerHeight,
|
|
75
|
-
});
|
|
76
71
|
// 内部列宽状态(未开启动态配置时使用)
|
|
77
72
|
var _v = useState({}), internalColumnWidths = _v[0], setInternalColumnWidths = _v[1];
|
|
78
73
|
// 处理选中框列、序号列等
|
|
@@ -85,6 +80,7 @@ function CanvasTable(props) {
|
|
|
85
80
|
dynamicKey: dynamicKey,
|
|
86
81
|
renderMode: renderMode,
|
|
87
82
|
dynamicSettingRef: dynamicSettingRef,
|
|
83
|
+
headerWrap: headerWrap,
|
|
88
84
|
});
|
|
89
85
|
// 表格状态管理
|
|
90
86
|
var _w = useTableState({
|
|
@@ -109,8 +105,25 @@ function CanvasTable(props) {
|
|
|
109
105
|
columns: processedColumns,
|
|
110
106
|
dataSource: newDataSource,
|
|
111
107
|
}), finalDataSource = _y.finalDataSource, hasSummaryRow = _y.hasSummaryRow;
|
|
112
|
-
//
|
|
113
|
-
var
|
|
108
|
+
// 自动高度计算(需要在容器尺寸计算之前)
|
|
109
|
+
var autoHeight = useCanvasTableAutoHeight(isAutoScrollY, autoScrollYMarginBottom, canvasTableId);
|
|
110
|
+
// 监听容器尺寸变化(提前计算,用于列宽自适应)
|
|
111
|
+
var containerSize = useContainerSize({
|
|
112
|
+
containerRef: containerRef,
|
|
113
|
+
width: width,
|
|
114
|
+
height: isAutoScrollY ? autoHeight || height : height,
|
|
115
|
+
});
|
|
116
|
+
var containerWidth = containerSize.width;
|
|
117
|
+
var containerHeight = containerSize.height;
|
|
118
|
+
// 计算列的渲染信息(使用容器宽度进行自适应填充)
|
|
119
|
+
var columnRenderInfos = useMemo(function () { return calculateColumnRenderInfos(processedColumns, containerWidth); }, [processedColumns, containerWidth]);
|
|
120
|
+
// 计算表头动态高度(支持wrap换行)
|
|
121
|
+
// 注意:必须在 columnRenderInfos 计算之后,以便使用实际的列宽
|
|
122
|
+
var calculatedHeaderHeight = useHeaderHeight({
|
|
123
|
+
columns: processedColumns,
|
|
124
|
+
headerHeight: headerHeight,
|
|
125
|
+
columnRenderInfos: columnRenderInfos,
|
|
126
|
+
});
|
|
114
127
|
// 计算合并单元格信息
|
|
115
128
|
var mergeCellMap = useMergeCells({
|
|
116
129
|
dataSource: finalDataSource,
|
|
@@ -123,16 +136,6 @@ function CanvasTable(props) {
|
|
|
123
136
|
var totalHeight = useMemo(function () {
|
|
124
137
|
return calculateTotalHeight(calculatedHeaderHeight, finalDataSource.length, rowHeight);
|
|
125
138
|
}, [calculatedHeaderHeight, finalDataSource.length, rowHeight]);
|
|
126
|
-
// 自动高度计算
|
|
127
|
-
var autoHeight = useCanvasTableAutoHeight(isAutoScrollY, autoScrollYMarginBottom, canvasTableId);
|
|
128
|
-
// 监听容器尺寸变化
|
|
129
|
-
var containerSize = useContainerSize({
|
|
130
|
-
containerRef: containerRef,
|
|
131
|
-
width: width,
|
|
132
|
-
height: isAutoScrollY ? autoHeight || height : height,
|
|
133
|
-
});
|
|
134
|
-
var containerWidth = containerSize.width;
|
|
135
|
-
var containerHeight = containerSize.height;
|
|
136
139
|
// 计算滚动条指标
|
|
137
140
|
var _z = useScrollbarMetrics({
|
|
138
141
|
containerWidth: containerWidth,
|
|
@@ -303,7 +306,8 @@ function CanvasTable(props) {
|
|
|
303
306
|
}), handleCanvasMouseDown = _3.handleCanvasMouseDown, handleCanvasMouseMove = _3.handleCanvasMouseMove, handleCanvasMouseUp = _3.handleCanvasMouseUp, handleCanvasMouseLeave = _3.handleCanvasMouseLeave, handleCanvasContextMenu = _3.handleCanvasContextMenu;
|
|
304
307
|
// 渲染表格
|
|
305
308
|
useTableRender(__assign(__assign({ canvasRef: canvasRef, processedDataSource: finalDataSource, columnRenderInfos: columnRenderInfos, columns: processedColumns, // 传递原始columns用于渲染多级表头
|
|
306
|
-
state: state, scrollState: scrollState, rowSelection: rowSelection, containerWidth: containerWidth, containerHeight: containerHeight, headerHeight: calculatedHeaderHeight,
|
|
309
|
+
state: state, scrollState: scrollState, rowSelection: rowSelection, containerWidth: containerWidth, containerHeight: containerHeight, headerHeight: calculatedHeaderHeight, baseHeaderHeight: headerHeight, // 传入原始基础高度用于计算每层高度
|
|
310
|
+
rowHeight: rowHeight, bordered: bordered, striped: striped }, scrollbarMetrics), { getRowKey: getRowKey, resizeState: resizeState, getColumnWidth: getColumnWidth, RESIZE_HANDLE_WIDTH: RESIZE_HANDLE_WIDTH, mergeCellMap: mergeCellMap, hasSummaryRow: hasSummaryRow, fixedRowsCount: fixedRowsCount, fixedRowsConfig: fixedRowsConfig, summaryFixed: summaryFixed }));
|
|
307
311
|
// 单元格覆盖层
|
|
308
312
|
var cellOverlays = useTableCellOverlay({
|
|
309
313
|
canvasRef: canvasRef,
|
|
@@ -365,6 +369,11 @@ function CanvasTable(props) {
|
|
|
365
369
|
display: "block",
|
|
366
370
|
// cursor 由 useTableInteraction 动态管理
|
|
367
371
|
touchAction: "none",
|
|
372
|
+
// 优化文本渲染 - 使用系统级文本渲染优化
|
|
373
|
+
fontSmooth: "always",
|
|
374
|
+
WebkitFontSmoothing: "antialiased",
|
|
375
|
+
MozOsxFontSmoothing: "grayscale",
|
|
376
|
+
textRendering: "optimizeLegibility",
|
|
368
377
|
} }), dynamicKey ? (jsx(DynamicSetting, { parentDynamicKey: TABLE_DYNAMIC_KEY, dynamicKey: dynamicKey, defaultList: defaultDynamicList, onCurrentListChange: onCurrentListChange, ref: dynamicSettingRef, hiddenOperationIcon: true, isMore: true, isFixed: true, isDimensionDynamic: isDimensionDynamic })) : null, jsx(EmptyPlaceholder, { visible: finalDataSource.length === 0, text: emptyText, headerHeight: calculatedHeaderHeight }), jsx(HeaderOverlay, { overlays: headerOverlays }), jsx(CellOverlay, { overlays: cellOverlays }), filterPopoverElement, jsx(Tooltip, { visible: state.tooltip.visible, content: state.tooltip.content, position: state.tooltip.position }), jsx(BadgePopover, { visible: state.badgePopover.visible, content: state.badgePopover.content, position: state.badgePopover.position, popoverRef: badgePopoverRef }), menuId ? (jsx(CanvasTableMenu, { menuId: menuId, containerRef: containerRef, isFullscreenHandle: isFullscreenHandle, tableMenuRef: tableMenuRef, onCopy: handleCopy, onExport: dynamicKey ? exportExcel : undefined })) : null] })) })));
|
|
369
378
|
}
|
|
370
379
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { __spreadArray } from '../../_virtual/_tslib.js';
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
-
import { getMaxDepth } from '../utils/multiHeaderHelpers.js';
|
|
3
|
+
import { getMaxDepth, calculateLayerHeights } 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;
|
|
10
10
|
var ICON_SPACING = 4;
|
|
11
|
-
var TEXT_PADDING =
|
|
11
|
+
var TEXT_PADDING = 5;
|
|
12
12
|
/**
|
|
13
13
|
* 计算文本在给定宽度下需要的行数
|
|
14
14
|
*/
|
|
@@ -46,12 +46,11 @@ var calculateTextLines = function (ctx, text, maxWidth) {
|
|
|
46
46
|
/**
|
|
47
47
|
* 计算单个列标题需要的行数
|
|
48
48
|
*/
|
|
49
|
-
var calculateColumnLines = function (ctx, column) {
|
|
49
|
+
var calculateColumnLines = function (ctx, column, columnWidth) {
|
|
50
50
|
if (!column.wrap || !column.title) {
|
|
51
51
|
return 1;
|
|
52
52
|
}
|
|
53
53
|
var titleText = String(column.title);
|
|
54
|
-
var columnWidth = column.width || 100;
|
|
55
54
|
// 计算图标占用的宽度
|
|
56
55
|
var hasOrder = column.isOrder !== false;
|
|
57
56
|
var hasFilter = column.isFilter !== false;
|
|
@@ -69,18 +68,19 @@ var calculateColumnLines = function (ctx, column) {
|
|
|
69
68
|
};
|
|
70
69
|
/**
|
|
71
70
|
* Hook: 计算表头动态高度
|
|
72
|
-
*
|
|
71
|
+
* 支持多级表头和文本换行
|
|
73
72
|
*/
|
|
74
73
|
var useHeaderHeight = function (params) {
|
|
75
|
-
var columns = params.columns, headerHeight = params.headerHeight;
|
|
74
|
+
var columns = params.columns, headerHeight = params.headerHeight, columnRenderInfos = params.columnRenderInfos;
|
|
76
75
|
var _a = useState(headerHeight), calculatedHeight = _a[0], setCalculatedHeight = _a[1];
|
|
77
76
|
useEffect(function () {
|
|
78
77
|
var calculateHeight = function () {
|
|
79
78
|
// 获取多级表头的最大深度
|
|
80
79
|
var maxDepth = getMaxDepth(columns);
|
|
81
|
-
//
|
|
80
|
+
// 如果是多级表头,使用 calculateLayerHeights 计算总高度
|
|
82
81
|
if (maxDepth > 1) {
|
|
83
|
-
|
|
82
|
+
var layerHeights = calculateLayerHeights(columns, headerHeight, columnRenderInfos).layerHeights;
|
|
83
|
+
return layerHeights.reduce(function (sum, h) { return sum + h; }, 0);
|
|
84
84
|
}
|
|
85
85
|
// 单层表头,考虑文本换行
|
|
86
86
|
var tempCanvas = document.createElement("canvas");
|
|
@@ -88,15 +88,19 @@ var useHeaderHeight = function (params) {
|
|
|
88
88
|
if (!ctx)
|
|
89
89
|
return headerHeight;
|
|
90
90
|
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
var maxLines = Math.max.apply(Math, __spreadArray([1], columns.map(function (column, index) {
|
|
92
|
+
var _a;
|
|
93
|
+
// 使用实际的列宽(如果有的话)
|
|
94
|
+
var columnWidth = ((_a = columnRenderInfos === null || columnRenderInfos === void 0 ? void 0 : columnRenderInfos[index]) === null || _a === void 0 ? void 0 : _a.width) || column.width || 100;
|
|
95
|
+
return calculateColumnLines(ctx, column, columnWidth);
|
|
96
|
+
}), false));
|
|
93
97
|
// 基础高度 + (额外行数 * 行高)
|
|
94
98
|
return maxLines > 1
|
|
95
99
|
? headerHeight + (maxLines - 1) * LINE_HEIGHT
|
|
96
100
|
: headerHeight;
|
|
97
101
|
};
|
|
98
102
|
setCalculatedHeight(calculateHeight());
|
|
99
|
-
}, [columns, headerHeight]);
|
|
103
|
+
}, [columns, headerHeight, columnRenderInfos]);
|
|
100
104
|
return calculatedHeight;
|
|
101
105
|
};
|
|
102
106
|
|
|
@@ -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]);
|
|
@@ -187,7 +187,7 @@ var useTableInteraction = function (params) {
|
|
|
187
187
|
var badgeProps = typeof column.badge === "function"
|
|
188
188
|
? column.badge(record)
|
|
189
189
|
: column.badge;
|
|
190
|
-
if (!badgeProps
|
|
190
|
+
if (!badgeProps)
|
|
191
191
|
return false;
|
|
192
192
|
// 计算单元格的位置和大小
|
|
193
193
|
var cellX = fixed ? fixedLeft : colX - scrollState.scrollLeft;
|
|
@@ -198,10 +198,12 @@ var useTableInteraction = function (params) {
|
|
|
198
198
|
// 使用工具函数判断点击位置是否在三角形区域内
|
|
199
199
|
var isInTriangle = isPointInTriangle(x, y, cellX, cellY, cellWidth, cellHeight, position);
|
|
200
200
|
if (isInTriangle) {
|
|
201
|
+
// 获取要显示的内容
|
|
202
|
+
var popoverContent_1 = badgeProps.title || "";
|
|
201
203
|
// 如果已经显示相同内容的弹框,则关闭;否则显示新弹框
|
|
202
204
|
setState(function (prev) {
|
|
203
205
|
if (prev.badgePopover.visible &&
|
|
204
|
-
prev.badgePopover.content ===
|
|
206
|
+
prev.badgePopover.content === popoverContent_1) {
|
|
205
207
|
return __assign(__assign({}, prev), { badgePopover: {
|
|
206
208
|
visible: false,
|
|
207
209
|
content: "",
|
|
@@ -211,7 +213,7 @@ var useTableInteraction = function (params) {
|
|
|
211
213
|
else {
|
|
212
214
|
return __assign(__assign({}, prev), { badgePopover: {
|
|
213
215
|
visible: true,
|
|
214
|
-
content:
|
|
216
|
+
content: popoverContent_1,
|
|
215
217
|
position: { x: clientX + 10, y: clientY + 10 },
|
|
216
218
|
} });
|
|
217
219
|
}
|
|
@@ -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,9 @@ 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 +
|
|
132
|
+
var textX = drawX + 5;
|
|
126
133
|
var textY = headerHeight / 2;
|
|
127
134
|
// 绘制选择框
|
|
128
135
|
if (column.key === "__selection__") {
|
|
@@ -135,8 +142,8 @@ var useTableRender = function (params) {
|
|
|
135
142
|
else {
|
|
136
143
|
// 计算图标占用的宽度
|
|
137
144
|
var iconArea = calculateIconArea(column, width);
|
|
138
|
-
// 计算文本可用宽度(左边距
|
|
139
|
-
var textMaxWidth = width -
|
|
145
|
+
// 计算文本可用宽度(左边距5px + 图标宽度 )
|
|
146
|
+
var textMaxWidth = width - 5 - iconArea.iconsWidth;
|
|
140
147
|
// 绘制文本(如果 title 是 React 元素,跳过Canvas渲染,在覆盖层中渲染)
|
|
141
148
|
if (!isValidElement(column.title)) {
|
|
142
149
|
var titleText = String(column.title || "");
|
|
@@ -205,7 +212,8 @@ var useTableRender = function (params) {
|
|
|
205
212
|
}
|
|
206
213
|
});
|
|
207
214
|
// 绘制多级表头单元格
|
|
208
|
-
var drawMultiHeaderCell = useMemoizedFn(function (ctx, headerCell,
|
|
215
|
+
var drawMultiHeaderCell = useMemoizedFn(function (ctx, headerCell, layerHeight, y, displayWidth, currentDepth, totalCellHeight, // 单元格的总高度(考虑rowSpan)
|
|
216
|
+
onlyFixed // 是否只绘制fixed列
|
|
209
217
|
) {
|
|
210
218
|
if (onlyFixed === void 0) { onlyFixed = false; }
|
|
211
219
|
var column = headerCell.column, colSpan = headerCell.colSpan, rowSpan = headerCell.rowSpan, colIndex = headerCell.colIndex;
|
|
@@ -245,19 +253,19 @@ var useTableRender = function (params) {
|
|
|
245
253
|
}
|
|
246
254
|
// 绘制单元格背景
|
|
247
255
|
ctx.fillStyle = COLORS.headerBg;
|
|
248
|
-
ctx.fillRect(drawX, y, width,
|
|
256
|
+
ctx.fillRect(drawX, y, width, totalCellHeight);
|
|
249
257
|
// 绘制边框
|
|
250
258
|
if (bordered) {
|
|
251
259
|
ctx.strokeStyle = COLORS.border;
|
|
252
260
|
ctx.lineWidth = 1;
|
|
253
|
-
ctx.strokeRect(drawX, y, width,
|
|
261
|
+
ctx.strokeRect(drawX, y, width, totalCellHeight);
|
|
254
262
|
}
|
|
255
263
|
// 判断是否是最末级表头(当前行+rowSpan 是否等于最大深度)
|
|
256
264
|
var isLastLevel = currentDepth + rowSpan === maxDepth;
|
|
257
265
|
// 判断是否是特殊列(选择框列、序号列等不需要排序和筛选的列)
|
|
258
266
|
var isSpecialColumn = column.key === "__selection__" || column.key === "__index__";
|
|
259
267
|
// 绘制单元格内容
|
|
260
|
-
var textY = y +
|
|
268
|
+
var textY = y + totalCellHeight / 2;
|
|
261
269
|
// 绘制选择框(只在第一行显示全选框)
|
|
262
270
|
if (column.key === "__selection__" && currentDepth === 0) {
|
|
263
271
|
// 计算选中状态
|
|
@@ -269,7 +277,7 @@ var useTableRender = function (params) {
|
|
|
269
277
|
else {
|
|
270
278
|
// 绘制文本
|
|
271
279
|
ctx.fillStyle = COLORS.text;
|
|
272
|
-
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
280
|
+
ctx.font = "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
273
281
|
ctx.textBaseline = "middle";
|
|
274
282
|
if (!isValidElement(column.title)) {
|
|
275
283
|
var titleText = String(column.title || "");
|
|
@@ -281,16 +289,42 @@ var useTableRender = function (params) {
|
|
|
281
289
|
// 如果是最末级且有图标,文本左对齐;否则居中
|
|
282
290
|
if (isLastLevel && iconArea.iconsWidth > 0) {
|
|
283
291
|
ctx.textAlign = "left";
|
|
284
|
-
var
|
|
285
|
-
var textMaxWidth = width -
|
|
286
|
-
|
|
287
|
-
|
|
292
|
+
var textX_1 = drawX + 5;
|
|
293
|
+
var textMaxWidth = width - 5 - iconArea.iconsWidth;
|
|
294
|
+
// 根据 wrap 属性处理文本
|
|
295
|
+
if (column.wrap) {
|
|
296
|
+
// 换行显示(支持\n和自动换行)
|
|
297
|
+
var lines = wrapText(ctx, titleText, textMaxWidth);
|
|
298
|
+
var lineHeight_2 = 20;
|
|
299
|
+
var startY_2 = textY - ((lines.length - 1) * lineHeight_2) / 2;
|
|
300
|
+
lines.forEach(function (line, index) {
|
|
301
|
+
var lineY = startY_2 + index * lineHeight_2;
|
|
302
|
+
ctx.fillText(line, textX_1, lineY);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
var truncated = truncateText(ctx, titleText, textMaxWidth);
|
|
307
|
+
ctx.fillText(truncated, textX_1, textY);
|
|
308
|
+
}
|
|
288
309
|
}
|
|
289
310
|
else {
|
|
290
311
|
ctx.textAlign = "center";
|
|
291
|
-
var
|
|
292
|
-
|
|
293
|
-
|
|
312
|
+
var textX_2 = drawX + width / 2;
|
|
313
|
+
// 根据 wrap 属性处理文本
|
|
314
|
+
if (column.wrap) {
|
|
315
|
+
// 换行显示(支持\n和自动换行)
|
|
316
|
+
var lines = wrapText(ctx, titleText, width - 10); // 左右各5px
|
|
317
|
+
var lineHeight_3 = 20;
|
|
318
|
+
var startY_3 = textY - ((lines.length - 1) * lineHeight_3) / 2;
|
|
319
|
+
lines.forEach(function (line, index) {
|
|
320
|
+
var lineY = startY_3 + index * lineHeight_3;
|
|
321
|
+
ctx.fillText(line, textX_2, lineY);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
var truncated = truncateText(ctx, titleText, width - 10); // 左右各5px
|
|
326
|
+
ctx.fillText(truncated, textX_2, textY);
|
|
327
|
+
}
|
|
294
328
|
}
|
|
295
329
|
// 绘制筛选图标(仅在最末级表头显示)
|
|
296
330
|
if (iconArea.hasFilter) {
|
|
@@ -315,20 +349,31 @@ var useTableRender = function (params) {
|
|
|
315
349
|
ctx.fillStyle = COLORS.headerBg;
|
|
316
350
|
ctx.fillRect(0, 0, displayWidth, headerHeight);
|
|
317
351
|
if (isMultiHeader) {
|
|
318
|
-
//
|
|
319
|
-
var rowHeight_1 = headerHeight / maxDepth;
|
|
352
|
+
// 多级表头渲染,使用计算出的每一层高度
|
|
320
353
|
// 先绘制非fixed列
|
|
321
354
|
flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
|
|
322
|
-
var y = rowIndex
|
|
355
|
+
var y = layerStartY[rowIndex];
|
|
356
|
+
var currentLayerHeight = layerHeights[rowIndex];
|
|
323
357
|
headerRow.forEach(function (headerCell) {
|
|
324
|
-
|
|
358
|
+
// 计算单元格的总高度(如果跨越多层)
|
|
359
|
+
var totalCellHeight = 0;
|
|
360
|
+
for (var i = rowIndex; i < rowIndex + headerCell.rowSpan; i++) {
|
|
361
|
+
totalCellHeight += layerHeights[i];
|
|
362
|
+
}
|
|
363
|
+
drawMultiHeaderCell(ctx, headerCell, currentLayerHeight, y, displayWidth, rowIndex, totalCellHeight, false);
|
|
325
364
|
});
|
|
326
365
|
});
|
|
327
366
|
// 再绘制fixed列(覆盖在非fixed列之上)
|
|
328
367
|
flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
|
|
329
|
-
var y = rowIndex
|
|
368
|
+
var y = layerStartY[rowIndex];
|
|
369
|
+
var currentLayerHeight = layerHeights[rowIndex];
|
|
330
370
|
headerRow.forEach(function (headerCell) {
|
|
331
|
-
|
|
371
|
+
// 计算单元格的总高度(如果跨越多层)
|
|
372
|
+
var totalCellHeight = 0;
|
|
373
|
+
for (var i = rowIndex; i < rowIndex + headerCell.rowSpan; i++) {
|
|
374
|
+
totalCellHeight += layerHeights[i];
|
|
375
|
+
}
|
|
376
|
+
drawMultiHeaderCell(ctx, headerCell, currentLayerHeight, y, displayWidth, rowIndex, totalCellHeight, true);
|
|
332
377
|
});
|
|
333
378
|
});
|
|
334
379
|
}
|
|
@@ -370,8 +415,25 @@ var useTableRender = function (params) {
|
|
|
370
415
|
(needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
|
|
371
416
|
fixedBottomHeight -
|
|
372
417
|
fixedSummaryHeight;
|
|
373
|
-
//
|
|
374
|
-
|
|
418
|
+
// 提前检查这一行是否有合并单元格,找出最大的rowSpan
|
|
419
|
+
var maxRowSpanInRow = 1;
|
|
420
|
+
if (mergeCellMap) {
|
|
421
|
+
columnRenderInfos.forEach(function (_, colIndex) {
|
|
422
|
+
var mergeCellKey = "".concat(rowIndex, "-").concat(colIndex);
|
|
423
|
+
var mergeInfo = mergeCellMap.get(mergeCellKey);
|
|
424
|
+
// 只考虑主单元格(不是被合并的单元格)
|
|
425
|
+
if (mergeInfo &&
|
|
426
|
+
!mergeInfo.skip &&
|
|
427
|
+
mergeInfo.rowSpan > maxRowSpanInRow) {
|
|
428
|
+
maxRowSpanInRow = mergeInfo.rowSpan;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
// 计算这一行的实际高度(考虑合并单元格)
|
|
433
|
+
var actualRowHeight = rowHeight * maxRowSpanInRow;
|
|
434
|
+
// 跳过不在可视区域的行
|
|
435
|
+
// 关键:使用合并后的实际高度判断,确保合并单元格的最后一行还在可视区域内时继续渲染
|
|
436
|
+
if (y + actualRowHeight < headerHeight || y >= effectiveHeight) {
|
|
375
437
|
return;
|
|
376
438
|
}
|
|
377
439
|
var recordKey = getRowKey(record, rowIndex);
|
|
@@ -430,7 +492,10 @@ var useTableRender = function (params) {
|
|
|
430
492
|
ctx.beginPath();
|
|
431
493
|
// 裁剪区域:确保不覆盖表头,从 headerHeight 开始
|
|
432
494
|
var clipY = Math.max(y, headerHeight);
|
|
433
|
-
|
|
495
|
+
// 计算单元格实际底部位置,并限制在可视区域内
|
|
496
|
+
var cellBottom = Math.min(y + cellHeight, effectiveHeight);
|
|
497
|
+
// 裁剪高度 = 单元格可见底部 - 裁剪起始位置
|
|
498
|
+
var clipHeight = Math.max(0, cellBottom - clipY);
|
|
434
499
|
// 对于非fixed列,还需要裁剪掉被fixed列遮挡的部分
|
|
435
500
|
var clipX = drawX;
|
|
436
501
|
var clipWidth = width;
|
|
@@ -476,9 +541,12 @@ var useTableRender = function (params) {
|
|
|
476
541
|
else {
|
|
477
542
|
var dataIndex = column.dataIndex || column.key;
|
|
478
543
|
var cellValue = record[dataIndex];
|
|
544
|
+
// 检查是否是合计行
|
|
545
|
+
var isSummaryRow = record[IS_SUMMARY];
|
|
479
546
|
// 检查是否有 render 函数且返回自定义 ReactNode
|
|
480
547
|
var shouldRenderInCanvas = true;
|
|
481
|
-
|
|
548
|
+
// 对于合计行,不应用render函数,直接显示原始数据
|
|
549
|
+
if (column.render && !isSummaryRow) {
|
|
482
550
|
var rendered = column.render(cellValue, record, rowIndex);
|
|
483
551
|
// 如果返回的不是字符串或数字,说明是自定义组件,应该在覆盖层渲染,不在 Canvas 渲染
|
|
484
552
|
if (typeof rendered !== "string" &&
|
|
@@ -491,52 +559,59 @@ var useTableRender = function (params) {
|
|
|
491
559
|
// 只有在需要在 Canvas 中渲染时才绘制文本
|
|
492
560
|
if (shouldRenderInCanvas) {
|
|
493
561
|
// 应用格式化(日期、精度、千分符)
|
|
494
|
-
var cellText =
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
rowIndex: rowIndex,
|
|
498
|
-
renderFn: column.render,
|
|
499
|
-
});
|
|
500
|
-
// 如果没有自定义render,应用格式化
|
|
501
|
-
if (!column.render) {
|
|
562
|
+
var cellText = void 0;
|
|
563
|
+
// 对于合计行,直接使用格式化函数,不应用render
|
|
564
|
+
if (isSummaryRow) {
|
|
502
565
|
cellText = formatCellValue(cellValue, column);
|
|
503
566
|
}
|
|
567
|
+
else {
|
|
568
|
+
cellText = extractCellText({
|
|
569
|
+
cellValue: cellValue,
|
|
570
|
+
record: record,
|
|
571
|
+
rowIndex: rowIndex,
|
|
572
|
+
renderFn: column.render,
|
|
573
|
+
});
|
|
574
|
+
// 如果没有自定义render,应用格式化
|
|
575
|
+
if (!column.render) {
|
|
576
|
+
cellText = formatCellValue(cellValue, column);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
504
579
|
// 绘制文本
|
|
505
580
|
ctx.fillStyle = COLORS.text;
|
|
506
581
|
// 合计行字体加粗
|
|
507
582
|
ctx.font = record[IS_SUMMARY]
|
|
508
583
|
? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
|
|
509
|
-
: "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
584
|
+
: "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
510
585
|
ctx.textBaseline = "middle";
|
|
511
586
|
var align = column.align || "left";
|
|
512
|
-
var
|
|
587
|
+
var textX_3 = drawX + 5;
|
|
513
588
|
if (align === "center") {
|
|
514
589
|
ctx.textAlign = "center";
|
|
515
|
-
|
|
590
|
+
textX_3 = drawX + width / 2;
|
|
516
591
|
}
|
|
517
592
|
else if (align === "right") {
|
|
518
593
|
ctx.textAlign = "right";
|
|
519
|
-
|
|
594
|
+
textX_3 = drawX + width - 5;
|
|
520
595
|
}
|
|
521
596
|
else {
|
|
522
597
|
ctx.textAlign = "left";
|
|
523
598
|
}
|
|
524
599
|
// 处理文本显示(ellipsis 或 wrap)
|
|
525
|
-
var maxWidth = width -
|
|
600
|
+
var maxWidth = width - 10; // 左右各5px
|
|
526
601
|
var ellipsis = column.ellipsis !== false; // 默认为true
|
|
527
602
|
var wrap = column.wrap === true; // 默认为false
|
|
528
603
|
if (wrap) {
|
|
529
604
|
// 换行显示
|
|
530
605
|
var lines = wrapText(ctx, cellText, maxWidth);
|
|
531
|
-
var
|
|
532
|
-
var totalHeight = lines.length *
|
|
606
|
+
var lineHeight_4 = FONT_SIZE + 4; // 行高
|
|
607
|
+
var totalHeight = lines.length * lineHeight_4;
|
|
533
608
|
// 使用合并后的单元格高度计算垂直居中位置
|
|
534
|
-
var
|
|
609
|
+
var startY_4 = y + (cellHeight - totalHeight) / 2 + lineHeight_4 / 2;
|
|
535
610
|
lines.forEach(function (line, lineIndex) {
|
|
536
|
-
var lineY =
|
|
611
|
+
var lineY = startY_4 + lineIndex * lineHeight_4;
|
|
537
612
|
// 只绘制在单元格可见区域内的文本
|
|
538
613
|
if (lineY >= y && lineY <= y + cellHeight) {
|
|
539
|
-
ctx.fillText(line,
|
|
614
|
+
ctx.fillText(line, textX_3, lineY);
|
|
540
615
|
}
|
|
541
616
|
});
|
|
542
617
|
}
|
|
@@ -544,15 +619,13 @@ var useTableRender = function (params) {
|
|
|
544
619
|
// 省略显示 - 使用合并后的单元格高度
|
|
545
620
|
var textY = y + cellHeight / 2;
|
|
546
621
|
var truncatedText = truncateText(ctx, cellText, maxWidth);
|
|
547
|
-
ctx.fillText(truncatedText,
|
|
622
|
+
ctx.fillText(truncatedText, textX_3, textY);
|
|
548
623
|
}
|
|
549
624
|
else {
|
|
550
625
|
// 不处理,直接显示 - 使用合并后的单元格高度
|
|
551
626
|
var textY = y + cellHeight / 2;
|
|
552
|
-
ctx.fillText(cellText,
|
|
627
|
+
ctx.fillText(cellText, textX_3, textY);
|
|
553
628
|
}
|
|
554
|
-
// 绘制角标(跳过合计行)
|
|
555
|
-
var isSummaryRow = hasSummaryRow && rowIndex === processedDataSource.length - 1;
|
|
556
629
|
if (column.badge && !isSummaryRow) {
|
|
557
630
|
var badgeProps = typeof column.badge === "function"
|
|
558
631
|
? column.badge(record)
|
|
@@ -765,7 +838,7 @@ var useTableRender = function (params) {
|
|
|
765
838
|
return;
|
|
766
839
|
var indexText = String(rowIndex + 1);
|
|
767
840
|
ctx.fillStyle = COLORS.text;
|
|
768
|
-
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
841
|
+
ctx.font = "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
769
842
|
ctx.textBaseline = "middle";
|
|
770
843
|
setTextAlign(ctx, "center");
|
|
771
844
|
var textX = drawX + actualWidth / 2;
|
|
@@ -790,7 +863,9 @@ var useTableRender = function (params) {
|
|
|
790
863
|
var cellText = formatCellValue(cellValue, column);
|
|
791
864
|
// 检查是否有render函数且返回自定义ReactNode
|
|
792
865
|
var shouldRenderInCanvas = true;
|
|
793
|
-
|
|
866
|
+
// 对于合计行,不应用render函数,直接显示原始数据
|
|
867
|
+
var isSummaryRowFixed = rowType === "summary";
|
|
868
|
+
if (column.render && !isSummaryRowFixed) {
|
|
794
869
|
var rendered = column.render(cellValue, record, rowIndex);
|
|
795
870
|
// 如果返回的不是字符串或数字,说明是自定义组件,应该在覆盖层渲染,不在 Canvas 渲染
|
|
796
871
|
if (typeof rendered !== "string" &&
|
|
@@ -807,11 +882,11 @@ var useTableRender = function (params) {
|
|
|
807
882
|
ctx.font =
|
|
808
883
|
rowType === "summary"
|
|
809
884
|
? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
|
|
810
|
-
: "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
885
|
+
: "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
811
886
|
ctx.textBaseline = "middle";
|
|
812
887
|
var align = column.align || "left";
|
|
813
888
|
setTextAlign(ctx, align);
|
|
814
|
-
var padding =
|
|
889
|
+
var padding = 5;
|
|
815
890
|
var maxWidth = actualWidth - padding * 2;
|
|
816
891
|
var textY = fixedY + rowHeight / 2;
|
|
817
892
|
var textX = calculateTextX(drawX, actualWidth, align, padding);
|
|
@@ -1160,6 +1235,9 @@ var useTableRender = function (params) {
|
|
|
1160
1235
|
canvas.style.width = "".concat(displayWidth, "px");
|
|
1161
1236
|
canvas.style.height = "".concat(displayHeight, "px");
|
|
1162
1237
|
ctx.scale(dpr, dpr);
|
|
1238
|
+
// 优化文字渲染质量,防止模糊
|
|
1239
|
+
ctx.imageSmoothingEnabled = true;
|
|
1240
|
+
ctx.imageSmoothingQuality = "high";
|
|
1163
1241
|
// 清空画布
|
|
1164
1242
|
ctx.clearRect(0, 0, displayWidth, displayHeight);
|
|
1165
1243
|
// 绘制背景
|
|
@@ -1182,9 +1260,39 @@ var useTableRender = function (params) {
|
|
|
1182
1260
|
// 计算可视区域(从固定行之后开始)
|
|
1183
1261
|
var scrollStartRow = fixedTopRowsCount + Math.floor(scrollState.scrollTop / rowHeight);
|
|
1184
1262
|
var scrollEndRow = Math.min(fixedTopRowsCount +
|
|
1185
|
-
Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length -
|
|
1186
|
-
|
|
1263
|
+
Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length -
|
|
1264
|
+
fixedBottomRowsCount -
|
|
1265
|
+
(summaryFixed && hasSummaryRow ? 1 : 0) // 如果合计行固定,需要排除它
|
|
1187
1266
|
);
|
|
1267
|
+
// 关键修复:向前扩展渲染范围,检查是否有合并单元格延伸到可视区域
|
|
1268
|
+
// 策略:对每一列独立检查,找到该列最靠前的需要渲染的主合并单元格
|
|
1269
|
+
if (mergeCellMap && scrollStartRow > fixedTopRowsCount) {
|
|
1270
|
+
var originalStartRow = scrollStartRow;
|
|
1271
|
+
var minStartRow = scrollStartRow;
|
|
1272
|
+
// 对每一列独立检查
|
|
1273
|
+
for (var colIndex = 0; colIndex < columnRenderInfos.length; colIndex++) {
|
|
1274
|
+
// 向前查找这一列的主合并单元格,最多向前查找20行
|
|
1275
|
+
for (var checkRow = originalStartRow - 1; checkRow >= fixedTopRowsCount && checkRow >= originalStartRow - 20; checkRow--) {
|
|
1276
|
+
var mergeCellKey = "".concat(checkRow, "-").concat(colIndex);
|
|
1277
|
+
var mergeInfo = mergeCellMap.get(mergeCellKey);
|
|
1278
|
+
// 如果找到主单元格(不是被合并的单元格)
|
|
1279
|
+
if (mergeInfo && !mergeInfo.skip && mergeInfo.rowSpan > 1) {
|
|
1280
|
+
// 计算合并单元格的底部行索引
|
|
1281
|
+
var mergeEndRow = checkRow + mergeInfo.rowSpan - 1;
|
|
1282
|
+
// 如果这个合并单元格延伸到可视区域
|
|
1283
|
+
if (mergeEndRow >= originalStartRow) {
|
|
1284
|
+
// 更新最小起始行
|
|
1285
|
+
minStartRow = Math.min(minStartRow, checkRow);
|
|
1286
|
+
}
|
|
1287
|
+
// 找到这一列的主单元格后,停止向前查找该列
|
|
1288
|
+
break;
|
|
1289
|
+
}
|
|
1290
|
+
// 如果是skip的单元格或没有合并信息,继续向前查找
|
|
1291
|
+
// 因为主单元格可能在更前面
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
scrollStartRow = minStartRow;
|
|
1295
|
+
}
|
|
1188
1296
|
// 1. 绘制可滚动区域的数据行(包括边框)
|
|
1189
1297
|
renderWithFixedLayer(ctx,
|
|
1190
1298
|
// 渲染行内容
|
|
@@ -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
|
*/
|
|
@@ -182,13 +182,13 @@ interface ICanvasColumnType<RecordType = any> {
|
|
|
182
182
|
type ICanvasColumnsType<RecordType = any> = ICanvasColumnType<RecordType>[];
|
|
183
183
|
interface IBadgeProps {
|
|
184
184
|
/**
|
|
185
|
-
*
|
|
185
|
+
* 角标提示文本(鼠标悬停时显示)
|
|
186
186
|
*/
|
|
187
|
-
|
|
187
|
+
title?: string;
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
189
|
+
* 角标颜色
|
|
190
190
|
*/
|
|
191
|
-
|
|
191
|
+
color?: string;
|
|
192
192
|
/**
|
|
193
193
|
* 角标位置
|
|
194
194
|
*/
|
|
@@ -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,10 +33,11 @@ var COLORS = {
|
|
|
33
33
|
checkboxDisabled: "#d9d9d9",
|
|
34
34
|
};
|
|
35
35
|
// 字体配置
|
|
36
|
-
var FONT_FAMILY = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto';
|
|
36
|
+
var FONT_FAMILY = '"Microsoft YaHei", 微软雅黑, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
37
37
|
var FONT_SIZE = 13;
|
|
38
|
+
var FONT_WEIGHT = "400"; // 正常字重,避免过细导致不清晰
|
|
38
39
|
// 图标尺寸
|
|
39
40
|
var CHECKBOX_SIZE = 16;
|
|
40
41
|
var SORT_ICON_SIZE = 6;
|
|
41
42
|
|
|
42
|
-
export { CHECKBOX_SIZE, COLORS, DEFAULT_COLUMN_WIDTH, DEFAULT_SELECTION_COLUMN_WIDTH, FONT_FAMILY, FONT_SIZE, MIN_SCROLLBAR_SIZE, SCROLLBAR_PADDING, SCROLLBAR_SIZE, SORT_ICON_SIZE };
|
|
43
|
+
export { CHECKBOX_SIZE, COLORS, DEFAULT_COLUMN_WIDTH, DEFAULT_SELECTION_COLUMN_WIDTH, FONT_FAMILY, FONT_SIZE, FONT_WEIGHT, MIN_SCROLLBAR_SIZE, SCROLLBAR_PADDING, SCROLLBAR_SIZE, SORT_ICON_SIZE };
|
|
@@ -77,6 +77,128 @@ function flattenHeaders(columns) {
|
|
|
77
77
|
};
|
|
78
78
|
walk(columns, 1, { value: 0 });
|
|
79
79
|
return headerRows;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 计算每一层表头的高度(考虑文本换行)
|
|
83
|
+
* @param columns 表头列配置
|
|
84
|
+
* @param baseHeaderHeight 基础表头高度
|
|
85
|
+
* @param columnRenderInfos 列渲染信息(用于获取实际列宽)
|
|
86
|
+
* @returns 每一层的高度数组,以及每一层的起始Y坐标数组
|
|
87
|
+
*/
|
|
88
|
+
function calculateLayerHeights(columns, baseHeaderHeight, columnRenderInfos) {
|
|
89
|
+
var maxDepth = getMaxDepth(columns);
|
|
90
|
+
// 如果是单层表头,直接返回基础高度
|
|
91
|
+
if (maxDepth === 1) {
|
|
92
|
+
return {
|
|
93
|
+
layerHeights: [baseHeaderHeight],
|
|
94
|
+
layerStartY: [0],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var flattenedHeaderRows = flattenHeaders(columns);
|
|
98
|
+
var leafColumns = getLeafColumns(columns);
|
|
99
|
+
var FONT_SIZE = 13;
|
|
100
|
+
var FONT_FAMILY = '"Microsoft YaHei", 微软雅黑, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
101
|
+
var LINE_HEIGHT = 20;
|
|
102
|
+
var FILTER_ICON_WIDTH = 15;
|
|
103
|
+
var SORT_ICON_WIDTH = 15;
|
|
104
|
+
var ICON_SPACING = 4;
|
|
105
|
+
var TEXT_PADDING = 5;
|
|
106
|
+
// 创建临时canvas用于测量文本
|
|
107
|
+
var tempCanvas = document.createElement("canvas");
|
|
108
|
+
var ctx = tempCanvas.getContext("2d");
|
|
109
|
+
if (!ctx) {
|
|
110
|
+
// 如果无法获取context,返回平均分配的高度
|
|
111
|
+
return {
|
|
112
|
+
layerHeights: Array(maxDepth).fill(baseHeaderHeight),
|
|
113
|
+
layerStartY: Array.from({ length: maxDepth }, function (_, i) { return i * baseHeaderHeight; }),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
|
|
117
|
+
var layerHeights = [];
|
|
118
|
+
// 遍历每一层
|
|
119
|
+
flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
|
|
120
|
+
var maxLines = 1;
|
|
121
|
+
// 计算这一层中所有单元格需要的最大行数
|
|
122
|
+
headerRow.forEach(function (headerCell) {
|
|
123
|
+
var _a;
|
|
124
|
+
var column = headerCell.column, colSpan = headerCell.colSpan, colIndex = headerCell.colIndex, rowSpan = headerCell.rowSpan;
|
|
125
|
+
// 如果没有wrap属性或者没有title,不需要计算
|
|
126
|
+
if (!column.wrap || !column.title) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// 计算跨列单元格的总宽度
|
|
130
|
+
var totalWidth = 0;
|
|
131
|
+
for (var i = colIndex; i < colIndex + colSpan && i < leafColumns.length; i++) {
|
|
132
|
+
if (columnRenderInfos && columnRenderInfos[i]) {
|
|
133
|
+
totalWidth += columnRenderInfos[i].width;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
totalWidth += ((_a = leafColumns[i]) === null || _a === void 0 ? void 0 : _a.width) || 100;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
var titleText = String(column.title);
|
|
140
|
+
// 判断是否是最末级表头(当前行+rowSpan 是否等于最大深度)
|
|
141
|
+
var isLastLevel = rowIndex + rowSpan === maxDepth;
|
|
142
|
+
// 判断是否是特殊列(选择框列、序号列等不需要排序和筛选的列)
|
|
143
|
+
var isSpecialColumn = column.key === "__selection__" || column.key === "__index__";
|
|
144
|
+
// 计算图标占用的宽度(仅在最末级表头且非特殊列时计算)
|
|
145
|
+
var iconsWidth = 0;
|
|
146
|
+
if (isLastLevel && !isSpecialColumn) {
|
|
147
|
+
var hasOrder = column.isOrder !== false;
|
|
148
|
+
var hasFilter = column.isFilter !== false;
|
|
149
|
+
if (hasFilter)
|
|
150
|
+
iconsWidth += FILTER_ICON_WIDTH;
|
|
151
|
+
if (hasOrder) {
|
|
152
|
+
iconsWidth += SORT_ICON_WIDTH;
|
|
153
|
+
if (hasFilter)
|
|
154
|
+
iconsWidth += ICON_SPACING;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// 计算文本可用宽度
|
|
158
|
+
var textMaxWidth = totalWidth - TEXT_PADDING - iconsWidth;
|
|
159
|
+
// 计算需要的行数
|
|
160
|
+
var lines = titleText.split("\n");
|
|
161
|
+
var totalLines = 0;
|
|
162
|
+
for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
|
|
163
|
+
var line = lines_1[_i];
|
|
164
|
+
if (line === "") {
|
|
165
|
+
totalLines += 1;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
var currentLine = "";
|
|
169
|
+
var lineCount = 0;
|
|
170
|
+
var chars = line.split("");
|
|
171
|
+
for (var _b = 0, chars_1 = chars; _b < chars_1.length; _b++) {
|
|
172
|
+
var char = chars_1[_b];
|
|
173
|
+
var testLine = currentLine + char;
|
|
174
|
+
var metrics = ctx.measureText(testLine);
|
|
175
|
+
if (metrics.width > textMaxWidth && currentLine.length > 0) {
|
|
176
|
+
lineCount += 1;
|
|
177
|
+
currentLine = char;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
currentLine = testLine;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (currentLine.length > 0) {
|
|
184
|
+
lineCount += 1;
|
|
185
|
+
}
|
|
186
|
+
totalLines += lineCount;
|
|
187
|
+
}
|
|
188
|
+
maxLines = Math.max(maxLines, totalLines);
|
|
189
|
+
});
|
|
190
|
+
// 这一层的高度 = 基础高度 + (额外行数 * 行高)
|
|
191
|
+
var layerHeight = maxLines > 1
|
|
192
|
+
? baseHeaderHeight + (maxLines - 1) * LINE_HEIGHT
|
|
193
|
+
: baseHeaderHeight;
|
|
194
|
+
layerHeights.push(layerHeight);
|
|
195
|
+
});
|
|
196
|
+
// 计算每一层的起始Y坐标
|
|
197
|
+
var layerStartY = [0];
|
|
198
|
+
for (var i = 1; i < layerHeights.length; i++) {
|
|
199
|
+
layerStartY.push(layerStartY[i - 1] + layerHeights[i - 1]);
|
|
200
|
+
}
|
|
201
|
+
return { layerHeights: layerHeights, layerStartY: layerStartY };
|
|
80
202
|
}
|
|
81
203
|
|
|
82
|
-
export { computeColSpan, flattenHeaders, getLeafColumns, getMaxDepth };
|
|
204
|
+
export { calculateLayerHeights, computeColSpan, flattenHeaders, getLeafColumns, getMaxDepth };
|
|
@@ -7,27 +7,58 @@ import { getLeafColumns } from './multiHeaderHelpers.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* 计算列的渲染信息
|
|
9
9
|
* 注意:多级表头时,只处理叶子列(用于渲染数据)
|
|
10
|
+
* @param columns 列配置
|
|
11
|
+
* @param containerWidth 容器宽度(可选),如果提供且总宽度小于容器宽度,会自动填充
|
|
10
12
|
*/
|
|
11
|
-
var calculateColumnRenderInfos = function (columns) {
|
|
13
|
+
var calculateColumnRenderInfos = function (columns, containerWidth) {
|
|
12
14
|
// 获取叶子列(多级表头时只有叶子列才渲染数据)
|
|
13
15
|
var leafColumns = getLeafColumns(columns);
|
|
14
|
-
|
|
15
|
-
var
|
|
16
|
-
var
|
|
16
|
+
// 第一次计算:使用原始宽度
|
|
17
|
+
var tempInfos = [];
|
|
18
|
+
var totalWidth = 0;
|
|
17
19
|
leafColumns.forEach(function (column) {
|
|
18
20
|
var columnWidth = column.width || DEFAULT_COLUMN_WIDTH;
|
|
19
21
|
var isFixed = column.fixed === "left" || column.fixed === true;
|
|
20
|
-
|
|
22
|
+
tempInfos.push({
|
|
21
23
|
column: column,
|
|
22
|
-
x: isFixed ? fixedLeftX : currentX,
|
|
23
24
|
width: columnWidth,
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
isFixed: isFixed,
|
|
26
|
+
});
|
|
27
|
+
totalWidth += columnWidth;
|
|
28
|
+
});
|
|
29
|
+
// 如果提供了容器宽度且总宽度小于容器宽度,将剩余宽度平分到各列
|
|
30
|
+
// 注意:序号列和勾选框列除外,保持原宽度
|
|
31
|
+
if (containerWidth && totalWidth < containerWidth) {
|
|
32
|
+
var remainingWidth = containerWidth - totalWidth;
|
|
33
|
+
// 统计需要平分宽度的列(排除序号列和勾选框列)
|
|
34
|
+
var columnsToExpand = tempInfos.filter(function (info) {
|
|
35
|
+
var key = info.column.key;
|
|
36
|
+
return key !== "__index__" && key !== "__selection__";
|
|
37
|
+
});
|
|
38
|
+
if (columnsToExpand.length > 0) {
|
|
39
|
+
var additionalWidthPerColumn_1 = remainingWidth / columnsToExpand.length;
|
|
40
|
+
// 只更新需要扩展的列的宽度
|
|
41
|
+
columnsToExpand.forEach(function (info) {
|
|
42
|
+
info.width += additionalWidthPerColumn_1;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 第二次计算:生成最终的渲染信息
|
|
47
|
+
var infos = [];
|
|
48
|
+
var currentX = 0;
|
|
49
|
+
var fixedLeftX = 0;
|
|
50
|
+
tempInfos.forEach(function (tempInfo) {
|
|
51
|
+
infos.push({
|
|
52
|
+
column: tempInfo.column,
|
|
53
|
+
x: tempInfo.isFixed ? fixedLeftX : currentX,
|
|
54
|
+
width: tempInfo.width,
|
|
55
|
+
fixed: tempInfo.isFixed,
|
|
56
|
+
fixedLeft: tempInfo.isFixed ? fixedLeftX : undefined,
|
|
26
57
|
});
|
|
27
|
-
if (isFixed) {
|
|
28
|
-
fixedLeftX +=
|
|
58
|
+
if (tempInfo.isFixed) {
|
|
59
|
+
fixedLeftX += tempInfo.width;
|
|
29
60
|
}
|
|
30
|
-
currentX +=
|
|
61
|
+
currentX += tempInfo.width;
|
|
31
62
|
});
|
|
32
63
|
return infos;
|
|
33
64
|
};
|