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.
- package/dist/es/canvastable/canvasTable.js +25 -3
- package/dist/es/canvastable/hooks/useScroll.js +62 -11
- package/dist/es/canvastable/hooks/useTableInteraction.js +1 -0
- package/dist/es/canvastable/hooks/useTableRender.js +39 -13
- package/dist/es/canvastable/utils/cellHelpers.js +52 -15
- package/dist/es/canvastable/utils/formatHelpers.js +4 -2
- package/dist/es/canvastable/utils/tableCalculations.js +18 -10
- package/package.json +1 -1
|
@@ -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,
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
122
|
-
|
|
121
|
+
deltaX = e.deltaY;
|
|
122
|
+
deltaY = 0;
|
|
123
123
|
}
|
|
124
124
|
else {
|
|
125
125
|
// 正常滚动逻辑
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
136
|
-
var
|
|
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 +
|
|
144
|
-
var newScrollLeft = Math.max(0, Math.min(maxScrollLeft, prev.scrollLeft +
|
|
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 () {
|
|
@@ -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
|
|
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
|
-
|
|
828
|
-
|
|
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 <=
|
|
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
|
-
|
|
908
|
+
fixedTopRowsCount * rowHeight +
|
|
909
|
+
(rowIndex + rowSpan - fixedTopRowsCount) * rowHeight -
|
|
886
910
|
scrollState.scrollTop;
|
|
887
|
-
|
|
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
|
-
|
|
88
|
+
foundCell = { row: rowIndex, col: i };
|
|
89
|
+
break;
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
// 如果不在fixed列上,再查找非fixed列
|
|
92
|
-
|
|
93
|
-
var
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
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
|
|
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
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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 -
|