zmdms-webui 2.3.5 → 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.
@@ -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
  // 处理选中框列、序号列等
@@ -110,8 +105,25 @@ function CanvasTable(props) {
110
105
  columns: processedColumns,
111
106
  dataSource: newDataSource,
112
107
  }), finalDataSource = _y.finalDataSource, hasSummaryRow = _y.hasSummaryRow;
113
- // 计算列的渲染信息
114
- var columnRenderInfos = useMemo(function () { return calculateColumnRenderInfos(processedColumns); }, [processedColumns]);
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
+ });
115
127
  // 计算合并单元格信息
116
128
  var mergeCellMap = useMergeCells({
117
129
  dataSource: finalDataSource,
@@ -124,16 +136,6 @@ function CanvasTable(props) {
124
136
  var totalHeight = useMemo(function () {
125
137
  return calculateTotalHeight(calculatedHeaderHeight, finalDataSource.length, rowHeight);
126
138
  }, [calculatedHeaderHeight, finalDataSource.length, rowHeight]);
127
- // 自动高度计算
128
- var autoHeight = useCanvasTableAutoHeight(isAutoScrollY, autoScrollYMarginBottom, canvasTableId);
129
- // 监听容器尺寸变化
130
- var containerSize = useContainerSize({
131
- containerRef: containerRef,
132
- width: width,
133
- height: isAutoScrollY ? autoHeight || height : height,
134
- });
135
- var containerWidth = containerSize.width;
136
- var containerHeight = containerSize.height;
137
139
  // 计算滚动条指标
138
140
  var _z = useScrollbarMetrics({
139
141
  containerWidth: containerWidth,
@@ -304,7 +306,8 @@ function CanvasTable(props) {
304
306
  }), handleCanvasMouseDown = _3.handleCanvasMouseDown, handleCanvasMouseMove = _3.handleCanvasMouseMove, handleCanvasMouseUp = _3.handleCanvasMouseUp, handleCanvasMouseLeave = _3.handleCanvasMouseLeave, handleCanvasContextMenu = _3.handleCanvasContextMenu;
305
307
  // 渲染表格
306
308
  useTableRender(__assign(__assign({ canvasRef: canvasRef, processedDataSource: finalDataSource, columnRenderInfos: columnRenderInfos, columns: processedColumns, // 传递原始columns用于渲染多级表头
307
- state: state, scrollState: scrollState, rowSelection: rowSelection, containerWidth: containerWidth, containerHeight: containerHeight, headerHeight: calculatedHeaderHeight, 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 }));
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 }));
308
311
  // 单元格覆盖层
309
312
  var cellOverlays = useTableCellOverlay({
310
313
  canvasRef: canvasRef,
@@ -366,6 +369,11 @@ function CanvasTable(props) {
366
369
  display: "block",
367
370
  // cursor 由 useTableInteraction 动态管理
368
371
  touchAction: "none",
372
+ // 优化文本渲染 - 使用系统级文本渲染优化
373
+ fontSmooth: "always",
374
+ WebkitFontSmoothing: "antialiased",
375
+ MozOsxFontSmoothing: "grayscale",
376
+ textRendering: "optimizeLegibility",
369
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] })) })));
370
378
  }
371
379
 
@@ -1,6 +1,6 @@
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
6
  var FONT_FAMILY = '"Microsoft YaHei", 微软雅黑, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
@@ -8,7 +8,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 = 16;
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
- return headerHeight * maxDepth;
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
- var maxLines = Math.max.apply(Math, __spreadArray([1], columns.map(function (column) { return calculateColumnLines(ctx, column); }), false));
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
 
