zmdms-webui 2.4.1 → 2.4.3

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.
@@ -39,7 +39,8 @@ 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, headerWrap = props.headerWrap, _u = props.headerAlign, headerAlign = _u === void 0 ? "center" : _u, tableRefHandle = props.tableRefHandle, exportExcelConfig = props.exportExcelConfig;
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, mode = props.mode, dynamicKey = props.dynamicKey, dynamicVersion = props.dynamicVersion, customDynamicListHandle = props.customDynamicListHandle, isDimensionDynamic = props.isDimensionDynamic, dimensionCustomSumKeys = props.dimensionCustomSumKeys, isAutoMerge = props.isAutoMerge, _m = props.isIndexMerge, isIndexMerge = _m === void 0 ? true : _m, _o = props.isSelectionMerge, isSelectionMerge = _o === void 0 ? false : _o, _p = props.renderMode, renderMode = _p === void 0 ? "object" : _p, fixedRowsCount = props.fixedRowsCount, fixedRowsConfig = props.fixedRowsConfig, _q = props.summaryFixed, summaryFixed = _q === void 0 ? false : _q, _r = props.isAutoScrollY, isAutoScrollY = _r === void 0 ? false : _r, _s = props.autoScrollYMarginBottom, autoScrollYMarginBottom = _s === void 0 ? 65 : _s, canvasTableId = props.canvasTableId, headerWrap = props.headerWrap, _t = props.headerAlign, headerAlign = _t === void 0 ? "center" : _t, tableRefHandle = props.tableRefHandle, exportExcelConfig = props.exportExcelConfig;
43
+ var actualMode = "mode" in props ? mode : "index";
43
44
  var canvasRef = useRef(null);
44
45
  var containerRef = useRef(null);
45
46
  var filterPopoverRef = useRef(null);
@@ -61,22 +62,22 @@ function CanvasTable(props) {
61
62
  return String((_a = record[rowKey]) !== null && _a !== void 0 ? _a : index);
62
63
  }, [rowKey]);
63
64
  // 动态列配置
64
- var _v = useDynamicListByColumns(columns, {
65
+ var _u = useDynamicListByColumns(columns, {
65
66
  dynamicKey: dynamicKey,
66
67
  dynamicVersion: dynamicVersion,
67
68
  customDynamicListHandle: customDynamicListHandle,
68
- }), defaultDynamicList = _v.defaultDynamicList, currentDynamicList = _v.currentDynamicList, onCurrentListChange = _v.onCurrentListChange, dynamicSettingRef = _v.dynamicSettingRef;
69
+ }), defaultDynamicList = _u.defaultDynamicList, currentDynamicList = _u.currentDynamicList, onCurrentListChange = _u.onCurrentListChange, dynamicSettingRef = _u.dynamicSettingRef;
69
70
  // 根据动态列配置处理columns
70
71
  var dynamicColumns = useMemo(function () { return getTableColumns(columns, currentDynamicList); }, [columns, currentDynamicList]).columns;
71
72
  // 内部列宽状态(未开启动态配置时使用)
72
- var _w = useState({}), internalColumnWidths = _w[0], setInternalColumnWidths = _w[1];
73
+ var _v = useState({}), internalColumnWidths = _v[0], setInternalColumnWidths = _v[1];
73
74
  // 跟踪用户手动调整过宽度的列(用于排除自动宽度分配)
74
- var _x = useState(new Set()), manuallyResizedColumnKeys = _x[0], setManuallyResizedColumnKeys = _x[1];
75
+ var _w = useState(new Set()), manuallyResizedColumnKeys = _w[0], setManuallyResizedColumnKeys = _w[1];
75
76
  // 处理选中框列、序号列等
