zmdms-webui 2.4.8 → 2.5.0

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.
@@ -28,7 +28,7 @@ import { useProcessedColumns } from './hooks/useProcessedColumns.js';
28
28
  import { useSummaryRow } from './hooks/useSummaryRow.js';
29
29
  import { useCanvasTableAutoHeight } from './hooks/useAutoHeight.js';
30
30
  import { SCROLLBAR_SIZE } from './utils/constants.js';
31
- import { calculateColumnRenderInfos, calculateTotalWidth, calculateTotalHeight } from './utils/tableCalculations.js';
31
+ import { calculateColumnRenderInfos, calculateTotalHeight, calculateTotalWidth } from './utils/tableCalculations.js';
32
32
  import 'zmdms-utils';
33
33
  import 'dayjs';
34
34
  import { TABLE_DYNAMIC_KEY } from '../table/constant.js';
@@ -122,9 +122,31 @@ function CanvasTable(props) {
122
122
  var containerHeight = containerSize.height;
123
123
  // 计算列的渲染信息(使用容器宽度进行自适应填充)
124
124
  // 手动调整过宽度的列不参与自动宽度分配
125
+ // 注意:需要先预判是否会有垂直滚动条,如果有则预留滚动条宽度
125
126
  var columnRenderInfos = useMemo(function () {
126
- return calculateColumnRenderInfos(processedColumns, containerWidth, manuallyResizedColumnKeys);
127
- }, [processedColumns, containerWidth, manuallyResizedColumnKeys]);
127
+ // 第一步:先用原始列宽计算,判断是否需要垂直滚动条
128
+ calculateColumnRenderInfos(processedColumns, undefined, // 不进行自适应填充
129
+ manuallyResizedColumnKeys);
130
+ // 计算原始总高度
131
+ var tempTotalHeight = calculateTotalHeight(headerHeight, // 使用基础表头高度做粗略判断
132
+ finalDataSource.length, rowHeight);
133
+ // 判断是否需要垂直滚动条
134
+ var needVerticalScrollbar = tempTotalHeight > containerHeight;
135
+ // 第二步:计算实际可用宽度(如果需要垂直滚动条,减去滚动条宽度)
136
+ var availableWidth = needVerticalScrollbar
137
+ ? containerWidth - SCROLLBAR_SIZE
138
+ : containerWidth;
139
+ // 第三步:使用可用宽度进行自适应填充
140
+ return calculateColumnRenderInfos(processedColumns, availableWidth, manuallyResizedColumnKeys);
141
+ }, [
142
+ processedColumns,
143
+ containerWidth,
144
+ containerHeight,
145
+ headerHeight,
146
+ finalDataSource.length,
147
+ rowHeight,
148
+ manuallyResizedColumnKeys,
149
+ ]);
128
150
  // 计算表头动态高度(支持wrap换行)
129
151
  // 注意:必须在 columnRenderInfos 计算之后,以便使用实际的列宽