@@ -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 || !badgeProps.text)
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 === badgeProps.text) {
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: badgeProps.text,
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 = 16; }
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 + 16;
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
- // 计算文本可用宽度(左边距16px + 图标宽度 )
139
- var textMaxWidth = width - 16 - iconArea.iconsWidth;
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, rowHeight, y, displayWidth, currentDepth, onlyFixed // 是否只绘制fixed列
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, rowHeight * rowSpan);
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, rowHeight * rowSpan);
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 + (rowHeight * rowSpan) / 2;
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,8 +289,8 @@ var useTableRender = function (params) {
281
289
  // 如果是最末级且有图标,文本左对齐;否则居中
282
290
  if (isLastLevel && iconArea.iconsWidth > 0) {
283
291
  ctx.textAlign = "left";
284
- var textX_1 = drawX + 16;
285
- var textMaxWidth = width - 16 - iconArea.iconsWidth;
292
+ var textX_1 = drawX + 5;
293
+ var textMaxWidth = width - 5 - iconArea.iconsWidth;
286
294
  // 根据 wrap 属性处理文本
287
295
  if (column.wrap) {
288
296
  // 换行显示(支持\n和自动换行)
@@ -305,7 +313,7 @@ var useTableRender = function (params) {
305
313
  // 根据 wrap 属性处理文本
306
314
  if (column.wrap) {
307
315
  // 换行显示(支持\n和自动换行)
308
- var lines = wrapText(ctx, titleText, width - 16);
316
+ var lines = wrapText(ctx, titleText, width - 10); // 左右各5px
309
317
  var lineHeight_3 = 20;
310
318
  var startY_3 = textY - ((lines.length - 1) * lineHeight_3) / 2;
311
319
  lines.forEach(function (line, index) {
@@ -314,7 +322,7 @@ var useTableRender = function (params) {
314
322
  });
315
323
  }
316
324
  else {
317
- var truncated = truncateText(ctx, titleText, width - 16);
325
+ var truncated = truncateText(ctx, titleText, width - 10); // 左右各5px
318
326
  ctx.fillText(truncated, textX_2, textY);
319
327
  }
320
328
  }
@@ -341,20 +349,31 @@ var useTableRender = function (params) {
341
349
  ctx.fillStyle = COLORS.headerBg;
342
350
  ctx.fillRect(0, 0, displayWidth, headerHeight);
343
351
  if (isMultiHeader) {
344
- // 多级表头渲染
345
- var rowHeight_1 = headerHeight / maxDepth;
352
+ // 多级表头渲染,使用计算出的每一层高度
346
353
  // 先绘制非fixed列
347
354
  flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
348
- var y = rowIndex * rowHeight_1;
355
+ var y = layerStartY[rowIndex];
356
+ var currentLayerHeight = layerHeights[rowIndex];
349
357
  headerRow.forEach(function (headerCell) {
350
- drawMultiHeaderCell(ctx, headerCell, rowHeight_1, y, displayWidth, rowIndex, false);
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);
351
364
  });
352
365
  });
353
366
  // 再绘制fixed列(覆盖在非fixed列之上)
354
367
  flattenedHeaderRows.forEach(function (headerRow, rowIndex) {
355
- var y = rowIndex * rowHeight_1;
368
+ var y = layerStartY[rowIndex];
369
+ var currentLayerHeight = layerHeights[rowIndex];
356
370
  headerRow.forEach(function (headerCell) {
357
- drawMultiHeaderCell(ctx, headerCell, rowHeight_1, y, displayWidth, rowIndex, true);
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);
358
377
  });
359
378
  });
360
379
  }
@@ -562,23 +581,23 @@ var useTableRender = function (params) {
562
581
  // 合计行字体加粗
563
582
  ctx.font = record[IS_SUMMARY]
564
583
  ? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
565
- : "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
584
+ : "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
566
585
  ctx.textBaseline = "middle";
567
586
  var align = column.align || "left";
568
- var textX_3 = drawX + 16;
587
+ var textX_3 = drawX + 5;
569
588
  if (align === "center") {
570
589
  ctx.textAlign = "center";
571
590
  textX_3 = drawX + width / 2;
572
591
  }
573
592
  else if (align === "right") {
574
593
  ctx.textAlign = "right";
575
- textX_3 = drawX + width - 16;
594
+ textX_3 = drawX + width - 5;
576
595
  }
577
596
  else {
578
597
  ctx.textAlign = "left";
579
598
  }
580
599
  // 处理文本显示(ellipsis 或 wrap)
581
- var maxWidth = width - 32;
600
+ var maxWidth = width - 10; // 左右各5px
582
601
  var ellipsis = column.ellipsis !== false; // 默认为true
583
602
  var wrap = column.wrap === true; // 默认为false
584
603
  if (wrap) {
@@ -819,7 +838,7 @@ var useTableRender = function (params) {
819
838
  return;
820
839
  var indexText = String(rowIndex + 1);
821
840
  ctx.fillStyle = COLORS.text;
822
- ctx.font = "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
841
+ ctx.font = "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
823
842
  ctx.textBaseline = "middle";
824
843
  setTextAlign(ctx, "center");
825
844
  var textX = drawX + actualWidth / 2;
@@ -863,11 +882,11 @@ var useTableRender = function (params) {
863
882
  ctx.font =
864
883
  rowType === "summary"
865
884
  ? "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY)
866
- : "".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
885
+ : "".concat(FONT_WEIGHT, " ").concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
867
886
  ctx.textBaseline = "middle";
868
887
  var align = column.align || "left";
869
888
  setTextAlign(ctx, align);
870
- var padding = 16;
889
+ var padding = 5;
871
890
  var maxWidth = actualWidth - padding * 2;
872
891
  var textY = fixedY + rowHeight / 2;
873
892
  var textX = calculateTextX(drawX, actualWidth, align, padding);
@@ -1241,8 +1260,9 @@ var useTableRender = function (params) {
1241
1260
  // 计算可视区域(从固定行之后开始)
1242
1261
  var scrollStartRow = fixedTopRowsCount + Math.floor(scrollState.scrollTop / rowHeight);
1243
1262
  var scrollEndRow = Math.min(fixedTopRowsCount +
1244
- Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length - fixedBottomRowsCount
1245
- // 注意:不再减去合计行数量,因为合计行的空间已在 dataAreaHeight 中考虑
1263
+ Math.ceil((scrollState.scrollTop + dataAreaHeight) / rowHeight), processedDataSource.length -
1264
+ fixedBottomRowsCount -
1265
+ (summaryFixed && hasSummaryRow ? 1 : 0) // 如果合计行固定,需要排除它
1246
1266
  );
1247
1267
  // 关键修复:向前扩展渲染范围,检查是否有合并单元格延伸到可视区域
1248
1268
  // 策略:对每一列独立检查,找到该列最靠前的需要渲染的主合并单元格
@@ -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
- color?: string;
187
+ title?: string;
188
188
  /**
189
- * 角标文本
189
+ * 角标颜色
190
190
  */
191
- text?: string;
191
+ color?: string;
192
192
  /**
193
193
  * 角标位置
194
194
  */
@@ -34,9 +34,10 @@ var COLORS = {
34
34
  };
35
35
  // 字体配置
36
36
  var FONT_FAMILY = '"Microsoft YaHei", 微软雅黑, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
37
- var FONT_SIZE = 13; // 增大1px,提升清晰度
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
- var infos = [];
15
- var currentX = 0;
16
- var fixedLeftX = 0;
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
- infos.push({
22
+ tempInfos.push({
21
23
  column: column,
22
- x: isFixed ? fixedLeftX : currentX,
23
24
  width: columnWidth,
24
- fixed: isFixed,
25
- fixedLeft: isFixed ? fixedLeftX : undefined,
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 += columnWidth;
58
+ if (tempInfo.isFixed) {
59
+ fixedLeftX += tempInfo.width;
29
60
  }
30
- currentX += columnWidth;
61
+ currentX += tempInfo.width;
31
62
  });
32
63
  return infos;
33
64
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zmdms-webui",
3
- "version": "2.3.5",
3
+ "version": "2.3.6",
4
4
  "private": false,
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",