76
77
  var processedColumnsOld = useProcessedColumns({
77
78
  columns: columns,
78
79
  dynamicColumns: dynamicColumns,
79
- mode: mode,
80
+ mode: actualMode,
80
81
  rowSelection: rowSelection,
81
82
  internalColumnWidths: internalColumnWidths,
82
83
  dynamicKey: dynamicKey,
@@ -86,29 +87,29 @@ function CanvasTable(props) {
86
87
  headerAlign: headerAlign,
87
88
  });
88
89
  // 表格状态管理
89
- var _y = useTableState({
90
+ var _x = useTableState({
90
91
  dataSource: dataSource,
91
92
  columns: processedColumnsOld,
92
93
  rowSelection: rowSelection,
93
94
  onFilterChange: onFilterChange,
94
95
  isAutoMerge: isAutoMerge,
95
- }), state = _y.state, setState = _y.setState, processedDataSource = _y.processedDataSource, handleFilterChange = _y.handleFilterChange, closeFilterPopover = _y.closeFilterPopover, autoGeneratedFilters = _y.autoGeneratedFilters;
96
+ }), state = _x.state, setState = _x.setState, processedDataSource = _x.processedDataSource, handleFilterChange = _x.handleFilterChange, closeFilterPopover = _x.closeFilterPopover, autoGeneratedFilters = _x.autoGeneratedFilters;
96
97
  var order = useMemo(function () {
97
98
  return state.sortOrder && state.sortField
98
99
  ? { field: state.sortField, order: state.sortOrder }
99
100
  : undefined;
100
101
  }, [state.sortOrder, state.sortField]);
101
- var _z = useAutoMerge(processedDataSource, processedColumnsOld, {
102
+ var _y = useAutoMerge(processedDataSource, processedColumnsOld, {
102
103
  isAutoMerge: isAutoMerge,
103
104
  isDimensionDynamic: isDimensionDynamic,
104
105
  order: order,
105
106
  dimensionCustomSumKeys: dimensionCustomSumKeys,
106
- }), newDataSource = _z[0], processedColumns = _z[1];
107
+ }), newDataSource = _y[0], processedColumns = _y[1];
107
108
  // 生成合计行和最终数据源
108
- var _0 = useSummaryRow({
109
+ var _z = useSummaryRow({
109
110
  columns: processedColumns,
110
111
  dataSource: newDataSource,
111
- }), finalDataSource = _0.finalDataSource, hasSummaryRow = _0.hasSummaryRow;
112
+ }), finalDataSource = _z.finalDataSource, hasSummaryRow = _z.hasSummaryRow;
112
113
  // 自动高度计算(需要在容器尺寸计算之前)
113
114
  var autoHeight = useCanvasTableAutoHeight(isAutoScrollY, autoScrollYMarginBottom, canvasTableId);
114
115
  // 监听容器尺寸变化(提前计算,用于列宽自适应)
@@ -144,7 +145,7 @@ function CanvasTable(props) {
144
145
  return calculateTotalHeight(calculatedHeaderHeight, finalDataSource.length, rowHeight);
145
146
  }, [calculatedHeaderHeight, finalDataSource.length, rowHeight]);
146
147
  // 计算滚动条指标
