stk-table-vue 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +194 -194
- package/lib/{Dropdown-D_9cB0rD.js → Dropdown-BAANgg_5.js} +2 -2
- package/lib/{StkTable-CVcmlZi9.js → StkTable-CGcxwpdb.js} +22 -6
- package/lib/src/StkTable/StkTable.vue.d.ts +18 -4
- package/lib/src/StkTable/types/index.d.ts +4 -1
- package/lib/src/StkTable/useVirtualScroll.d.ts +6 -1
- package/lib/stk-table-vue.js +3 -3
- package/lib/style.css +1 -1
- package/package.json +93 -93
- package/src/StkTable/StkTable.vue +2362 -2338
- package/src/StkTable/components/DragHandle.vue +9 -9
- package/src/StkTable/components/SortIcon.vue +6 -6
- package/src/StkTable/components/TreeNodeCell.vue +19 -19
- package/src/StkTable/components/TriangleIcon.vue +3 -3
- package/src/StkTable/const.ts +50 -50
- package/src/StkTable/custom-cells/ChangeCell/ChangeCell.less +34 -34
- package/src/StkTable/custom-cells/ChangeCell/createChangeCell.ts +90 -90
- package/src/StkTable/custom-cells/ChangeCell/index.ts +4 -4
- package/src/StkTable/custom-cells/CheckboxCell/CheckboxCell.less +14 -14
- package/src/StkTable/custom-cells/CheckboxCell/CheckboxCell.vue +56 -56
- package/src/StkTable/custom-cells/CheckboxCell/createCheckboxCell.ts +146 -146
- package/src/StkTable/custom-cells/CheckboxCell/index.ts +5 -5
- package/src/StkTable/custom-cells/FilterCell/Dropdown/index.ts +16 -16
- package/src/StkTable/custom-cells/FilterCell/Dropdown/index.vue +186 -186
- package/src/StkTable/custom-cells/FilterCell/Filter.less +118 -118
- package/src/StkTable/custom-cells/FilterCell/Filter.vue +66 -66
- package/src/StkTable/custom-cells/FilterCell/createFilterCell.ts +120 -120
- package/src/StkTable/custom-cells/FilterCell/index.ts +6 -6
- package/src/StkTable/custom-cells/FilterCell/types.ts +43 -43
- package/src/StkTable/custom-cells/NumberCell/createNumberCell.ts +43 -43
- package/src/StkTable/custom-cells/NumberCell/index.ts +2 -2
- package/src/StkTable/custom-cells/utils/formatNumber.ts +137 -137
- package/src/StkTable/features/const.ts +1 -1
- package/src/StkTable/features/index.ts +1 -1
- package/src/StkTable/features/useAreaSelection.ts +1240 -1239
- package/src/StkTable/index.ts +20 -20
- package/src/StkTable/mergeCellsCache.ts +69 -69
- package/src/StkTable/registerFeature.ts +40 -40
- package/src/StkTable/style.less +791 -791
- package/src/StkTable/types/highlightDimOptions.ts +28 -28
- package/src/StkTable/types/index.ts +503 -500
- package/src/StkTable/useAutoResize.ts +87 -87
- package/src/StkTable/useColResize.ts +202 -202
- package/src/StkTable/useFixedCol.ts +168 -168
- package/src/StkTable/useFixedStyle.ts +75 -75
- package/src/StkTable/useGetFixedColPosition.ts +63 -63
- package/src/StkTable/useHighlight.ts +270 -270
- package/src/StkTable/useIndexResolver.ts +29 -29
- package/src/StkTable/useKeyboardArrowScroll.ts +114 -114
- package/src/StkTable/useMaxRowSpan.ts +60 -60
- package/src/StkTable/useMergeCells.ts +541 -541
- package/src/StkTable/useRowExpand.ts +81 -81
- package/src/StkTable/useScrollRowByRow.ts +96 -96
- package/src/StkTable/useScrollbar.ts +177 -177
- package/src/StkTable/useSorter.ts +267 -267
- package/src/StkTable/useTableColumns.ts +142 -142
- package/src/StkTable/useThDrag.ts +94 -94
- package/src/StkTable/useTrDrag.ts +100 -100
- package/src/StkTable/useTree.ts +253 -253
- package/src/StkTable/useVirtualScroll.ts +1032 -1025
- package/src/StkTable/useWheeling.ts +23 -23
- package/src/StkTable/utils/constRefUtils.ts +29 -29
- package/src/StkTable/utils/index.ts +331 -331
- package/src/StkTable/utils/rowHeightFenwickTree.ts +74 -74
- package/src/StkTable/utils/useTriggerRef.ts +33 -33
- package/src/VirtualTree.vue +622 -622
- package/src/VirtualTreeSelect.vue +367 -367
- package/src/vite-env.d.ts +10 -10
|
@@ -1,1025 +1,1032 @@
|
|
|
1
|
-
import { Ref, ShallowRef, computed, shallowRef, triggerRef } from 'vue';
|
|
2
|
-
import { DEFAULT_ROW_HEIGHT, DEFAULT_TABLE_HEIGHT, DEFAULT_TABLE_WIDTH } from './const';
|
|
3
|
-
import { MergeCellsCache } from './mergeCellsCache';
|
|
4
|
-
import { AutoRowHeightConfig, PrivateRowDT, PrivateStkTableColumn, RowKeyGen, StkTableColumn, UniqKey } from './types';
|
|
5
|
-
import { ScrollbarOptions } from './useScrollbar';
|
|
6
|
-
import { binarySearch } from './utils';
|
|
7
|
-
import { getCalculatedColWidth } from './utils/constRefUtils';
|
|
8
|
-
import { RowHeightFenwickTree } from './utils/rowHeightFenwickTree';
|
|
9
|
-
|
|
10
|
-
/** 暂存纵向虚拟滚动的数据 */
|
|
11
|
-
export type VirtualScrollStore = {
|
|
12
|
-
/** 容器高度 */
|
|
13
|
-
containerHeight: number;
|
|
14
|
-
/** 一页的大小 */
|
|
15
|
-
pageSize: number;
|
|
16
|
-
/** 数组开始位置 */
|
|
17
|
-
startIndex: number;
|
|
18
|
-
/** 数组结束位置 */
|
|
19
|
-
endIndex: number;
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function
|
|
92
|
-
colWidthCache.cols
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
return
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (foundStart) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
//
|
|
426
|
-
for (let i =
|
|
427
|
-
const col = tableHeaderLastValue[i];
|
|
428
|
-
if (col?.fixed === '
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
prefix
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const tempRowHeightFn = rowHeightFn;
|
|
534
|
-
rowHeightFn = (row?: PrivateRowDT) => (row
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
let
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
return
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const
|
|
741
|
-
//
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
if (
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
// Not change:
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
const {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
let
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1
|
+
import { Ref, ShallowRef, computed, shallowRef, triggerRef } from 'vue';
|
|
2
|
+
import { DEFAULT_ROW_HEIGHT, DEFAULT_TABLE_HEIGHT, DEFAULT_TABLE_WIDTH } from './const';
|
|
3
|
+
import { MergeCellsCache } from './mergeCellsCache';
|
|
4
|
+
import { AutoRowHeightConfig, PrivateRowDT, PrivateStkTableColumn, RowKeyGen, StkTableColumn, UniqKey } from './types';
|
|
5
|
+
import { ScrollbarOptions } from './useScrollbar';
|
|
6
|
+
import { binarySearch } from './utils';
|
|
7
|
+
import { getCalculatedColWidth } from './utils/constRefUtils';
|
|
8
|
+
import { RowHeightFenwickTree } from './utils/rowHeightFenwickTree';
|
|
9
|
+
|
|
10
|
+
/** 暂存纵向虚拟滚动的数据 */
|
|
11
|
+
export type VirtualScrollStore = {
|
|
12
|
+
/** 容器高度 */
|
|
13
|
+
containerHeight: number;
|
|
14
|
+
/** 一页的大小 */
|
|
15
|
+
pageSize: number;
|
|
16
|
+
/** 数组开始位置 */
|
|
17
|
+
startIndex: number;
|
|
18
|
+
/** 数组结束位置 */
|
|
19
|
+
endIndex: number;
|
|
20
|
+
/**
|
|
21
|
+
* 基准行高(px)。
|
|
22
|
+
* 由 initVirtualScrollY 随 props.rowHeight 同步(模板中的 --row-height 亦取此值,二者同源),
|
|
23
|
+
* 因此行高配置动态变化后此处不会是旧快照。
|
|
24
|
+
* 仅记录统一基准行高:变高模式(autoRowHeight)与展开行的行级高度不写入此字段。
|
|
25
|
+
*/
|
|
26
|
+
rowHeight: number;
|
|
27
|
+
/** 表格定位上边距 */
|
|
28
|
+
offsetTop: number;
|
|
29
|
+
/**
|
|
30
|
+
* 纵向滚动条位置,用于判断是横向滚动还是纵向。
|
|
31
|
+
* 静默字段:非 relative 模式下写入不触发 triggerRef(滚动窗口未变的帧零重渲染),
|
|
32
|
+
* 不得在响应式上下文(computed/模板/watch)中依赖此字段。
|
|
33
|
+
*/
|
|
34
|
+
scrollTop: number;
|
|
35
|
+
/** 总滚动高度 */
|
|
36
|
+
scrollHeight: number;
|
|
37
|
+
translateY: number;
|
|
38
|
+
/** 视口实际起始行索引(rowspan 修正前的原始值,用于区分 above-viewport 行) */
|
|
39
|
+
viewportStartIndex: number;
|
|
40
|
+
/** 视口实际结束行索引(rowspan 修正前的原始值,用于区分 below-viewport 行) */
|
|
41
|
+
viewportEndIndex: number;
|
|
42
|
+
};
|
|
43
|
+
/** 暂存横向虚拟滚动的数据 */
|
|
44
|
+
export type VirtualScrollXStore = {
|
|
45
|
+
/** 父容器宽度 */
|
|
46
|
+
containerWidth: number;
|
|
47
|
+
/** 滚动容器的宽度 */
|
|
48
|
+
scrollWidth: number;
|
|
49
|
+
/** 开始位置 */
|
|
50
|
+
startIndex: number;
|
|
51
|
+
/** 结束始位置 */
|
|
52
|
+
endIndex: number;
|
|
53
|
+
/** 表格定位左边距 */
|
|
54
|
+
offsetLeft: number;
|
|
55
|
+
/**
|
|
56
|
+
* 横向滚动位置,用于判断是横向滚动还是纵向。
|
|
57
|
+
* 静默字段:非 relative 模式下写入不触发 triggerRef,不得在响应式上下文中依赖此字段。
|
|
58
|
+
*/
|
|
59
|
+
scrollLeft: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** 列宽缓存项 */
|
|
63
|
+
type ColWidthCacheItem = { index: number; cumWidth: number };
|
|
64
|
+
/** 左侧固定列缓存项 */
|
|
65
|
+
type LeftFixedColCacheItem = { index: number; width: number };
|
|
66
|
+
/** 列宽缓存 */
|
|
67
|
+
type ColWidthCache<T> = { cols: T[] | null; nonFixedCols: ColWidthCacheItem[]; leftFixedCols: LeftFixedColCacheItem[] };
|
|
68
|
+
|
|
69
|
+
/** 横向虚拟滚动列宽缓存,避免每次滚动都 O(n) 构建 */
|
|
70
|
+
function useColWidthCache<T extends { fixed?: StkTableColumn<PrivateRowDT>['fixed'] }>(getColWidth: (col: T) => number) {
|
|
71
|
+
let colWidthCache: ColWidthCache<T> = { cols: null, nonFixedCols: [], leftFixedCols: [] };
|
|
72
|
+
|
|
73
|
+
function build(cols: T[]): ColWidthCache<T> {
|
|
74
|
+
const nonFixedCols: ColWidthCacheItem[] = [];
|
|
75
|
+
const leftFixedCols: LeftFixedColCacheItem[] = [];
|
|
76
|
+
let cumWidth = 0;
|
|
77
|
+
for (let i = 0; i < cols.length; i++) {
|
|
78
|
+
const col = cols[i];
|
|
79
|
+
const w = getColWidth(col);
|
|
80
|
+
if (col.fixed === 'left') {
|
|
81
|
+
leftFixedCols.push({ index: i, width: w });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
cumWidth += w;
|
|
85
|
+
nonFixedCols.push({ index: i, cumWidth });
|
|
86
|
+
}
|
|
87
|
+
colWidthCache = { cols, nonFixedCols, leftFixedCols };
|
|
88
|
+
return colWidthCache;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function get(cols: T[]): ColWidthCache<T> {
|
|
92
|
+
if (colWidthCache.cols === cols) return colWidthCache;
|
|
93
|
+
return build(cols);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function clear() {
|
|
97
|
+
colWidthCache.cols = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return [get, clear] as const;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** vue2 优化滚动回收延时 */
|
|
104
|
+
const VUE2_SCROLL_TIMEOUT_MS = 200;
|
|
105
|
+
|
|
106
|
+
/** 静默字段常量(模块级复用,避免每次滚动帧分配数组);relative 模式不静默(见 assignVs 注释) */
|
|
107
|
+
const SILENT_Y_KEYS: ['scrollTop'] = ['scrollTop'];
|
|
108
|
+
const SILENT_X_KEYS: ['scrollLeft'] = ['scrollLeft'];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 合并单元格可视范围修正的最大迭代次数。
|
|
112
|
+
* 合法(不重叠)的合并配置最多 2 轮即收敛(1 轮扩展 + 1 轮验证);
|
|
113
|
+
* 此上限仅针对重叠合并区域等病态配置兜底,防止修正循环过长。
|
|
114
|
+
*/
|
|
115
|
+
const MAX_MERGE_RANGE_EXPAND_ITERATIONS = 8;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* virtual scroll
|
|
119
|
+
* @returns
|
|
120
|
+
*/
|
|
121
|
+
export function useVirtualScroll(
|
|
122
|
+
props: any,
|
|
123
|
+
tableContainerRef: Ref<HTMLElement | undefined>,
|
|
124
|
+
trRef: Ref<HTMLTableRowElement[] | undefined>,
|
|
125
|
+
dataSourceCopy: ShallowRef<PrivateRowDT[]>,
|
|
126
|
+
tableHeaderLast: ShallowRef<PrivateStkTableColumn<PrivateRowDT>[]>,
|
|
127
|
+
tableHeaders: ShallowRef<PrivateStkTableColumn<PrivateRowDT>[][]>,
|
|
128
|
+
rowKeyGen: RowKeyGen,
|
|
129
|
+
maxRowSpan: Map<UniqKey, number>,
|
|
130
|
+
/** 全局最大 rowspan(限定跨界修正扫描范围用) */
|
|
131
|
+
getMaxRowSpanValue: () => number,
|
|
132
|
+
scrollbarOptions: Ref<Required<ScrollbarOptions>>,
|
|
133
|
+
isExperimentalScrollY: Ref<boolean | undefined>,
|
|
134
|
+
/** relative 固定模式的样式(useFixedStyle)以响应式方式依赖 scrollTop/scrollLeft,该模式下保留滚动位置的响应式触发 */
|
|
135
|
+
isRelativeMode: Ref<boolean>,
|
|
136
|
+
/** mergeCells 结果共享缓存(与 useMergeCells 共用,避免重复调用用户回调) */
|
|
137
|
+
mergeCellsCache: MergeCellsCache,
|
|
138
|
+
) {
|
|
139
|
+
const tableHeaderHeight = computed(() => props.headerRowHeight * tableHeaders.value.length);
|
|
140
|
+
|
|
141
|
+
const virtualScroll = shallowRef<VirtualScrollStore>({
|
|
142
|
+
containerHeight: 0,
|
|
143
|
+
rowHeight: props.rowHeight,
|
|
144
|
+
pageSize: 0,
|
|
145
|
+
startIndex: 0,
|
|
146
|
+
endIndex: 0,
|
|
147
|
+
offsetTop: 0,
|
|
148
|
+
scrollTop: 0,
|
|
149
|
+
scrollHeight: 0,
|
|
150
|
+
translateY: 0,
|
|
151
|
+
viewportStartIndex: 0,
|
|
152
|
+
viewportEndIndex: 0,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// TODO: init pageSize
|
|
156
|
+
|
|
157
|
+
const virtualScrollX = shallowRef<VirtualScrollXStore>({
|
|
158
|
+
containerWidth: 0,
|
|
159
|
+
scrollWidth: 0,
|
|
160
|
+
startIndex: 0,
|
|
161
|
+
endIndex: 0,
|
|
162
|
+
offsetLeft: 0,
|
|
163
|
+
scrollLeft: 0,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const [getColWidthCache, clearColWidthCache] = useColWidthCache<PrivateStkTableColumn<PrivateRowDT>>(getCalculatedColWidth);
|
|
167
|
+
|
|
168
|
+
const hasExpandCol = computed(() => {
|
|
169
|
+
return tableHeaderLast.value.some(col => col.type === 'expand');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
/** 是否虚拟滚动标志 */
|
|
173
|
+
const virtual_on = computed(() => {
|
|
174
|
+
return props.virtual && dataSourceCopy.value.length > virtualScroll.value.pageSize;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const virtual_dataSourcePart = computed(() => {
|
|
178
|
+
if (!virtual_on.value) return dataSourceCopy.value;
|
|
179
|
+
const { startIndex, endIndex } = virtualScroll.value;
|
|
180
|
+
return dataSourceCopy.value.slice(startIndex, endIndex + 1);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const virtual_offsetBottom = computed(() => {
|
|
184
|
+
if (!virtual_on.value) return 0;
|
|
185
|
+
const { startIndex, endIndex } = virtualScroll.value;
|
|
186
|
+
const dataSourceCopyValue = dataSourceCopy.value;
|
|
187
|
+
const rowHeight = getRowHeightFn.value();
|
|
188
|
+
if (props.autoRowHeight) {
|
|
189
|
+
// 由行高 Fenwick 树 O(log n) 推导:总高 − 含 endIndex 的累计高,避免尾部逐行累加
|
|
190
|
+
const { tree } = getRowHeightTreeCache(dataSourceCopyValue);
|
|
191
|
+
const offsetBottom = tree.total - tree.query(endIndex + 1);
|
|
192
|
+
return offsetBottom > 0 ? offsetBottom : 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return (dataSourceCopyValue.length - startIndex - virtual_dataSourcePart.value.length) * rowHeight;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const virtualX_on = computed(() => {
|
|
199
|
+
return (
|
|
200
|
+
props.virtualX && tableHeaderLast.value.reduce((sum, col) => (sum += getCalculatedColWidth(col)), 0) > virtualScrollX.value.containerWidth
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
/** 是否多级表头 */
|
|
205
|
+
const isMultiLevelHeader = computed(() => tableHeaders.value.length > 1);
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 需要参与可视范围修正的合并列集合。仅依赖 tableHeaderLast(columns 变化),
|
|
209
|
+
* 缓存后避免在数据更新、纵向滚动换窗等高频调用 buildColMergeRange 时重复收集。
|
|
210
|
+
* 无需修正时返回 null。
|
|
211
|
+
*/
|
|
212
|
+
const virtualX_mergeColsInfo = computed(() => {
|
|
213
|
+
const headers = tableHeaderLast.value;
|
|
214
|
+
const headerLength = headers.length;
|
|
215
|
+
// 右固定列始终渲染在可视区末尾,不参与主可视区合并修正
|
|
216
|
+
let maxColIndex = headerLength;
|
|
217
|
+
const mergeCols: { col: PrivateStkTableColumn<PrivateRowDT>; index: number }[] = [];
|
|
218
|
+
for (let i = 0; i < headerLength; i++) {
|
|
219
|
+
const col = headers[i];
|
|
220
|
+
if (maxColIndex === headerLength && col.fixed === 'right') maxColIndex = i;
|
|
221
|
+
// 固定列始终渲染,不需要修正可视范围
|
|
222
|
+
if (col.mergeCells && !col.fixed) mergeCols.push({ col, index: i });
|
|
223
|
+
}
|
|
224
|
+
if (!mergeCols.length || !maxColIndex) return null;
|
|
225
|
+
return { headerLength, maxColIndex, mergeCols };
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 收集行集合中的列合并(colspan)区间
|
|
230
|
+
* @param rows 行数据
|
|
231
|
+
* @param rowIndexBase mergeCells 入参 rowIndex 的起始值
|
|
232
|
+
* @return { leftReach, rightEnd } 合并区间
|
|
233
|
+
*/
|
|
234
|
+
function buildColMergeRange(rows: PrivateRowDT[], rowIndexBase: number) {
|
|
235
|
+
const mergeColsInfo = virtualX_mergeColsInfo.value;
|
|
236
|
+
if (!mergeColsInfo) return null;
|
|
237
|
+
const { headerLength, maxColIndex, mergeCols } = mergeColsInfo;
|
|
238
|
+
|
|
239
|
+
const leftReach = new Int32Array(headerLength).fill(-1);
|
|
240
|
+
const rightEnd = new Int32Array(headerLength);
|
|
241
|
+
for (let r = 0; r < rows.length; r++) {
|
|
242
|
+
const row = rows[r];
|
|
243
|
+
for (let m = 0; m < mergeCols.length; m++) {
|
|
244
|
+
const { col, index } = mergeCols[m];
|
|
245
|
+
if (index >= maxColIndex) break;
|
|
246
|
+
// 走共享缓存:与 buildHiddenCellMap / 渲染期的 mergeCells 调用复用同一份结果
|
|
247
|
+
const { colspan } = mergeCellsCache.getMergeCellsResult(row, col, rowIndexBase + r, index);
|
|
248
|
+
if (colspan > 1) {
|
|
249
|
+
const end = Math.min(index + colspan, maxColIndex);
|
|
250
|
+
for (let c = index; c < end; c++) {
|
|
251
|
+
if (leftReach[c] === -1 || index < leftReach[c]) leftReach[c] = index;
|
|
252
|
+
if (end > rightEnd[c]) rightEnd[c] = end;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return { leftReach, rightEnd };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 全量数据的列合并覆盖信息(懒计算)。
|
|
262
|
+
* 仅在 Y 虚拟滚动未开启(可视行即全量数据)且被消费时才遍历全量数据;
|
|
263
|
+
* 依赖 dataSourceCopy 与 columns,数据/列变化时自动重算。
|
|
264
|
+
* virtual + virtualX 模式下消费方走可视行分支,本 computed 直接返回 null,
|
|
265
|
+
* 避免 O(rows × mergeCols) 的全量 mergeCells 调用浪费。
|
|
266
|
+
*/
|
|
267
|
+
const virtualX_colMergeRangeFull = computed(() => {
|
|
268
|
+
if (!virtualX_on.value || virtual_on.value) return null;
|
|
269
|
+
return buildColMergeRange(dataSourceCopy.value, 0);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* virtual-x 列合并覆盖信息。
|
|
274
|
+
* Y 虚拟滚动开启时仅计算可视行(rowIndex 传入绝对行索引,与渲染时 mergeCells 入参保持一致);
|
|
275
|
+
* 否则使用全量数据懒计算缓存(此时可视行即全量数据)。
|
|
276
|
+
*/
|
|
277
|
+
const virtualX_colMergeRange = computed(() => {
|
|
278
|
+
if (!virtualX_on.value) return null;
|
|
279
|
+
if (virtual_on.value) {
|
|
280
|
+
return buildColMergeRange(virtual_dataSourcePart.value, virtualScroll.value.startIndex);
|
|
281
|
+
}
|
|
282
|
+
return virtualX_colMergeRangeFull.value;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 经列合并修正后的可视列范围。
|
|
287
|
+
* 合并单元格的锚点列可能已滚出可视区(但合并区域仍覆盖可视列),
|
|
288
|
+
* 也可能合并区域超出可视区右边界,需要将范围扩展到能完整渲染合并单元格。
|
|
289
|
+
*/
|
|
290
|
+
const virtualX_colRange = computed(() => {
|
|
291
|
+
let { startIndex, endIndex } = virtualScrollX.value;
|
|
292
|
+
const mergeRange = virtualX_colMergeRange.value;
|
|
293
|
+
if (mergeRange) {
|
|
294
|
+
const { leftReach, rightEnd } = mergeRange;
|
|
295
|
+
const len = leftReach.length;
|
|
296
|
+
// 扩展后的范围可能引入新的合并单元格,迭代直到稳定(合法配置最多 2 轮,上限兜底病态配置)
|
|
297
|
+
for (let k = 0; k < MAX_MERGE_RANGE_EXPAND_ITERATIONS; k++) {
|
|
298
|
+
let newStart = startIndex;
|
|
299
|
+
let newEnd = endIndex;
|
|
300
|
+
const loopEnd = Math.min(endIndex, len);
|
|
301
|
+
for (let i = Math.max(0, startIndex); i < loopEnd; i++) {
|
|
302
|
+
const reach = leftReach[i];
|
|
303
|
+
if (reach > -1 && reach < newStart) newStart = reach;
|
|
304
|
+
if (rightEnd[i] > newEnd) newEnd = rightEnd[i];
|
|
305
|
+
}
|
|
306
|
+
if (newStart === startIndex && newEnd === endIndex) break;
|
|
307
|
+
startIndex = newStart;
|
|
308
|
+
endIndex = newEnd;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { startIndex, endIndex };
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* 多级表头横向虚拟滚动参数:以顶层列组为单位计算开始/结束位置。
|
|
316
|
+
* - 只有整个顶层组完全滚出视口时才移除(避免 colSpan 变化导致抖动)。
|
|
317
|
+
* - 单级表头时退化为与 tbody 相同的参数。
|
|
318
|
+
* - 范围基于列合并修正后的 virtualX_colRange,保证合并单元格完整渲染。
|
|
319
|
+
*/
|
|
320
|
+
const theadVirtualX = computed(() => {
|
|
321
|
+
if (!virtualX_on.value) {
|
|
322
|
+
const { startIndex, endIndex, offsetLeft } = virtualScrollX.value;
|
|
323
|
+
return { startIndex, endIndex, offsetLeft };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const { startIndex, endIndex } = virtualX_colRange.value;
|
|
327
|
+
|
|
328
|
+
if (!isMultiLevelHeader.value) {
|
|
329
|
+
// 单级表头:offsetLeft 为起始列之前所有非固定列宽度之和
|
|
330
|
+
const { nonFixedCols } = getColWidthCache(tableHeaderLast.value);
|
|
331
|
+
const found = binarySearch(nonFixedCols, mid => (nonFixedCols[mid].index < startIndex ? -1 : 1));
|
|
332
|
+
const offsetLeft = found > 0 ? nonFixedCols[found - 1].cumWidth : 0;
|
|
333
|
+
return { startIndex, endIndex, offsetLeft };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 多级表头:保留与修正后可视范围有交集的顶层列组
|
|
337
|
+
const topLevelCols = tableHeaders.value[0];
|
|
338
|
+
const totalLeafCount = tableHeaderLast.value.length;
|
|
339
|
+
|
|
340
|
+
let theadStartIndex = totalLeafCount;
|
|
341
|
+
let theadEndIndex = totalLeafCount;
|
|
342
|
+
let theadOffsetLeft = 0;
|
|
343
|
+
let cumLeft = 0;
|
|
344
|
+
let foundStart = false;
|
|
345
|
+
|
|
346
|
+
for (let i = 0, len = topLevelCols.length; i < len; i++) {
|
|
347
|
+
const col = topLevelCols[i];
|
|
348
|
+
if (col.fixed === 'left' || col.fixed === 'right') continue;
|
|
349
|
+
|
|
350
|
+
const groupStart = col.__LF_S__ ?? 0;
|
|
351
|
+
const groupEnd = col.__LF_E__ ?? groupStart + 1;
|
|
352
|
+
const groupWidth = col.__W__ || getCalculatedColWidth(col);
|
|
353
|
+
|
|
354
|
+
if (!foundStart && groupEnd > startIndex) {
|
|
355
|
+
foundStart = true;
|
|
356
|
+
theadStartIndex = groupStart;
|
|
357
|
+
theadOffsetLeft = cumLeft;
|
|
358
|
+
}
|
|
359
|
+
if (foundStart) {
|
|
360
|
+
if (groupStart >= endIndex) break;
|
|
361
|
+
theadEndIndex = groupEnd;
|
|
362
|
+
}
|
|
363
|
+
cumLeft += groupWidth;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!foundStart) {
|
|
367
|
+
theadOffsetLeft = cumLeft;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return { startIndex: theadStartIndex, endIndex: theadEndIndex, offsetLeft: theadOffsetLeft };
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const virtualX_columnPart = computed(() => {
|
|
374
|
+
const tableHeaderLastValue = tableHeaderLast.value;
|
|
375
|
+
if (virtualX_on.value) {
|
|
376
|
+
// 使用列合并修正后的可视范围
|
|
377
|
+
const { startIndex, endIndex } = virtualX_colRange.value;
|
|
378
|
+
// 将索引钳制到列数组范围内,防止列数减少时越界
|
|
379
|
+
const maxIndex = tableHeaderLastValue.length;
|
|
380
|
+
const validEndIndex = Math.min(endIndex, maxIndex);
|
|
381
|
+
const validStartIndex = Math.min(startIndex, maxIndex);
|
|
382
|
+
|
|
383
|
+
// 多级表头:分离左/右固定列,插入 spacer 标记实现对齐
|
|
384
|
+
if (isMultiLevelHeader.value) {
|
|
385
|
+
const leftFixedCols: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
386
|
+
const rightFixedCols: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
387
|
+
const visibleCols: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
388
|
+
for (let i = 0; i < tableHeaderLastValue.length; i++) {
|
|
389
|
+
const col = tableHeaderLastValue[i];
|
|
390
|
+
if (col.fixed === 'right') {
|
|
391
|
+
rightFixedCols.push(col);
|
|
392
|
+
} else if (col.fixed === 'left') {
|
|
393
|
+
leftFixedCols.push(col);
|
|
394
|
+
} else if (i >= validStartIndex && i < validEndIndex) {
|
|
395
|
+
visibleCols.push(col);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const result: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
400
|
+
result.push(...leftFixedCols);
|
|
401
|
+
|
|
402
|
+
// left spacer:theadStart ~ tbodyStart 之间非 fixed:left 的叶子列数
|
|
403
|
+
const theadStart = theadVirtualX.value.startIndex;
|
|
404
|
+
const leftSpacerColspan = Math.max(0, startIndex - theadStart);
|
|
405
|
+
if (leftSpacerColspan) {
|
|
406
|
+
result.push({ __VT_C_SP__: leftSpacerColspan } as PrivateStkTableColumn<PrivateRowDT>);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
result.push(...visibleCols);
|
|
410
|
+
|
|
411
|
+
// right spacer:tbodyEnd ~ theadEnd 之间的非 fixed:right 列数
|
|
412
|
+
const rightSpacerColspan = Math.max(0, theadVirtualX.value.endIndex - endIndex);
|
|
413
|
+
if (rightSpacerColspan) {
|
|
414
|
+
result.push({ __VT_C_SP__: rightSpacerColspan } as PrivateStkTableColumn<PrivateRowDT>);
|
|
415
|
+
}
|
|
416
|
+
result.push(...rightFixedCols);
|
|
417
|
+
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// 单级表头:保持原有重排逻辑(向后兼容)
|
|
422
|
+
const leftCols: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
423
|
+
const rightCols: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
424
|
+
|
|
425
|
+
// 左侧固定列,如果在左边不可见区。则需要拿出来放在前面
|
|
426
|
+
for (let i = 0; i < validStartIndex; i++) {
|
|
427
|
+
const col = tableHeaderLastValue[i];
|
|
428
|
+
if (col?.fixed === 'left') leftCols.push(col);
|
|
429
|
+
}
|
|
430
|
+
// 右侧固定列,如果在右边不可见区。则需要拿出来放在后面
|
|
431
|
+
for (let i = validEndIndex; i < tableHeaderLastValue.length; i++) {
|
|
432
|
+
const col = tableHeaderLastValue[i];
|
|
433
|
+
if (col?.fixed === 'right') rightCols.push(col);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const mainColumns = tableHeaderLastValue.slice(validStartIndex, validEndIndex);
|
|
437
|
+
return leftCols.concat(mainColumns).concat(rightCols);
|
|
438
|
+
}
|
|
439
|
+
return tableHeaderLastValue;
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* tbody 行渲染的列分段(超长 colspan 优化)。
|
|
444
|
+
* 修正扩展出的区域都在原始可视视口之外,非锚点单元格无需真实渲染:
|
|
445
|
+
* - leftExpand(原始视口左侧扩展区):由调用方逐行处理——锚点/被覆盖单元格保留原行为,
|
|
446
|
+
* 其余连续普通单元格合并为单个 colspan 占位 td(占位相同列槽位,保持对齐);
|
|
447
|
+
* - 右侧扩展区:完全不渲染(不影响可视单元格对齐,区域在屏幕外)。
|
|
448
|
+
* thead 仍保持完整修正范围(锚点 colspan 需要真实列槽位),仅 tbody 收缩。
|
|
449
|
+
* 多级表头含 spacer/分组逻辑,暂不分段(返回 null 回退完整列列表)。
|
|
450
|
+
*/
|
|
451
|
+
const virtualX_expandColSegments = computed(() => {
|
|
452
|
+
if (!virtualX_on.value || isMultiLevelHeader.value) return null;
|
|
453
|
+
const { startIndex: xStart, endIndex: xEnd } = virtualScrollX.value;
|
|
454
|
+
const { startIndex: cStart, endIndex: cEnd } = virtualX_colRange.value;
|
|
455
|
+
// 无修正扩展时无需分段
|
|
456
|
+
if (cStart >= xStart && cEnd <= xEnd) return null;
|
|
457
|
+
|
|
458
|
+
const headers = tableHeaderLast.value;
|
|
459
|
+
const maxIndex = headers.length;
|
|
460
|
+
const vs = Math.min(cStart, maxIndex);
|
|
461
|
+
const ve = Math.min(cEnd, maxIndex);
|
|
462
|
+
|
|
463
|
+
const prefix: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
464
|
+
const suffix: PrivateStkTableColumn<PrivateRowDT>[] = [];
|
|
465
|
+
for (let i = 0; i < headers.length; i++) {
|
|
466
|
+
const col = headers[i];
|
|
467
|
+
if (i < vs && col.fixed === 'left') prefix.push(col);
|
|
468
|
+
else if (i >= ve && col.fixed === 'right') suffix.push(col);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return {
|
|
472
|
+
prefix,
|
|
473
|
+
leftExpand: headers.slice(vs, Math.min(xStart, ve)),
|
|
474
|
+
// 可视区取原始视口范围;[xEnd, cEnd) 为右扩展区,不进入 tbody 行列表
|
|
475
|
+
viewport: headers.slice(Math.min(xStart, ve), Math.min(xEnd, ve)),
|
|
476
|
+
suffix,
|
|
477
|
+
/** leftExpand 起始的绝对叶子列索引(mergeCells 缓存键用) */
|
|
478
|
+
leftExpandStart: vs,
|
|
479
|
+
};
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* 表头横向虚拟滚动:
|
|
484
|
+
* - 单级表头:最后一行使用 virtualX_columnPart,其他行原样返回。
|
|
485
|
+
* - 多级表头:按顶层组粒度过滤(整个组滚出才移除),保持 colSpan 稳定。
|
|
486
|
+
*/
|
|
487
|
+
const virtualX_tableHeaders = computed(() => {
|
|
488
|
+
if (!virtualX_on.value) return tableHeaders.value;
|
|
489
|
+
if (isMultiLevelHeader.value) {
|
|
490
|
+
const { startIndex, endIndex } = theadVirtualX.value;
|
|
491
|
+
return tableHeaders.value.map(row => {
|
|
492
|
+
return row.filter(col => {
|
|
493
|
+
if (col.fixed === 'left' || col.fixed === 'right') return true;
|
|
494
|
+
const leafStart = col.__LF_S__ ?? 0;
|
|
495
|
+
const leafEnd = col.__LF_E__ ?? leafStart + 1;
|
|
496
|
+
return leafEnd > startIndex && leafStart < endIndex;
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
// 单级:最后一行用 virtualX_columnPart
|
|
501
|
+
const headers = tableHeaders.value;
|
|
502
|
+
return headers.map((row, i) => (i === headers.length - 1 ? virtualX_columnPart.value : row));
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
/** 展开行 colspan:虚拟滚动时等于所有 td 元素数量(含 spacer)之和 */
|
|
506
|
+
const expandRowColspan = computed(() => {
|
|
507
|
+
if (!virtualX_on.value) return tableHeaderLast.value.length;
|
|
508
|
+
const spacers = virtualX_columnPart.value.filter(c => c.__VT_C_SP__);
|
|
509
|
+
// 2 = vt-x-left + vt-x-right
|
|
510
|
+
// 每个 spacer 项占 1 个位置,colspan > 1 时额外增加 (colspan - 1)
|
|
511
|
+
return 2 + virtualX_columnPart.value.length + spacers.reduce((sum, s) => sum + Math.max(0, (s.__VT_C_SP__ ?? 0) - 1), 0);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const virtualX_offsetRight = computed(() => {
|
|
515
|
+
if (!virtualX_on.value) return 0;
|
|
516
|
+
// 多级表头使用 theadEndIndex,单级使用列合并修正后的 endIndex
|
|
517
|
+
const endIndex = isMultiLevelHeader.value ? theadVirtualX.value.endIndex : virtualX_colRange.value.endIndex;
|
|
518
|
+
let width = 0;
|
|
519
|
+
const tableHeaderLastValue = tableHeaderLast.value;
|
|
520
|
+
for (let i = endIndex; i < tableHeaderLastValue.length; i++) {
|
|
521
|
+
const col = tableHeaderLastValue[i];
|
|
522
|
+
if (col.fixed !== 'right') {
|
|
523
|
+
width += getCalculatedColWidth(col);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return width;
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const getRowHeightFn = computed(() => {
|
|
530
|
+
const rowHeight = props.rowHeight || DEFAULT_ROW_HEIGHT;
|
|
531
|
+
let rowHeightFn: (row?: PrivateRowDT) => number = () => rowHeight;
|
|
532
|
+
if (props.autoRowHeight) {
|
|
533
|
+
const tempRowHeightFn = rowHeightFn;
|
|
534
|
+
rowHeightFn = (row?: PrivateRowDT) => getAutoRowHeight(row) || tempRowHeightFn(row);
|
|
535
|
+
}
|
|
536
|
+
if (hasExpandCol.value) {
|
|
537
|
+
const expandedRowHeight = props.expandConfig?.height;
|
|
538
|
+
const tempRowHeightFn = rowHeightFn;
|
|
539
|
+
rowHeightFn = (row?: PrivateRowDT) => (row && row.__EXP_R__ && expandedRowHeight) || tempRowHeightFn(row);
|
|
540
|
+
}
|
|
541
|
+
return rowHeightFn;
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* 初始化虚拟滚动参数
|
|
546
|
+
* @param {number} [height] 虚拟滚动的高度
|
|
547
|
+
*/
|
|
548
|
+
function initVirtualScroll(height?: number) {
|
|
549
|
+
initVirtualScrollY(height);
|
|
550
|
+
initVirtualScrollX();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* 浅合并 patch 到 shallowRef store,且仅当任一属性值实际变化时才 triggerRef。
|
|
555
|
+
* shallowRef 内部属性变更不会触发响应式,必须手动 triggerRef;但无条件触发会让
|
|
556
|
+
* 「值未变」的调用也强制整表重渲染(每帧滚动多付 1~5ms 渲染成本)。
|
|
557
|
+
* 本函数恢复 Vue 深响应式 ref 的 hasChanged 语义,滚动重算回到 0.02ms 级。
|
|
558
|
+
*
|
|
559
|
+
* silentKeys:写入 store 但不参与 triggerRef 判定的字段。滚动位置(scrollTop/scrollLeft)
|
|
560
|
+
* 每帧必变且模板不直接消费,静默写入使「滚动窗口(startIndex/endIndex)未变」的帧
|
|
561
|
+
* 完全跳过组件重渲染(消费方均在滚动处理函数中即时读取 .value)。
|
|
562
|
+
* relative 固定模式例外:useFixedStyle 的样式计算响应式依赖滚动位置,该模式下不静默。
|
|
563
|
+
*/
|
|
564
|
+
function assignVs<T extends object>(ref: ShallowRef<T>, patch: Partial<T>, silentKeys?: (keyof T)[]) {
|
|
565
|
+
const store = ref.value;
|
|
566
|
+
let changed = false;
|
|
567
|
+
for (const key in patch) {
|
|
568
|
+
const k = key as keyof T;
|
|
569
|
+
if (store[k] !== patch[k]) {
|
|
570
|
+
store[k] = patch[k] as T[keyof T];
|
|
571
|
+
if (!silentKeys?.includes(k)) changed = true;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (changed) triggerRef(ref);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* 初始化Y虚拟滚动参数
|
|
579
|
+
* @param {number} [height] 虚拟滚动的高度
|
|
580
|
+
*/
|
|
581
|
+
function initVirtualScrollY(height?: number) {
|
|
582
|
+
if (height !== void 0 && typeof height !== 'number') {
|
|
583
|
+
console.warn('initVirtualScrollY: height must be a number');
|
|
584
|
+
height = 0;
|
|
585
|
+
}
|
|
586
|
+
const { clientHeight, scrollHeight } = tableContainerRef.value || {};
|
|
587
|
+
// 当 isExperimentalScrollY 为 true 时,DOM 的 scrollTop 始终为 0(纵向滚动通过 transform 模拟)
|
|
588
|
+
// 此时应该使用 virtualScroll 中保存的 scrollTop 值
|
|
589
|
+
let scrollTop = isExperimentalScrollY.value ? virtualScroll.value.scrollTop : tableContainerRef.value?.scrollTop || 0;
|
|
590
|
+
|
|
591
|
+
const rowHeight = getRowHeightFn.value();
|
|
592
|
+
const containerHeight = height || clientHeight || DEFAULT_TABLE_HEIGHT;
|
|
593
|
+
const { headless } = props;
|
|
594
|
+
let pageSize = Math.ceil(containerHeight / rowHeight);
|
|
595
|
+
if (!headless) {
|
|
596
|
+
/** 表头高度占几行表体高度数 */
|
|
597
|
+
const headerToBodyRowHeightCount = Math.floor(tableHeaderHeight.value / rowHeight);
|
|
598
|
+
pageSize -= headerToBodyRowHeightCount; //减去表头行数
|
|
599
|
+
}
|
|
600
|
+
// 变高模式下用行高 Fenwick 树的真实总高钳制(与 updateVirtualScrollY 的 scrollHeight 语义一致)
|
|
601
|
+
const totalRowsHeight =
|
|
602
|
+
props.autoRowHeight || hasExpandCol.value
|
|
603
|
+
? getRowHeightTreeCache(dataSourceCopy.value).tree.total
|
|
604
|
+
: dataSourceCopy.value.length * rowHeight;
|
|
605
|
+
const maxScrollTop = Math.max(0, totalRowsHeight + tableHeaderHeight.value - containerHeight);
|
|
606
|
+
if (scrollTop > maxScrollTop) {
|
|
607
|
+
/** fix: 滚动条不在顶部时,表格数据变少,导致滚动条位置有误 */
|
|
608
|
+
scrollTop = maxScrollTop;
|
|
609
|
+
}
|
|
610
|
+
// rowHeight 必须随 props.rowHeight 变化同步:模板的 --row-height 与 SRBR/区域选取均读此字段,
|
|
611
|
+
// 若只在 shallowRef 初始化时求值一次,改行高后会出现「计算已用新行高、渲染仍是旧行高」的不一致。
|
|
612
|
+
assignVs(virtualScroll, { rowHeight, containerHeight, pageSize, scrollHeight });
|
|
613
|
+
updateVirtualScrollY(scrollTop);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function initVirtualScrollX() {
|
|
617
|
+
const { clientWidth, scrollLeft, scrollWidth } = tableContainerRef.value || {};
|
|
618
|
+
assignVs(virtualScrollX, {
|
|
619
|
+
containerWidth: clientWidth || DEFAULT_TABLE_WIDTH,
|
|
620
|
+
scrollWidth: scrollWidth || DEFAULT_TABLE_WIDTH,
|
|
621
|
+
});
|
|
622
|
+
updateVirtualScrollX(scrollLeft);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
let vue2ScrollYTimeout: null | number = null;
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* every row actual height.
|
|
629
|
+
* 不在当前数据中的行键条目会在行高 Fenwick 树缓存重建时被修剪(见 getRowHeightTreeCache),不会无限残留。
|
|
630
|
+
*/
|
|
631
|
+
const autoRowHeightMap = new Map<string, number>();
|
|
632
|
+
|
|
633
|
+
/** 行高 Fenwick 树缓存:以 dataSourceCopy 数组引用与行高函数为缓存键,任一变化时懒重建(仿 useColWidthCache 模式) */
|
|
634
|
+
let rowHeightTreeCache: {
|
|
635
|
+
data: PrivateRowDT[] | null;
|
|
636
|
+
tree: RowHeightFenwickTree;
|
|
637
|
+
keyIndex: Map<string, number>;
|
|
638
|
+
heightFn: ((row?: PrivateRowDT) => number) | null;
|
|
639
|
+
} = {
|
|
640
|
+
data: null,
|
|
641
|
+
tree: new RowHeightFenwickTree(0),
|
|
642
|
+
keyIndex: new Map(),
|
|
643
|
+
heightFn: null,
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* 获取行高 Fenwick 树缓存。dataSourceCopy 引用或行高函数变化(如 rowHeight / expectedHeight prop 变更)时 O(n) 重建:
|
|
648
|
+
* 各行高度取 getRowHeightFn(row) 当前值(实测/setAutoHeight 值 → expectedHeight → 默认行高),
|
|
649
|
+
* 并顺带修剪 autoRowHeightMap(删除不在新数据中的行键,防止内存无限残留)。
|
|
650
|
+
*/
|
|
651
|
+
function getRowHeightTreeCache(data: PrivateRowDT[]) {
|
|
652
|
+
const heightFn = getRowHeightFn.value;
|
|
653
|
+
if (rowHeightTreeCache.data === data && rowHeightTreeCache.heightFn === heightFn) return rowHeightTreeCache;
|
|
654
|
+
const len = data.length;
|
|
655
|
+
const heights = new Float64Array(len);
|
|
656
|
+
const keyIndex = new Map<string, number>();
|
|
657
|
+
for (let i = 0; i < len; i++) {
|
|
658
|
+
const row = data[i];
|
|
659
|
+
keyIndex.set(String(rowKeyGen(row)), i);
|
|
660
|
+
heights[i] = heightFn(row);
|
|
661
|
+
}
|
|
662
|
+
const tree = new RowHeightFenwickTree(len);
|
|
663
|
+
tree.build(heights);
|
|
664
|
+
// 修剪:不在新数据中的行键的行高(仅发生在数据变化的低频时刻)
|
|
665
|
+
for (const key of autoRowHeightMap.keys()) {
|
|
666
|
+
if (!keyIndex.has(key)) autoRowHeightMap.delete(key);
|
|
667
|
+
}
|
|
668
|
+
rowHeightTreeCache = { data, tree, keyIndex, heightFn };
|
|
669
|
+
return rowHeightTreeCache;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/** 内部诊断:行高缓存状态(不暴露为组件实例方法,测试用) */
|
|
673
|
+
function getRowHeightCacheInfo() {
|
|
674
|
+
return {
|
|
675
|
+
mapSize: autoRowHeightMap.size,
|
|
676
|
+
mapKeys: [...autoRowHeightMap.keys()],
|
|
677
|
+
cacheSize: rowHeightTreeCache.keyIndex.size,
|
|
678
|
+
total: rowHeightTreeCache.tree.total,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/** 如果行高度有变化,则要调用此方法清除保存的行高 */
|
|
683
|
+
function setAutoHeight(rowKey: UniqKey, height?: number | null) {
|
|
684
|
+
const key = String(rowKey);
|
|
685
|
+
// 行在当前数据中则对树做单点更新,使定位/总高立即生效;行键不在当前数据中时仅更新 Map
|
|
686
|
+
const cache = rowHeightTreeCache;
|
|
687
|
+
const idx = cache.data === dataSourceCopy.value ? cache.keyIndex.get(key) : void 0;
|
|
688
|
+
const row = idx === void 0 || !cache.data ? void 0 : cache.data[idx];
|
|
689
|
+
const oldHeight = row ? getRowHeightFn.value(row) : 0;
|
|
690
|
+
if (!height) {
|
|
691
|
+
autoRowHeightMap.delete(key);
|
|
692
|
+
} else {
|
|
693
|
+
autoRowHeightMap.set(key, height);
|
|
694
|
+
}
|
|
695
|
+
if (row) cache.tree.add(idx as number, getRowHeightFn.value(row) - oldHeight);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function clearAllAutoHeight() {
|
|
699
|
+
autoRowHeightMap.clear();
|
|
700
|
+
// 失效树缓存:下次访问时按估算高度重建
|
|
701
|
+
rowHeightTreeCache.data = null;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* 前 count 行的累计高度(即第 count 行(0 起始)顶部的偏移量)。
|
|
706
|
+
* 固定行高直接相乘;变高/展开行逐行累加(实测/setAutoHeight 值 → expectedHeight → rowHeight)。
|
|
707
|
+
*/
|
|
708
|
+
function getRowsHeight(count: number) {
|
|
709
|
+
const data = dataSourceCopy.value;
|
|
710
|
+
const end = Math.min(count, data.length);
|
|
711
|
+
if (end <= 0) return 0;
|
|
712
|
+
const heightFn = getRowHeightFn.value;
|
|
713
|
+
if (!props.autoRowHeight && !hasExpandCol.value) return end * heightFn();
|
|
714
|
+
let sum = 0;
|
|
715
|
+
for (let i = 0; i < end; i++) {
|
|
716
|
+
sum += heightFn(data[i]);
|
|
717
|
+
}
|
|
718
|
+
return sum;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function getAutoRowHeight(row?: PrivateRowDT) {
|
|
722
|
+
if (!row) return;
|
|
723
|
+
const rowKey = rowKeyGen(row);
|
|
724
|
+
const storedHeight = autoRowHeightMap.get(String(rowKey));
|
|
725
|
+
if (storedHeight) {
|
|
726
|
+
return storedHeight;
|
|
727
|
+
}
|
|
728
|
+
const expectedHeight: AutoRowHeightConfig<PrivateRowDT>['expectedHeight'] = props.autoRowHeight?.expectedHeight;
|
|
729
|
+
if (expectedHeight) {
|
|
730
|
+
if (typeof expectedHeight === 'function') {
|
|
731
|
+
return expectedHeight(row);
|
|
732
|
+
} else {
|
|
733
|
+
return expectedHeight;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** 通过滚动条位置,计算虚拟滚动的参数 */
|
|
739
|
+
function updateVirtualScrollY(sTop = 0) {
|
|
740
|
+
const { pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex, containerHeight } = virtualScroll.value;
|
|
741
|
+
// 先更新滚动条位置记录,其他地方有依赖。(stripe 时ArrowUp/Down滚动依赖)
|
|
742
|
+
|
|
743
|
+
const dataSourceCopyTemp = dataSourceCopy.value;
|
|
744
|
+
const dataLength = dataSourceCopyTemp.length;
|
|
745
|
+
const rowHeight = getRowHeightFn.value();
|
|
746
|
+
|
|
747
|
+
const vsValue: any = {};
|
|
748
|
+
// 变高模式取行高 Fenwick 树的真实累计高度(实测优先,未测量行按估算高度),替代「行数 × 默认行高」的粗略估算
|
|
749
|
+
const totalRowsHeight =
|
|
750
|
+
props.autoRowHeight || hasExpandCol.value ? getRowHeightTreeCache(dataSourceCopyTemp).tree.total : dataLength * rowHeight;
|
|
751
|
+
const scrollHeight = totalRowsHeight + tableHeaderHeight.value;
|
|
752
|
+
const { enabled: scrollbarEnable } = scrollbarOptions.value;
|
|
753
|
+
if (scrollbarEnable) {
|
|
754
|
+
vsValue.scrollHeight = scrollHeight;
|
|
755
|
+
if (isExperimentalScrollY.value) {
|
|
756
|
+
let maxTop: number;
|
|
757
|
+
sTop = sTop < 0 ? 0 : sTop < (maxTop = scrollHeight - containerHeight) ? sTop : maxTop;
|
|
758
|
+
vsValue.translateY = props.scrollRowByRow ? 0 : -(sTop % rowHeight);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
vsValue.scrollTop = sTop;
|
|
762
|
+
|
|
763
|
+
// relative 模式固定表头样式响应式依赖 scrollTop,保留触发;其余模式静默写入
|
|
764
|
+
assignVs(virtualScroll, vsValue, isRelativeMode.value ? undefined : SILENT_Y_KEYS);
|
|
765
|
+
|
|
766
|
+
if (!virtual_on.value) {
|
|
767
|
+
// github #34 init
|
|
768
|
+
// endIndex 重置为最后一行而非 0:virtual_on=false 时虽不参与切片(全量渲染),
|
|
769
|
+
// 但若后续数据变多而未被重算(如旧版筛选/排序路径,见 #80),残留 endIndex=0
|
|
770
|
+
// 会使 slice(startIndex, 0+1) 只渲染 1 行;指向末行可让残留窗口覆盖全量数据。
|
|
771
|
+
const lastIdx = Math.max(0, dataLength - 1);
|
|
772
|
+
assignVs(virtualScroll, {
|
|
773
|
+
startIndex: 0,
|
|
774
|
+
endIndex: lastIdx,
|
|
775
|
+
offsetTop: 0,
|
|
776
|
+
viewportStartIndex: 0,
|
|
777
|
+
viewportEndIndex: lastIdx,
|
|
778
|
+
});
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const { autoRowHeight, stripe, optimizeVue2Scroll } = props;
|
|
783
|
+
// const dataLength = dataSourceCopyTemp.length;
|
|
784
|
+
|
|
785
|
+
let startIndex = 0;
|
|
786
|
+
let endIndex = dataLength;
|
|
787
|
+
let autoRowHeightTop = 0;
|
|
788
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
789
|
+
const treeCache = getRowHeightTreeCache(dataSourceCopyTemp);
|
|
790
|
+
if (autoRowHeight && trRef.value) {
|
|
791
|
+
// Batch DOM measurements for better performance
|
|
792
|
+
const trElements = trRef.value;
|
|
793
|
+
const { tree, keyIndex } = treeCache;
|
|
794
|
+
const heightFn = getRowHeightFn.value;
|
|
795
|
+
for (let i = 0, len = trElements.length; i < len; i++) {
|
|
796
|
+
const tr = trElements[i];
|
|
797
|
+
const rowKey = tr.dataset.rowKey;
|
|
798
|
+
if (!rowKey || autoRowHeightMap.has(rowKey)) continue;
|
|
799
|
+
const idx = keyIndex.get(rowKey);
|
|
800
|
+
const row = idx === void 0 ? void 0 : dataSourceCopyTemp[idx];
|
|
801
|
+
// 本次测量前树中记录的高度(实测/setAutoHeight 值或估算值)
|
|
802
|
+
const oldHeight = row ? heightFn(row) : 0;
|
|
803
|
+
autoRowHeightMap.set(rowKey, tr.offsetHeight);
|
|
804
|
+
// 实测高度为 0 时有效高度不变(getAutoRowHeight 忽略 0 值),跳过树更新
|
|
805
|
+
if (idx !== void 0 && tr.offsetHeight) tree.add(idx, tr.offsetHeight - oldHeight);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
// calculate startIndex:树上二分,累计高度(含本行)首次 >= sTop 的行,O(log n)
|
|
809
|
+
const tree = treeCache.tree;
|
|
810
|
+
startIndex = tree.findFirstPrefixGE(sTop);
|
|
811
|
+
if (startIndex >= dataLength) {
|
|
812
|
+
// sTop 超过总高:与旧逐行累加语义保持一致(startIndex 停留 0,offsetTop 为总高)
|
|
813
|
+
startIndex = 0;
|
|
814
|
+
autoRowHeightTop = tree.total;
|
|
815
|
+
} else {
|
|
816
|
+
autoRowHeightTop = tree.query(startIndex);
|
|
817
|
+
}
|
|
818
|
+
// calculate endIndex:树上二分,自 startIndex 之后累计高度首次 >= containerHeight 的行,O(log n)
|
|
819
|
+
const found = tree.findFirstPrefixGE(tree.query(startIndex + 1) + containerHeight);
|
|
820
|
+
endIndex = Math.max(found, Math.min(startIndex + 1, dataLength));
|
|
821
|
+
} else {
|
|
822
|
+
startIndex = Math.floor(sTop / rowHeight);
|
|
823
|
+
endIndex = startIndex + pageSize;
|
|
824
|
+
if (startIndex === oldStartIndex && endIndex === oldEndIndex) {
|
|
825
|
+
// Not change: still update viewportStartIndex/viewportEndIndex for above/below-viewport tracking
|
|
826
|
+
const vs = virtualScroll.value;
|
|
827
|
+
if (vs.viewportStartIndex !== startIndex || vs.viewportEndIndex !== endIndex) {
|
|
828
|
+
vs.viewportStartIndex = startIndex;
|
|
829
|
+
vs.viewportEndIndex = endIndex;
|
|
830
|
+
triggerRef(virtualScroll);
|
|
831
|
+
}
|
|
832
|
+
// Not change: not update
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Save viewportStartIndex/viewportEndIndex before rowspan/stripe correction
|
|
838
|
+
const viewportStartIndex = startIndex;
|
|
839
|
+
const viewportEndIndex = endIndex;
|
|
840
|
+
|
|
841
|
+
if (maxRowSpan.size) {
|
|
842
|
+
// fix startIndex:查找是否有合并行跨越当前startIndex
|
|
843
|
+
let correctedStartIndex = startIndex;
|
|
844
|
+
let correctedEndIndex = endIndex;
|
|
845
|
+
|
|
846
|
+
// 跨越 startIndex 的锚点行必然位于 [startIndex - maxSpan + 1, startIndex - 1],
|
|
847
|
+
// 在此范围内反向扫描即可(保留最早锚点语义:循环不提前 break,最终留下最小 i),
|
|
848
|
+
// 避免原实现从 0 起 O(startIndex) 全量扫描——大数据量滚动到深处时每帧开销巨大。
|
|
849
|
+
const scanFrom = Math.max(0, startIndex - getMaxRowSpanValue() + 1);
|
|
850
|
+
for (let i = startIndex - 1; i >= scanFrom; i--) {
|
|
851
|
+
const row = dataSourceCopyTemp[i];
|
|
852
|
+
if (!row) continue;
|
|
853
|
+
const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
|
|
854
|
+
if (spanEndIndex > startIndex) {
|
|
855
|
+
// 找到跨越startIndex的合并行,将startIndex修正为合并行的起始索引
|
|
856
|
+
correctedStartIndex = i;
|
|
857
|
+
if (spanEndIndex > endIndex) {
|
|
858
|
+
// 合并行跨越了整个可视区
|
|
859
|
+
// spanEndIndex 是开区间末端(最后一行+1),endIndex 需要的是闭区间最后一行,
|
|
860
|
+
// 否则会把合并区域外的下一行也算进视口下方,导致下方占位 tr 多 1 行高,
|
|
861
|
+
// 跨视口底部的 rowspan 单元格可视高度被撑大 1 行(滚动时居中文字抖动)。
|
|
862
|
+
correctedEndIndex = spanEndIndex - 1;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// fix endIndex:查找是否有合并行跨越当前endIndex
|
|
868
|
+
for (let i = correctedStartIndex; i < endIndex; i++) {
|
|
869
|
+
const row = dataSourceCopyTemp[i];
|
|
870
|
+
if (!row) continue;
|
|
871
|
+
const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
|
|
872
|
+
if (spanEndIndex > correctedEndIndex) {
|
|
873
|
+
// 找到跨越endIndex的合并行,将endIndex修正为合并行的结束索引(闭区间最后一行)。
|
|
874
|
+
// spanEndIndex 为开区间末端,需 -1,避免把合并区域外的一行并入视口下方占位 tr。
|
|
875
|
+
correctedEndIndex = Math.max(spanEndIndex - 1, correctedEndIndex);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
startIndex = correctedStartIndex;
|
|
880
|
+
endIndex = correctedEndIndex;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (stripe && !isExperimentalScrollY.value && startIndex > 0 && startIndex % 2) {
|
|
884
|
+
// 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
|
|
885
|
+
startIndex -= 1; // 奇数-1变成偶数
|
|
886
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
887
|
+
const height = getRowHeightFn.value(dataSourceCopyTemp[startIndex]);
|
|
888
|
+
autoRowHeightTop -= height;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
startIndex = Math.max(0, startIndex);
|
|
893
|
+
endIndex = Math.min(endIndex, dataLength);
|
|
894
|
+
|
|
895
|
+
if (startIndex >= endIndex) {
|
|
896
|
+
// fallback(#80:scrollTop 残留超出新数据长度时 endIndex 被钳到 dataLength,
|
|
897
|
+
// 需回退到末页窗口;钳非负防 pageSize 并常时产生负 startIndex → 负 offsetTop)
|
|
898
|
+
startIndex = Math.max(0, endIndex - pageSize);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (vue2ScrollYTimeout) {
|
|
902
|
+
window.clearTimeout(vue2ScrollYTimeout);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
let offsetTop = 0;
|
|
906
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
907
|
+
offsetTop = autoRowHeightTop;
|
|
908
|
+
} else {
|
|
909
|
+
offsetTop = startIndex * rowHeight;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* en: If scroll faster than one page, roll back
|
|
914
|
+
*/
|
|
915
|
+
if (!optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
|
|
916
|
+
// scroll up
|
|
917
|
+
assignVs(virtualScroll, { startIndex, endIndex, offsetTop, viewportStartIndex, viewportEndIndex });
|
|
918
|
+
} else {
|
|
919
|
+
// vue2 scroll down optimize
|
|
920
|
+
assignVs(virtualScroll, { endIndex, viewportStartIndex, viewportEndIndex });
|
|
921
|
+
vue2ScrollYTimeout = window.setTimeout(() => {
|
|
922
|
+
assignVs(virtualScroll, { startIndex, offsetTop, viewportStartIndex, viewportEndIndex });
|
|
923
|
+
}, VUE2_SCROLL_TIMEOUT_MS);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
let vue2ScrollXTimeout: null | number = null;
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Calculate virtual scroll parameters based on horizontal scroll bar position
|
|
931
|
+
*/
|
|
932
|
+
function updateVirtualScrollX(sLeft = 0) {
|
|
933
|
+
if (!props.virtualX) return;
|
|
934
|
+
const tableHeaderLastValue = tableHeaderLast.value;
|
|
935
|
+
const headerLength = tableHeaderLastValue?.length;
|
|
936
|
+
if (!headerLength) return;
|
|
937
|
+
|
|
938
|
+
const { scrollLeft, containerWidth } = virtualScrollX.value;
|
|
939
|
+
let startIndex = 0;
|
|
940
|
+
let offsetLeft = 0;
|
|
941
|
+
/** 横向滚动时,第一列的剩余宽度 */
|
|
942
|
+
let leftFirstColRestWidth = 0;
|
|
943
|
+
|
|
944
|
+
// 使用缓存的累计宽度数组,列配置不变时直接复用
|
|
945
|
+
const { nonFixedCols, leftFixedCols } = getColWidthCache(tableHeaderLastValue);
|
|
946
|
+
|
|
947
|
+
if (nonFixedCols.length > 0 && sLeft > 0) {
|
|
948
|
+
// 二分查找:找到第一个累计宽度 > sLeft 的非固定列
|
|
949
|
+
// 使用 <= 确保当列右边缘恰好等于 sLeft 时(列完全滚出视口),不再将其作为起始列
|
|
950
|
+
const found = binarySearch(nonFixedCols, mid => {
|
|
951
|
+
return nonFixedCols[mid].cumWidth <= sLeft ? -1 : 1;
|
|
952
|
+
});
|
|
953
|
+
const idx = Math.min(found, nonFixedCols.length - 1);
|
|
954
|
+
startIndex = nonFixedCols[idx].index;
|
|
955
|
+
offsetLeft = idx > 0 ? nonFixedCols[idx - 1].cumWidth : 0;
|
|
956
|
+
leftFirstColRestWidth = nonFixedCols[idx].cumWidth - sLeft;
|
|
957
|
+
} else if (nonFixedCols.length > 0) {
|
|
958
|
+
startIndex = nonFixedCols[0].index;
|
|
959
|
+
}
|
|
960
|
+
// -----
|
|
961
|
+
// 根据 startIndex 快速计算实际在可视区域内的左侧固定列宽度
|
|
962
|
+
let actualLeftColWidthSum = 0;
|
|
963
|
+
for (const leftCol of leftFixedCols) {
|
|
964
|
+
if (leftCol.index >= startIndex) break;
|
|
965
|
+
actualLeftColWidthSum += leftCol.width;
|
|
966
|
+
}
|
|
967
|
+
const containerW = containerWidth - actualLeftColWidthSum;
|
|
968
|
+
let endIndex = headerLength;
|
|
969
|
+
let endColWidthSum = leftFirstColRestWidth;
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* 这里根据 leftFirstColRestWidth 如果为0 说明开始位置恰好在单元格边界,则计算endIndex 需要从当前单元格开始。
|
|
973
|
+
* 如果有值,则说明开始位置的单元格已经切了一半,需要从下一个单元格开始计算 因此startIndex + 1。
|
|
974
|
+
*/
|
|
975
|
+
for (let colIndex = leftFirstColRestWidth ? startIndex + 1 : startIndex; colIndex < headerLength; colIndex++) {
|
|
976
|
+
const col = tableHeaderLastValue[colIndex];
|
|
977
|
+
endColWidthSum += getCalculatedColWidth(col);
|
|
978
|
+
// 列宽大于容器宽度则停止
|
|
979
|
+
if (endColWidthSum >= containerW) {
|
|
980
|
+
endIndex = colIndex + 1; // slice endIndex + 1
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
endIndex = Math.min(endIndex, headerLength);
|
|
986
|
+
|
|
987
|
+
if (vue2ScrollXTimeout) {
|
|
988
|
+
window.clearTimeout(vue2ScrollXTimeout);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// relative 模式固定列样式响应式依赖 scrollLeft,保留触发;其余模式静默写入
|
|
992
|
+
const silentXKeys = isRelativeMode.value ? undefined : SILENT_X_KEYS;
|
|
993
|
+
|
|
994
|
+
// <= 等于是因为初始化时要赋值
|
|
995
|
+
if (!props.optimizeVue2Scroll || sLeft <= scrollLeft) {
|
|
996
|
+
// 向左滚动
|
|
997
|
+
assignVs(virtualScrollX, { startIndex, endIndex, offsetLeft, scrollLeft: sLeft }, silentXKeys);
|
|
998
|
+
} else {
|
|
999
|
+
// vue2 向右滚动优化
|
|
1000
|
+
assignVs(virtualScrollX, { endIndex, scrollLeft: sLeft }, silentXKeys);
|
|
1001
|
+
vue2ScrollXTimeout = window.setTimeout(() => {
|
|
1002
|
+
assignVs(virtualScrollX, { startIndex, offsetLeft });
|
|
1003
|
+
}, VUE2_SCROLL_TIMEOUT_MS);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return [
|
|
1008
|
+
virtualScroll,
|
|
1009
|
+
virtualScrollX,
|
|
1010
|
+
virtual_on,
|
|
1011
|
+
virtual_dataSourcePart,
|
|
1012
|
+
virtual_offsetBottom,
|
|
1013
|
+
virtualX_on,
|
|
1014
|
+
virtualX_offsetRight,
|
|
1015
|
+
tableHeaderHeight,
|
|
1016
|
+
initVirtualScroll,
|
|
1017
|
+
initVirtualScrollY,
|
|
1018
|
+
initVirtualScrollX,
|
|
1019
|
+
updateVirtualScrollY,
|
|
1020
|
+
updateVirtualScrollX,
|
|
1021
|
+
setAutoHeight,
|
|
1022
|
+
clearAllAutoHeight,
|
|
1023
|
+
getRowsHeight,
|
|
1024
|
+
clearColWidthCache,
|
|
1025
|
+
virtualX_tableHeaders,
|
|
1026
|
+
expandRowColspan,
|
|
1027
|
+
theadVirtualX,
|
|
1028
|
+
virtualX_columnPart,
|
|
1029
|
+
virtualX_expandColSegments,
|
|
1030
|
+
getRowHeightCacheInfo,
|
|
1031
|
+
] as const;
|
|
1032
|
+
}
|