130
152
  var calculatedHeaderHeight = useHeaderHeight({
@@ -111,37 +111,88 @@ var useTableScroll = function (params) {
111
111
  // 允许过滤弹框内部滚动
112
112
  return;
113
113
  }
114
- e.preventDefault();
115
- e.stopPropagation();
116
114
  // 当鼠标在容器内任何位置(包括CellOverlay)时都能滚动
117
115
  // 累积滚动增量
118
116
  // 支持 Shift + 鼠标滚轮进行水平滚动
117
+ var deltaX = 0;
118
+ var deltaY = 0;
119
119
  if (e.shiftKey) {
120
120
  // 按住 Shift 键时,将垂直滚动转换为水平滚动
121
- pendingDeltaRef.current.deltaX += e.deltaY;
122
- pendingDeltaRef.current.deltaY += 0;
121
+ deltaX = e.deltaY;
122
+ deltaY = 0;
123
123
  }
124
124
  else {
125
125
  // 正常滚动逻辑
126
- pendingDeltaRef.current.deltaX += e.deltaX;
127
- pendingDeltaRef.current.deltaY += e.deltaY;
126
+ deltaX = e.deltaX;
127
+ deltaY = e.deltaY;
128
+ }
129
+ // 检查是否需要支持父元素滚动
130
+ // 1. 如果没有滚动条(maxScrollTop 和 maxScrollLeft 都为 0)
131
+ // 2. 或者已经滚动到底部且继续向下滚动
132
+ // 3. 或者已经滚动到顶部且继续向上滚动
133
+ var hasNoVerticalScrollbar = maxScrollTop <= 0;
134
+ var hasNoHorizontalScrollbar = maxScrollLeft <= 0;
135
+ var isAtBottom = Math.abs(scrollState.scrollTop - maxScrollTop) < 1;
136
+ var isAtTop = scrollState.scrollTop <= 0;
137
+ var isAtRight = Math.abs(scrollState.scrollLeft - maxScrollLeft) < 1;
138
+ var isAtLeft = scrollState.scrollLeft <= 0;
139
+ var shouldAllowParentScroll = false;
140
+ // 垂直滚动检查
141
+ if (deltaY !== 0) {
142
+ if (hasNoVerticalScrollbar) {
143
+ // 没有垂直滚动条,允许父元素滚动
144
+ shouldAllowParentScroll = true;
145
+ }
146
+ else if (deltaY > 0 && isAtBottom) {
147
+ // 向下滚动且已在底部,允许父元素滚动
148
+ shouldAllowParentScroll = true;
149
+ }
150
+ else if (deltaY < 0 && isAtTop) {
151
+ // 向上滚动且已在顶部,允许父元素滚动
152
+ shouldAllowParentScroll = true;
153
+ }
128
154
  }
155
+ // 水平滚动检查
156
+ if (deltaX !== 0) {
157
+ if (hasNoHorizontalScrollbar) {
158
+ // 没有水平滚动条,允许父元素滚动
159
+ shouldAllowParentScroll = true;
160
+ }
161
+ else if (deltaX > 0 && isAtRight) {
162
+ // 向右滚动且已在右边界,允许父元素滚动
163
+ shouldAllowParentScroll = true;
164
+ }
165
+ else if (deltaX < 0 && isAtLeft) {
166
+ // 向左滚动且已在左边界,允许父元素滚动
167
+ shouldAllowParentScroll = true;
168
+ }
169
+ }
170
+ // 如果需要支持父元素滚动,不阻止默认行为
171
+ if (shouldAllowParentScroll) {
172
+ return;
173
+ }
174
+ // 阻止默认行为和事件冒泡
175
+ e.preventDefault();
176
+ e.stopPropagation();
177
+ // 累积滚动增量
178
+ pendingDeltaRef.current.deltaX += deltaX;
179
+ pendingDeltaRef.current.deltaY += deltaY;
129
180
  // 如果已经有pending的动画帧,不需要再次请求
130
181
  if (rafIdRef.current !== null) {
131
182
  return;
132
183
  }
133
184
  // 使用 requestAnimationFrame 节流更新
134
185
  rafIdRef.current = requestAnimationFrame(function () {
135
- var deltaX = pendingDeltaRef.current.deltaX;
136
- var deltaY = pendingDeltaRef.current.deltaY;
186
+ var accumulatedDeltaX = pendingDeltaRef.current.deltaX;
187
+ var accumulatedDeltaY = pendingDeltaRef.current.deltaY;
137
188
  // 重置累积的增量
138
189
  pendingDeltaRef.current.deltaX = 0;
139
190
  pendingDeltaRef.current.deltaY = 0;
140
191
  rafIdRef.current = null;
141
192
  // 批量更新状态
142
193
  setScrollState(function (prev) {
143
- var newScrollTop = Math.max(0, Math.min(maxScrollTop, prev.scrollTop + deltaY));
144
- var newScrollLeft = Math.max(0, Math.min(maxScrollLeft, prev.scrollLeft + deltaX));
194
+ var newScrollTop = Math.max(0, Math.min(maxScrollTop, prev.scrollTop + accumulatedDeltaY));
195
+ var newScrollLeft = Math.max(0, Math.min(maxScrollLeft, prev.scrollLeft + accumulatedDeltaX));
145
196
  if (newScrollTop !== prev.scrollTop ||
146
197
  newScrollLeft !== prev.scrollLeft) {
147
198
  onScroll === null || onScroll === void 0 ? void 0 : onScroll(newScrollLeft, newScrollTop);
@@ -162,7 +213,7 @@ var useTableScroll = function (params) {
162
213
  rafIdRef.current = null;
163
214
  }
164
215
  };
165
- }, [containerRef, maxScrollTop, maxScrollLeft, onScroll, setScrollState]);
216
+ }, [containerRef, maxScrollTop, maxScrollLeft, onScroll, setScrollState, scrollState.scrollTop, scrollState.scrollLeft]);
166
217
  // 全局鼠标事件处理(拖拽滚动条)
167
218
  useEffect(function () {
168
219
  var handleGlobalMouseUp = function () {
@@ -35,6 +35,7 @@ var useTableInteraction = function (params) {
35
35
  displayHeight: containerHeight,
36
36
  needHorizontalScrollbar: needHorizontalScrollbar,
37
37
  scrollbarSize: scrollbarSize,
38
+ mergeCellMap: mergeCellMap,
38
39
  });
39
40
  });
40
41
  // 处理选择框列的全选点击
@@ -621,7 +621,7 @@ var useTableRender = function (params) {
621
621
  // 合计行选择框列显示文本(比如"合计")
622
622
  var dataIndex = column.dataIndex || column.key;
623
623
  var cellValue = record[dataIndex];
624
- var cellText = formatCellValue(cellValue, column);
624
+ var cellText = formatCellValue(cellValue, column, true);
625
625
  if (cellText) {
626
626
  ctx.fillStyle = COLORS.text;
627
627
  ctx.font = "bold ".concat(FONT_SIZE, "px ").concat(FONT_FAMILY);
@@ -656,7 +656,7 @@ var useTableRender = function (params) {
656
656
  // 应用格式化(日期、精度、千分符)
657
657
  var cellText = void 0;
658
658
  // 对于合计行,直接使用格式化函数,不应用render
659
- if (isSummaryRow) {
659
+ if (isSummaryRow && column.key !== "__index__") {
660
660
  cellText = formatCellValue(cellValue, column);
661
661
  }
662
662
  else {
@@ -800,8 +800,24 @@ var useTableRender = function (params) {
800
800
  ctx.stroke();
801
801
  ctx.lineWidth = 1; // 恢复为1px
802
802
  }
803
- // 计算实际数据区域的底部位置
804
- var dataBottomY = Math.min(headerHeight + dataRowCount * rowHeight - scrollState.scrollTop, displayHeight);
803
+ // 计算固定行占用的高度
804
+ var fixedTopRowsCount = fixedRowsCount || (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.topCount) || 0;
805
+ var fixedBottomRowsCount = (fixedRowsConfig === null || fixedRowsConfig === void 0 ? void 0 : fixedRowsConfig.bottomCount) || 0;
806
+ var fixedSummaryHeight = summaryFixed && hasSummaryRow ? rowHeight : 0;
807
+ var fixedBottomHeight = fixedBottomRowsCount * rowHeight;
808
+ // 计算可滚动数据行的数量(排除固定行和合计行)
809
+ var scrollableDataRowCount = dataRowCount -
810
+ fixedTopRowsCount -
811
+ fixedBottomRowsCount -
812
+ (summaryFixed && hasSummaryRow ? 1 : 0);
813
+ // 计算实际数据区域的底部位置(考虑固定底部行和固定合计行)
814
+ var dataBottomY = Math.min(headerHeight +
815
+ fixedTopRowsCount * rowHeight +
816
+ scrollableDataRowCount * rowHeight -
817
+ scrollState.scrollTop, displayHeight -
818
+ (needHorizontalScrollbar ? SCROLLBAR_SIZE : 0) -
819
+ fixedBottomHeight -
820
+ fixedSummaryHeight);
805
821
  // 绘制垂直边框(只绘制body部分,从headerHeight开始,表头边框在drawHeaderCell中绘制)
806
822
  columnRenderInfos.forEach(function (info, colIndex) {
807
823
  var x = info.x, width = info.width, fixed = info.fixed, fixedLeft = info.fixedLeft;
@@ -823,9 +839,17 @@ var useTableRender = function (params) {
823
839
  ctx.stroke();
824
840
  }
825
841
  });
842
+ // 绘制水平边框(只绘制实际数据行的边框)
843
+ var maxRow = Math.min(endRow, dataRowCount, fixedTopRowsCount + scrollableDataRowCount);
826
844
  var _loop_1 = function (i) {
827
- var y = headerHeight + i * rowHeight - scrollState.scrollTop;
828
- if (y >= headerHeight && y <= displayHeight) {
845
+ // 计算Y坐标时需要考虑固定顶部行
846
+ var y = headerHeight +
847
+ fixedTopRowsCount * rowHeight +
848
+ (i - fixedTopRowsCount) * rowHeight -
849
+ scrollState.scrollTop;
850
+ // 只绘制在可滚动数据区域内的边框
851
+ if (y >= headerHeight + fixedTopRowsCount * rowHeight &&
852
+ y <= dataBottomY) {
829
853
  // 分段绘制,只绘制当前范围(fixed或非fixed)的边框
830
854
  columnRenderInfos.forEach(function (info, colIndex) {
831
855
  // 根据 onlyFixed 参数过滤
@@ -856,8 +880,7 @@ var useTableRender = function (params) {
856
880
  });
857
881
  }
858
882
  };
859
- // 绘制水平边框(只绘制实际数据行的边框)
860
- for (var i = startRow; i <= endRow && i <= dataRowCount; i++) {
883
+ for (var i = startRow; i <= maxRow; i++) {
861
884
  _loop_1(i);
862
885
  }
863
886
  // 为合并单元格绘制底部边框
@@ -865,8 +888,8 @@ var useTableRender = function (params) {
865
888
  mergeCellMap.forEach(function (mergeInfo, key) {
866
889
  if (!mergeInfo.skip && mergeInfo.rowSpan > 1) {
867
890
  var rowIndex = mergeInfo.rowIndex, colIndex = mergeInfo.colIndex, rowSpan = mergeInfo.rowSpan;
868
- // 只绘制在可视范围内的
869
- if (rowIndex >= startRow && rowIndex <= endRow) {
891
+ // 只绘制在可视范围内的且在数据区域内的
892
+ if (rowIndex >= startRow && rowIndex <= maxRow) {
870
893
  var columnInfo = columnRenderInfos[colIndex];
871
894
  if (!columnInfo)
872
895
  return;
@@ -880,11 +903,14 @@ var useTableRender = function (params) {
880
903
  ? getColumnWidth(colIndex)
881
904
  : width;
882
905
  var drawX = fixed ? fixedLeft : x - scrollState.scrollLeft;
883
- // 绘制合并单元格的底边框
906
+ // 绘制合并单元格的底边框,考虑固定顶部行
884
907
  var bottomY = headerHeight +
885
- (rowIndex + rowSpan) * rowHeight -
908
+ fixedTopRowsCount * rowHeight +
909
+ (rowIndex + rowSpan - fixedTopRowsCount) * rowHeight -
886
910
  scrollState.scrollTop;
887
- if (bottomY >= headerHeight && bottomY <= displayHeight) {
911
+ // 只在数据区域内绘制
912
+ if (bottomY >= headerHeight + fixedTopRowsCount * rowHeight &&
913
+ bottomY <= dataBottomY) {
888
914
  ctx.beginPath();
889
915
  ctx.moveTo(drawX, bottomY);
890
916
  ctx.lineTo(drawX + actualWidth, bottomY);
@@ -5,7 +5,7 @@
5
5
  * 获取鼠标位置对应的单元格
6
6
  */
7
7
  var getCellFromMousePosition = function (params) {
8
- var x = params.x, y = params.y, headerHeight = params.headerHeight, rowHeight = params.rowHeight, scrollTop = params.scrollTop, scrollLeft = params.scrollLeft, columnRenderInfos = params.columnRenderInfos, dataLength = params.dataLength, _a = params.fixedTopRowsCount, fixedTopRowsCount = _a === void 0 ? 0 : _a, _b = params.fixedBottomRowsCount, fixedBottomRowsCount = _b === void 0 ? 0 : _b, _c = params.hasSummaryRow, hasSummaryRow = _c === void 0 ? false : _c, _d = params.summaryFixed, summaryFixed = _d === void 0 ? false : _d, _e = params.displayHeight, displayHeight = _e === void 0 ? 0 : _e, _f = params.needHorizontalScrollbar, needHorizontalScrollbar = _f === void 0 ? false : _f, _g = params.scrollbarSize, scrollbarSize = _g === void 0 ? 12 : _g;
8
+ var x = params.x, y = params.y, headerHeight = params.headerHeight, rowHeight = params.rowHeight, scrollTop = params.scrollTop, scrollLeft = params.scrollLeft, columnRenderInfos = params.columnRenderInfos, dataLength = params.dataLength, _a = params.fixedTopRowsCount, fixedTopRowsCount = _a === void 0 ? 0 : _a, _b = params.fixedBottomRowsCount, fixedBottomRowsCount = _b === void 0 ? 0 : _b, _c = params.hasSummaryRow, hasSummaryRow = _c === void 0 ? false : _c, _d = params.summaryFixed, summaryFixed = _d === void 0 ? false : _d, _e = params.displayHeight, displayHeight = _e === void 0 ? 0 : _e, _f = params.needHorizontalScrollbar, needHorizontalScrollbar = _f === void 0 ? false : _f, _g = params.scrollbarSize, scrollbarSize = _g === void 0 ? 12 : _g, mergeCellMap = params.mergeCellMap;
9
9
  // 判断是否在表头区域(row=-1表示表头)
10
10
  var rowIndex;
11
11
  if (y < headerHeight) {
@@ -20,7 +20,7 @@ var getCellFromMousePosition = function (params) {
20
20
  rowIndex = Math.floor(relativeY / rowHeight);
21
21
  if (rowIndex < 0 || rowIndex >= fixedTopRowsCount)
22
22
  return null;
23
- return findColumn(x, scrollLeft, columnRenderInfos, rowIndex);
23
+ return findColumn(x, scrollLeft, columnRenderInfos, rowIndex, mergeCellMap);
24
24
  }
25
25
  // 检查是否在固定合计行区域
26
26
  if (summaryFixed && hasSummaryRow && displayHeight > 0) {
@@ -30,7 +30,7 @@ var getCellFromMousePosition = function (params) {
30
30
  if (y >= summaryRowY) {
31
31
  // 在固定合计行区域
32
32
  rowIndex = dataLength - 1; // 合计行是最后一行
33
- return findColumn(x, scrollLeft, columnRenderInfos, rowIndex);
33
+ return findColumn(x, scrollLeft, columnRenderInfos, rowIndex, mergeCellMap);
34
34
  }
35
35
  }
36
36
  // 检查是否在固定底部行区域
@@ -54,7 +54,7 @@ var getCellFromMousePosition = function (params) {
54
54
  rowIndex = bottomRowsStartIndex + relativeIndex;
55
55
  if (rowIndex < 0 || rowIndex >= dataLength)
56
56
  return null;
57
- return findColumn(x, scrollLeft, columnRenderInfos, rowIndex);
57
+ return findColumn(x, scrollLeft, columnRenderInfos, rowIndex, mergeCellMap);
58
58
  }
59
59
  }
60
60
  // 在可滚动区域
@@ -71,12 +71,13 @@ var getCellFromMousePosition = function (params) {
71
71
  return null;
72
72
  }
73
73
  }
74
- return findColumn(x, scrollLeft, columnRenderInfos, rowIndex);
74
+ return findColumn(x, scrollLeft, columnRenderInfos, rowIndex, mergeCellMap);
75
75
  };
76
76
  /**
77
77
  * 根据x坐标查找列索引
78
78
  */
79
- function findColumn(x, scrollLeft, columnRenderInfos, rowIndex) {
79
+ function findColumn(x, scrollLeft, columnRenderInfos, rowIndex, mergeCellMap) {
80
+ var foundCell = null;
80
81
  // 优先查找fixed列(因为fixed列在上层,会遮挡下层的非fixed列)
81
82
  for (var i = 0; i < columnRenderInfos.length; i++) {
82
83
  var info = columnRenderInfos[i];
@@ -84,22 +85,58 @@ function findColumn(x, scrollLeft, columnRenderInfos, rowIndex) {
84
85
  var fixedLeft = info.fixedLeft, width = info.width;
85
86
  var drawX = fixedLeft;
86
87
  if (x >= drawX && x < drawX + width) {
87
- return { row: rowIndex, col: i };
88
+ foundCell = { row: rowIndex, col: i };
89
+ break;
88
90
  }
89
91
  }
90
92
  }
91
93
  // 如果不在fixed列上,再查找非fixed列
92
- for (var i = 0; i < columnRenderInfos.length; i++) {
93
- var info = columnRenderInfos[i];
94
- if (!info.fixed) {
95
- var width = info.width;
96
- var drawX = info.x - scrollLeft;
97
- if (x >= drawX && x < drawX + width) {
98
- return { row: rowIndex, col: i };
94
+ if (!foundCell) {
95
+ for (var i = 0; i < columnRenderInfos.length; i++) {
96
+ var info = columnRenderInfos[i];
97
+ if (!info.fixed) {
98
+ var width = info.width;
99
+ var drawX = info.x - scrollLeft;
100
+ if (x >= drawX && x < drawX + width) {
101
+ foundCell = { row: rowIndex, col: i };
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ // 如果找到了单元格,检查它是否是被合并的单元格
108
+ if (foundCell && mergeCellMap) {
109
+ var mergeCellKey = "".concat(foundCell.row, "-").concat(foundCell.col);
110
+ var mergeInfo = mergeCellMap.get(mergeCellKey);
111
+ // 如果是被合并的单元格(skip=true),需要找到主单元格
112
+ if (mergeInfo && mergeInfo.skip) {
113
+ // 遍历所有可能的主单元格位置(从左上角开始,到当前位置)
114
+ // 找到覆盖当前位置的主单元格
115
+ for (var checkRow = 0; checkRow <= foundCell.row; checkRow++) {
116
+ for (var checkCol = 0; checkCol <= foundCell.col; checkCol++) {
117
+ var mainCellKey = "".concat(checkRow, "-").concat(checkCol);
118
+ var mainMergeInfo = mergeCellMap.get(mainCellKey);
119
+ // 找到主单元格(skip=false且有合并)
120
+ if (mainMergeInfo &&
121
+ !mainMergeInfo.skip &&
122
+ (mainMergeInfo.rowSpan > 1 || mainMergeInfo.colSpan > 1)) {
123
+ // 计算主单元格覆盖的范围
124
+ var mainCellEndRow = checkRow + mainMergeInfo.rowSpan - 1;
125
+ var mainCellEndCol = checkCol + mainMergeInfo.colSpan - 1;
126
+ // 检查当前位置是否在主单元格的覆盖范围内
127
+ if (foundCell.row >= checkRow &&
128
+ foundCell.row <= mainCellEndRow &&
129
+ foundCell.col >= checkCol &&
130
+ foundCell.col <= mainCellEndCol) {
131
+ // 返回主单元格的坐标
132
+ return { row: checkRow, col: checkCol };
133
+ }
134
+ }
135
+ }
99
136
  }
100
137
  }
101
138
  }
102
- return null;
139
+ return foundCell;
103
140
  }
104
141
  /**
105
142
  * 提取单元格文本(处理 render 函数)
@@ -17,9 +17,11 @@ var formatDate = function (value, format) {
17
17
  /**
18
18
  * 格式化单元格值
19
19
  */
20
- var formatCellValue = function (value, column) {
20
+ var formatCellValue = function (value, column, noEmptyText) {
21
21
  // 空值处理:null、undefined、空字符串
22
22
  if (value === null || value === undefined || value === "") {
23
+ if (noEmptyText)
24
+ return "";
23
25
  return column.emptyText !== undefined ? column.emptyText : "— —";
24
26
  }
25
27
  var formattedValue = value;
@@ -28,7 +30,7 @@ var formatCellValue = function (value, column) {
28
30
  formattedValue = formatDate(formattedValue, column.dateFormat);
29
31
  }
30
32
  // 数值精度
31
- if (column.precision !== undefined && typeof formattedValue === "number") {
33
+ if (column.precision !== undefined) {
32
34
  formattedValue = exactRound(formattedValue, column.precision);
33
35
  }
34
36
  // 千分符
@@ -88,18 +88,26 @@ var calculateTotalHeight = function (headerHeight, dataLength, rowHeight) {
88
88
  */
89
89
  var calculateScrollbarMetrics = function (params) {
90
90
  var containerWidth = params.containerWidth, containerHeight = params.containerHeight, totalWidth = params.totalWidth, totalHeight = params.totalHeight, headerHeight = params.headerHeight, scrollbarSize = params.scrollbarSize, minScrollbarSize = params.minScrollbarSize, scrollTop = params.scrollTop, scrollLeft = params.scrollLeft;
91
- // 是否需要显示滚动条
91
+ // 计算实际可用的内容区域尺寸
92
+ // 注意:需要先确定是否需要滚动条,这里采用两次判断的方式
93
+ // 第一次判断:基于原始容器尺寸
92
94
  var needVerticalScrollbar = totalHeight > containerHeight;
93
95
  var needHorizontalScrollbar = totalWidth > containerWidth;
94
- // 计算可滚动的最大值
95
- // maxScrollTop 需要考虑水平滚动条占用的空间
96
- // 当有水平滚动条时,数据可视区域减少了 scrollbarSize 的高度
97
- var maxScrollTop = Math.max(0, totalHeight -
98
- containerHeight +
99
- (needHorizontalScrollbar ? scrollbarSize : 0));
100
- // maxScrollLeft 需要考虑垂直滚动条占用的空间
101
- // 当有垂直滚动条时,数据可视区域减少了 scrollbarSize 的宽度
102
- var maxScrollLeft = Math.max(0, totalWidth - containerWidth + (needVerticalScrollbar ? scrollbarSize : 0));
96
+ // 第二次判断:考虑滚动条互相影响
97
+ // 如果有垂直滚动条,可用宽度会减少,可能导致需要水平滚动条
98
+ if (needVerticalScrollbar && !needHorizontalScrollbar) {
99
+ needHorizontalScrollbar = totalWidth > containerWidth - scrollbarSize;
100
+ }
101
+ // 如果有水平滚动条,可用高度会减少,可能导致需要垂直滚动条
102
+ if (needHorizontalScrollbar && !needVerticalScrollbar) {
103
+ needVerticalScrollbar = totalHeight > containerHeight - scrollbarSize;
104
+ }
105
+ // 计算实际可用的内容区域尺寸(减去滚动条占用的空间)
106
+ var availableWidth = containerWidth - (needVerticalScrollbar ? scrollbarSize : 0);
107
+ var availableHeight = containerHeight - (needHorizontalScrollbar ? scrollbarSize : 0);
108
+ // 计算可滚动的最大值(基于实际可用空间)
109
+ var maxScrollTop = Math.max(0, totalHeight - availableHeight);
110
+ var maxScrollLeft = Math.max(0, totalWidth - availableWidth);
103
111
  // 计算数据区域高度(不包括表头和水平滚动条)
104
112
  var dataAreaHeight = containerHeight -
105
113
  headerHeight -
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zmdms-webui",
3
- "version": "2.4.8",
3
+ "version": "2.5.0",
4
4
  "private": false,
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",