147
- var _1 = useScrollbarMetrics({
148
+ var _0 = useScrollbarMetrics({
148
149
  containerWidth: containerWidth,
149
150
  containerHeight: containerHeight,
150
151
  totalWidth: totalWidth,
@@ -152,14 +153,14 @@ function CanvasTable(props) {
152
153
  headerHeight: calculatedHeaderHeight,
153
154
  scrollTop: 0,
154
155
  scrollLeft: 0,
155
- }), maxScrollTop = _1.maxScrollTop, maxScrollLeft = _1.maxScrollLeft;
156
+ }), maxScrollTop = _0.maxScrollTop, maxScrollLeft = _0.maxScrollLeft;
156
157
  // 滚动管理(使用预先计算的maxScrollTop和maxScrollLeft)
157
- var _2 = useTableScroll({
158
+ var _1 = useTableScroll({
158
159
  containerRef: containerRef,
159
160
  maxScrollTop: maxScrollTop,
160
161
  maxScrollLeft: maxScrollLeft,
161
162
  onScroll: onScroll,
162
- }), scrollState = _2.scrollState, setScrollState = _2.setScrollState;
163
+ }), scrollState = _1.scrollState, setScrollState = _1.setScrollState;
163
164
  // 数据变化时重置滚动位置(筛选、排序、数据源更新等操作后)
164
165
  useScrollReset({
165
166
  dataSourceLength: dataSource.length,
@@ -180,12 +181,12 @@ function CanvasTable(props) {
180
181
  scrollLeft: scrollState.scrollLeft,
181
182
  }).actualMetrics;
182
183
  // 单元格框选
183
- var _3 = useTableSelection({
184
+ var _2 = useTableSelection({
184
185
  state: state,
185
186
  setState: setState,
186
187
  }),
187
188
  // selectionStartRef,
188
- startSelection = _3.startSelection, updateSelection = _3.updateSelection, extendSelection = _3.extendSelection;
189
+ startSelection = _2.startSelection, updateSelection = _2.updateSelection, extendSelection = _2.extendSelection;
189
190
  // 处理列宽调整
190
191
  var handleColumnResize = useCallback(function (columnKey, newWidth) {
191
192
  var _a;
@@ -240,13 +241,13 @@ function CanvasTable(props) {
240
241
  onCurrentListChange,
241
242
  ]);
242
243
  // 列宽调整
243
- var _4 = useColumnResize({
244
+ var _3 = useColumnResize({
244
245
  columnRenderInfos: columnRenderInfos,
245
246
  containerWidth: containerWidth,
246
247
  headerHeight: calculatedHeaderHeight,
247
248
  scrollLeft: scrollState.scrollLeft,
248
249
  onColumnResize: handleColumnResize,
249
- }), resizeState = _4.resizeState, checkResizeHandle = _4.checkResizeHandle, startResize = _4.startResize, updateResize = _4.updateResize, endResize = _4.endResize, setHoverResizeColumn = _4.setHoverResizeColumn, getColumnWidth = _4.getColumnWidth, RESIZE_HANDLE_WIDTH = _4.RESIZE_HANDLE_WIDTH;
250
+ }), resizeState = _3.resizeState, checkResizeHandle = _3.checkResizeHandle, startResize = _3.startResize, updateResize = _3.updateResize, endResize = _3.endResize, setHoverResizeColumn = _3.setHoverResizeColumn, getColumnWidth = _3.getColumnWidth, RESIZE_HANDLE_WIDTH = _3.RESIZE_HANDLE_WIDTH;
250
251
  // 复制到剪贴板
251
252
  var getSelectedCellsText = useCopyToClipboard({
252
253
  cellSelection: state.cellSelection,
@@ -274,7 +275,7 @@ function CanvasTable(props) {
274
275
  summaryConfig: [],
275
276
  });
276
277
  // 交互事件处理(使用baseScrollbarMetrics的maxScrollTop/maxScrollLeft以保持稳定)
277
- var _5 = useTableInteraction({
278
+ var _4 = useTableInteraction({
278
279
  state: state,
279
280
  setState: setState,
280
281
  scrollState: scrollState,
@@ -321,7 +322,7 @@ function CanvasTable(props) {
321
322
  fixedRowsCount: fixedRowsCount,
322
323
  fixedRowsConfig: fixedRowsConfig,
323
324
  summaryFixed: summaryFixed,
324
- }), handleCanvasMouseDown = _5.handleCanvasMouseDown, handleCanvasMouseMove = _5.handleCanvasMouseMove, handleCanvasMouseUp = _5.handleCanvasMouseUp, handleCanvasMouseLeave = _5.handleCanvasMouseLeave, handleCanvasContextMenu = _5.handleCanvasContextMenu;
325
+ }), handleCanvasMouseDown = _4.handleCanvasMouseDown, handleCanvasMouseMove = _4.handleCanvasMouseMove, handleCanvasMouseUp = _4.handleCanvasMouseUp, handleCanvasMouseLeave = _4.handleCanvasMouseLeave, handleCanvasContextMenu = _4.handleCanvasContextMenu;
325
326
  // 渲染表格
326
327
  useTableRender(__assign(__assign({ canvasRef: canvasRef, processedDataSource: finalDataSource, columnRenderInfos: columnRenderInfos, columns: processedColumns, // 传递原始columns用于渲染多级表头
327
328
  state: state, scrollState: scrollState, rowSelection: rowSelection, containerWidth: containerWidth, containerHeight: containerHeight, headerHeight: calculatedHeaderHeight, baseHeaderHeight: headerHeight, // 传入原始基础高度用于计算每层高度
@@ -1,5 +1,5 @@
1
1
  import { __assign } from '../../_virtual/_tslib.js';
2
- import { useMemo, useState, useRef, useEffect } from 'react';
2
+ import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { SCROLLBAR_SIZE, MIN_SCROLLBAR_SIZE } from '../utils/constants.js';
4
4
  import { calculateScrollbarMetrics } from '../utils/tableCalculations.js';
5
5
  import 'zmdms-utils';
@@ -191,103 +191,211 @@ var useSelectionBoundaryScroll = function (params) {
191
191
  var containerRef = params.containerRef, isSelecting = params.isSelecting,
192
192
  // scrollState,
193
193
  setScrollState = params.setScrollState, maxScrollTop = params.maxScrollTop, maxScrollLeft = params.maxScrollLeft, onScroll = params.onScroll, getCellFromPosition = params.getCellFromPosition, updateSelection = params.updateSelection, columnRenderInfos = params.columnRenderInfos;
194
- var scrollIntervalRef = useRef(null);
194
+ var scrollRafRef = useRef(null);
195
195
  var currentMousePosRef = useRef(null);
196
+ var lastCellRef = useRef(null);
197
+ var scrollVelocityRef = useRef({
198
+ deltaX: 0,
199
+ deltaY: 0,
200
+ });
201
+ // 状态更新去抖:避免频繁的状态更新
202
+ var stateUpdateRafRef = useRef(null);
203
+ var pendingStateUpdateRef = useRef(null);
204
+ // 缓存当前的滚动状态,避免频繁读取
205
+ var currentScrollStateRef = useRef({ scrollLeft: 0, scrollTop: 0 });
206
+ // 使用 ref 保存最新的函数引用,避免闭包问题
207
+ var setScrollStateRef = useRef(setScrollState);
208
+ var onScrollRef = useRef(onScroll);
209
+ var getCellFromPositionRef = useRef(getCellFromPosition);
210
+ var updateSelectionRef = useRef(updateSelection);
211
+ var columnRenderInfosRef = useRef(columnRenderInfos);
212
+ // 更新 ref 中的函数引用
213
+ useEffect(function () {
214
+ setScrollStateRef.current = setScrollState;
215
+ onScrollRef.current = onScroll;
216
+ getCellFromPositionRef.current = getCellFromPosition;
217
+ updateSelectionRef.current = updateSelection;
218
+ columnRenderInfosRef.current = columnRenderInfos;
219
+ });
220
+ // 同步当前滚动状态到 ref
221
+ useEffect(function () {
222
+ // 通过 setScrollState 的返回值来获取当前状态(不触发状态更新)
223
+ var currentState;
224
+ setScrollStateRef.current(function (prev) {
225
+ currentState = prev;
226
+ return prev; // 不修改状态,只读取
227
+ });
228
+ if (currentState) {
229
+ currentScrollStateRef.current = {
230
+ scrollLeft: currentState.scrollLeft,
231
+ scrollTop: currentState.scrollTop,
232
+ };
233
+ }
234
+ });
196
235
  var SCROLL_BOUNDARY_SIZE = 50; // 触发滚动的边界距离
197
- var SCROLL_SPEED = 10; // 每次滚动的距离
198
- // 检查边界滚动
199
- var checkBoundaryScroll = useMemo(function () {
200
- return function (mouseX, mouseY) {
201
- if (!containerRef.current || !isSelecting)
202
- return;
203
- var rect = containerRef.current.getBoundingClientRect();
204
- var containerWidth = rect.width;
205
- var containerHeight = rect.height;
206
- var scrollDeltaX = 0;
207
- var scrollDeltaY = 0;
208
- // 检查水平边界
209
- if (mouseX < SCROLL_BOUNDARY_SIZE) {
210
- // 接近左边界,向左滚动
211
- scrollDeltaX = -SCROLL_SPEED;
212
- }
213
- else if (mouseX > containerWidth - SCROLL_BOUNDARY_SIZE) {
214
- // 接近右边界,向右滚动
215
- scrollDeltaX = SCROLL_SPEED;
216
- }
217
- // 检查垂直边界
218
- if (mouseY < SCROLL_BOUNDARY_SIZE) {
219
- // 接近顶部边界,向上滚动
220
- scrollDeltaY = -SCROLL_SPEED;
236
+ var SCROLL_SPEED = 6; // 每次滚动的距离(进一步降低以减少频率)
237
+ // 防抖:避免鼠标移动时过度频繁地触发边界检测
238
+ var boundaryCheckThrottleRef = useRef(0);
239
+ // 批量状态更新函数:减少重新渲染次数
240
+ var batchUpdateScrollState = useCallback(function (newScrollLeft, newScrollTop) {
241
+ // 立即更新缓存状态
242
+ currentScrollStateRef.current = {
243
+ scrollLeft: newScrollLeft,
244
+ scrollTop: newScrollTop,
245
+ };
246
+ // 保存待更新的状态
247
+ pendingStateUpdateRef.current = {
248
+ scrollLeft: newScrollLeft,
249
+ scrollTop: newScrollTop,
250
+ };
251
+ // 如果已经有待处理的更新,直接返回
252
+ if (stateUpdateRafRef.current !== null) {
253
+ return;
254
+ }
255
+ // 延迟到下一个渲染周期批量更新
256
+ stateUpdateRafRef.current = requestAnimationFrame(function () {
257
+ var pendingUpdate = pendingStateUpdateRef.current;
258
+ if (pendingUpdate) {
259
+ setScrollStateRef.current(function (prev) {
260
+ if (prev.scrollLeft !== pendingUpdate.scrollLeft ||
261
+ prev.scrollTop !== pendingUpdate.scrollTop) {
262
+ // 延迟调用 onScroll 回调,避免阻塞滚动
263
+ setTimeout(function () {
264
+ var _a;
265
+ (_a = onScrollRef.current) === null || _a === void 0 ? void 0 : _a.call(onScrollRef, pendingUpdate.scrollLeft, pendingUpdate.scrollTop);
266
+ }, 0);
267
+ return __assign(__assign({}, prev), { scrollLeft: pendingUpdate.scrollLeft, scrollTop: pendingUpdate.scrollTop });
268
+ }
269
+ return prev;
270
+ });
221
271
  }
222
- else if (mouseY > containerHeight - SCROLL_BOUNDARY_SIZE) {
223
- // 接近底部边界,向下滚动
224
- scrollDeltaY = SCROLL_SPEED;
272
+ // 清理状态
273
+ stateUpdateRafRef.current = null;
274
+ pendingStateUpdateRef.current = null;
275
+ });
276
+ }, []);
277
+ // 检查边界滚动
278
+ var checkBoundaryScroll = useCallback(function (mouseX, mouseY) {
279
+ if (!containerRef.current || !isSelecting)
280
+ return;
281
+ // 节流:限制边界检测频率(每16ms最多执行一次,约60fps)
282
+ var now = performance.now();
283
+ if (now - boundaryCheckThrottleRef.current < 16) {
284
+ return;
285
+ }
286
+ boundaryCheckThrottleRef.current = now;
287
+ var rect = containerRef.current.getBoundingClientRect();
288
+ var containerWidth = rect.width;
289
+ var containerHeight = rect.height;
290
+ var scrollDeltaX = 0;
291
+ var scrollDeltaY = 0;
292
+ // 检查水平边界
293
+ if (mouseX < SCROLL_BOUNDARY_SIZE) {
294
+ // 接近左边界,向左滚动
295
+ scrollDeltaX = -SCROLL_SPEED;
296
+ }
297
+ else if (mouseX > containerWidth - SCROLL_BOUNDARY_SIZE) {
298
+ // 接近右边界,向右滚动
299
+ scrollDeltaX = SCROLL_SPEED;
300
+ }
301
+ // 检查垂直边界
302
+ if (mouseY < SCROLL_BOUNDARY_SIZE) {
303
+ // 接近顶部边界,向上滚动
304
+ scrollDeltaY = -SCROLL_SPEED;
305
+ }
306
+ else if (mouseY > containerHeight - SCROLL_BOUNDARY_SIZE) {
307
+ // 接近底部边界,向下滚动
308
+ scrollDeltaY = SCROLL_SPEED;
309
+ }
310
+ // 保存当前鼠标位置和滚动速度
311
+ currentMousePosRef.current = { x: mouseX, y: mouseY };
312
+ scrollVelocityRef.current = {
313
+ deltaX: scrollDeltaX,
314
+ deltaY: scrollDeltaY,
315
+ };
316
+ // 如果需要滚动
317
+ if (scrollDeltaX !== 0 || scrollDeltaY !== 0) {
318
+ // 清除之前的动画帧
319
+ if (scrollRafRef.current) {
320
+ cancelAnimationFrame(scrollRafRef.current);
225
321
  }
226
- // 保存当前鼠标位置(用于滚动时更新框选)
227
- currentMousePosRef.current = { x: mouseX, y: mouseY };
228
- // 如果需要滚动
229
- if (scrollDeltaX !== 0 || scrollDeltaY !== 0) {
230
- // 清除之前的定时器
231
- if (scrollIntervalRef.current) {
232
- clearInterval(scrollIntervalRef.current);
322
+ // 开始持续滚动(优化版本)
323
+ var performScroll_1 = function () {
324
+ var _a;
325
+ var velocity = scrollVelocityRef.current;
326
+ if (velocity.deltaX === 0 && velocity.deltaY === 0) {
327
+ return; // 停止滚动
233
328
  }
234
- // 开始持续滚动
235
- scrollIntervalRef.current = setInterval(function () {
236
- setScrollState(function (prev) {
237
- var _a;
238
- var newScrollLeft = Math.max(0, Math.min(maxScrollLeft, prev.scrollLeft + scrollDeltaX));
239
- var newScrollTop = Math.max(0, Math.min(maxScrollTop, prev.scrollTop + scrollDeltaY));
240
- if (newScrollLeft !== prev.scrollLeft ||
241
- newScrollTop !== prev.scrollTop) {
242
- onScroll === null || onScroll === void 0 ? void 0 : onScroll(newScrollLeft, newScrollTop);
243
- // 滚动后检查鼠标位置的单元格并更新框选
244
- if (getCellFromPosition &&
245
- updateSelection &&
246
- columnRenderInfos &&
247
- currentMousePosRef.current) {
248
- var _b = currentMousePosRef.current, x = _b.x, y = _b.y;
249
- var cell = getCellFromPosition(x, y);
250
- if (cell) {
251
- // 检查是否在选择框列,如果是,则不更新框选
252
- var isSelectionColumn = ((_a = columnRenderInfos[cell.col]) === null || _a === void 0 ? void 0 : _a.column.key) === "__selection__";
253
- if (!isSelectionColumn) {
254
- updateSelection(cell);
255
- }
329
+ // 获取当前滚动状态(从缓存中读取,避免触发状态更新)
330
+ var currentState = currentScrollStateRef.current;
331
+ var newScrollLeft = Math.max(0, Math.min(maxScrollLeft, currentState.scrollLeft + velocity.deltaX));
332
+ var newScrollTop = Math.max(0, Math.min(maxScrollTop, currentState.scrollTop + velocity.deltaY));
333
+ // 只有在位置真正发生变化时才更新
334
+ if (newScrollLeft !== currentState.scrollLeft ||
335
+ newScrollTop !== currentState.scrollTop) {
336
+ // 使用批量更新机制
337
+ batchUpdateScrollState(newScrollLeft, newScrollTop);
338
+ // 优化的框选更新:减少不必要的计算
339
+ if (getCellFromPositionRef.current &&
340
+ updateSelectionRef.current &&
341
+ columnRenderInfosRef.current &&
342
+ currentMousePosRef.current) {
343
+ var _b = currentMousePosRef.current, x = _b.x, y = _b.y;
344
+ // 添加位置计算的缓存,避免重复计算
345
+ try {
346
+ var cell = getCellFromPositionRef.current(x, y);
347
+ // 只有当单元格位置发生变化时才更新框选
348
+ if (cell &&
349
+ (!lastCellRef.current ||
350
+ lastCellRef.current.row !== cell.row ||
351
+ lastCellRef.current.col !== cell.col)) {
352
+ var isSelectionColumn = ((_a = columnRenderInfosRef.current[cell.col]) === null || _a === void 0 ? void 0 : _a.column.key) ===
353
+ "__selection__";
354
+ if (!isSelectionColumn) {
355
+ updateSelectionRef.current(cell);
356
+ lastCellRef.current = cell;
256
357
  }
257
358
  }
258
- return __assign(__assign({}, prev), { scrollLeft: newScrollLeft, scrollTop: newScrollTop });
259
359
  }
260
- return prev;
261
- });
262
- }, 50); // 每50毫秒滚动一次
263
- }
264
- else {
265
- // 停止滚动
266
- if (scrollIntervalRef.current) {
267
- clearInterval(scrollIntervalRef.current);
268
- scrollIntervalRef.current = null;
360
+ catch (error) {
361
+ // 如果计算单元格位置出错,静默处理,不影响滚动
362
+ console.warn("Cell position calculation error during boundary scroll:", error);
363
+ }
364
+ }
269
365
  }
366
+ // 继续滚动
367
+ scrollRafRef.current = requestAnimationFrame(performScroll_1);
368
+ };
369
+ scrollRafRef.current = requestAnimationFrame(performScroll_1);
370
+ }
371
+ else {
372
+ // 停止滚动
373
+ if (scrollRafRef.current) {
374
+ cancelAnimationFrame(scrollRafRef.current);
375
+ scrollRafRef.current = null;
270
376
  }
271
- };
377
+ scrollVelocityRef.current = { deltaX: 0, deltaY: 0 };
378
+ }
272
379
  }, [
273
380
  containerRef,
274
381
  isSelecting,
275
- setScrollState,
276
382
  maxScrollTop,
277
383
  maxScrollLeft,
278
- onScroll,
279
- getCellFromPosition,
280
- updateSelection,
281
- columnRenderInfos,
384
+ batchUpdateScrollState,
282
385
  ]);
283
386
  // 停止边界滚动
284
- var stopBoundaryScroll = useMemo(function () {
285
- return function () {
286
- if (scrollIntervalRef.current) {
287
- clearInterval(scrollIntervalRef.current);
288
- scrollIntervalRef.current = null;
289
- }
290
- };
387
+ var stopBoundaryScroll = useCallback(function () {
388
+ if (scrollRafRef.current) {
389
+ cancelAnimationFrame(scrollRafRef.current);
390
+ scrollRafRef.current = null;
391
+ }
392
+ if (stateUpdateRafRef.current) {
393
+ cancelAnimationFrame(stateUpdateRafRef.current);
394
+ stateUpdateRafRef.current = null;
395
+ }
396
+ scrollVelocityRef.current = { deltaX: 0, deltaY: 0 };
397
+ pendingStateUpdateRef.current = null;
398
+ lastCellRef.current = null; // 重置缓存的单元格位置
291
399
  }, []);
292
400
  // 当框选结束时停止滚动
293
401
  useEffect(function () {
@@ -295,11 +403,14 @@ var useSelectionBoundaryScroll = function (params) {
295
403
  stopBoundaryScroll();
296
404
  }
297
405
  }, [isSelecting, stopBoundaryScroll]);
298
- // 组件卸载时清理定时器
406
+ // 组件卸载时清理动画帧
299
407
  useEffect(function () {
300
408
  return function () {
301
- if (scrollIntervalRef.current) {
302
- clearInterval(scrollIntervalRef.current);
409
+ if (scrollRafRef.current) {
410
+ cancelAnimationFrame(scrollRafRef.current);
411
+ }
412
+ if (stateUpdateRafRef.current) {
413
+ cancelAnimationFrame(stateUpdateRafRef.current);
303
414
  }
304
415
  };
305
416
  }, []);
@@ -289,12 +289,21 @@ interface ITableRefHandel {
289
289
  */
290
290
  clearErrorClass?: () => void;
291
291
  /**
292
- * 滚动
293
- */
294
- scrollTo?: (obj: {
295
- row: number;
296
- y: number;
297
- vid: string;
292
+ * 滚动到指定位置
293
+ * 虚拟滚动时:使用 virtuallist-antd 的 scrollTo 方法,需要传 row、y、vid
294
+ * 普通滚动时:操作 DOM,可以传 y、x 或 index
295
+ * @param options.row - 虚拟滚动时滚动到第几行
296
+ * @param options.y - 垂直滚动的像素值
297
+ * @param options.x - 水平滚动的像素值
298
+ * @param options.vid - 虚拟滚动的唯一标识符
299
+ * @param options.index - 滚动到指定行索引(普通滚动)
300
+ */
301
+ scrollTo?: (options: {
302
+ row?: number;
303
+ y?: number;
304
+ x?: number;
305
+ vid?: string;
306
+ index?: number;
298
307
  }) => void;
299
308
  /**
300
309
  * 重置
@@ -19,7 +19,7 @@ import useSummary from './useSummary.js';
19
19
  import useInnerPagination, { getInnerIndex } from './useInnerPagination.js';
20
20
  import { useScuRfresh, useCustomSort, useCalcScrollY, useEditChange, useMoveRowChange, useAddAndDelChange, useCustomFilter, useAutoMerge } from './hooks.js';
21
21
  import { HTML5Backend } from '../node_modules/react-dnd-html5-backend/dist/index.js';
22
- import { VList, scrollTo } from '../node_modules/virtuallist-antd/dist/index.es.js';
22
+ import { scrollTo, VList } from '../node_modules/virtuallist-antd/dist/index.es.js';
23
23
  import useTableValidate, { tableValidate } from './useTableValidate.js';
24
24
  import TitleDirectionColumn from './components/TitleDirectionColumn.js';
25
25
  import { useExcelExport } from './excel.js';
@@ -193,6 +193,43 @@ var Table = function (props) {
193
193
  });
194
194
  // 表格验证
195
195
  var _y = useTableValidate(), tableRef = _y.tableRef, getCurrentTable = _y.getCurrentTable, clearErrorClass = _y.clearErrorClass;
196
+ // 滚动条控制方法
197
+ var scrollToPosition = useMemoizedFn(function (options) {
198
+ var _a;
199
+ // 如果是虚拟滚动,使用 virtuallist-antd 的 scrollTo 方法
200
+ if (virtualKey && options.vid) {
201
+ scrollTo({
202
+ row: options.row || 0,
203
+ y: options.y || 0,
204
+ vid: options.vid || virtualKey,
205
+ });
206
+ }
207
+ else {
208
+ // 普通滚动,操作 DOM
209
+ var tableBody = (_a = tableRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(".ant-table-body");
210
+ if (tableBody) {
211
+ // 如果指定了 y 值,直接滚动到指定位置
212
+ if (typeof options.y === "number") {
213
+ tableBody.scrollTop = options.y;
214
+ }
215
+ // 如果指定了 x 值,直接滚动到指定位置
216
+ if (typeof options.x === "number") {
217
+ tableBody.scrollLeft = options.x;
218
+ }
219
+ // 如果指定了 index,滚动到指定行
220
+ if (typeof options.index === "number") {
221
+ var rows = tableBody.querySelectorAll(".ant-table-row");
222
+ var targetRow = rows[options.index];
223
+ if (targetRow) {
224
+ targetRow.scrollIntoView({
225
+ behavior: "smooth",
226
+ block: "center",
227
+ });
228
+ }
229
+ }
230
+ }
231
+ }
232
+ });
196
233
  // 虚拟滚动选项
197
234
  var vComponents = useMemo(function () {
198
235
  if (virtualKey) {
@@ -307,9 +344,13 @@ var Table = function (props) {
307
344
  }); },
308
345
  /** 清除错误样式 */
309
346
  clearErrorClass: clearErrorClass,
310
- /** 虚拟滚动 滚动到顶部方法 */
311
- scrollTo: function (obj) {
312
- scrollTo(obj);
347
+ /**
348
+ * 滚动到指定位置
349
+ * 虚拟滚动时使用 virtuallist-antd 的 scrollTo 方法
350
+ * 普通滚动时操作 DOM
351
+ */
352
+ scrollTo: function (options) {
353
+ scrollToPosition(options);
313
354
  },
314
355
  /** 动态列配置重置 */
315
356
  onResetDynamicList: function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zmdms-webui",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "private": false,
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",