stk-table-vue 0.2.9 → 0.3.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/README.md +79 -7
- package/lib/src/StkTable/StkTable.vue.d.ts +16 -3
- package/lib/src/StkTable/const.d.ts +2 -1
- package/lib/src/StkTable/types/index.d.ts +13 -12
- package/lib/src/StkTable/useAutoResize.d.ts +2 -2
- package/lib/src/StkTable/useColResize.d.ts +5 -5
- package/lib/src/StkTable/useFixedCol.d.ts +5 -5
- package/lib/src/StkTable/useFixedStyle.d.ts +3 -3
- package/lib/src/StkTable/useHighlight.d.ts +11 -11
- package/lib/src/StkTable/useKeyboardArrowScroll.d.ts +4 -3
- package/lib/src/StkTable/useVirtualScroll.d.ts +4 -4
- package/lib/src/StkTable/utils.d.ts +11 -3
- package/lib/stk-table-vue.js +317 -187
- package/lib/style.css +7 -2
- package/package.json +4 -5
- package/src/StkTable/StkTable.vue +70 -43
- package/src/StkTable/const.ts +3 -1
- package/src/StkTable/style.less +10 -6
- package/src/StkTable/types/index.ts +14 -6
- package/src/StkTable/useAutoResize.ts +5 -5
- package/src/StkTable/useColResize.ts +18 -18
- package/src/StkTable/useFixedCol.ts +6 -6
- package/src/StkTable/useFixedStyle.ts +11 -8
- package/src/StkTable/useHighlight.ts +252 -107
- package/src/StkTable/useKeyboardArrowScroll.ts +19 -7
- package/src/StkTable/useVirtualScroll.ts +14 -11
- package/src/StkTable/utils.ts +27 -9
- package/src/vite-env.d.ts +4 -0
package/lib/style.css
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
--bg-border-left:linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
|
|
22
22
|
--highlight-color:#71a2fd;
|
|
23
23
|
--highlight-duration:2s;
|
|
24
|
+
--highlight-easing:linear;
|
|
24
25
|
--stripe-bgc:#fafafc;
|
|
25
26
|
--sort-arrow-color:#757699;
|
|
26
27
|
--sort-arrow-hover-color:#8f90b5;
|
|
@@ -149,13 +150,16 @@
|
|
|
149
150
|
background-color:inherit;
|
|
150
151
|
}
|
|
151
152
|
.stk-table .stk-table-main td.highlight-cell{
|
|
152
|
-
animation:stk-table-dim var(--highlight-duration)
|
|
153
|
+
animation:stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
153
154
|
}
|
|
154
155
|
.stk-table .stk-table-main td.text-overflow .table-cell-wrapper{
|
|
155
156
|
white-space:nowrap;
|
|
156
157
|
overflow:hidden;
|
|
157
158
|
text-overflow:ellipsis;
|
|
158
159
|
}
|
|
160
|
+
.stk-table .stk-table-main td.seq-column{
|
|
161
|
+
text-align:center;
|
|
162
|
+
}
|
|
159
163
|
.stk-table .stk-table-main .fixed-cell--left{
|
|
160
164
|
--shadow-rotate:90deg;
|
|
161
165
|
}
|
|
@@ -246,9 +250,10 @@
|
|
|
246
250
|
.stk-table .stk-table-main tbody tr{
|
|
247
251
|
background-color:var(--td-bgc);
|
|
248
252
|
height:var(--row-height);
|
|
253
|
+
transform:translateZ(0);
|
|
249
254
|
}
|
|
250
255
|
.stk-table .stk-table-main tbody tr.highlight-row{
|
|
251
|
-
animation:stk-table-dim var(--highlight-duration)
|
|
256
|
+
animation:stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
252
257
|
}
|
|
253
258
|
.stk-table .stk-table-main tbody tr.hover,
|
|
254
259
|
.stk-table .stk-table-main tbody tr:hover{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stk-table-vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Simple realtime virtual table for vue3&vue2.7",
|
|
5
5
|
"main": "./lib/stk-table-vue.js",
|
|
6
6
|
"types": "./lib/src/StkTable/index.d.ts",
|
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
"@types/node": "^20.11.14",
|
|
36
36
|
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
37
37
|
"@typescript-eslint/parser": "^6.14.0",
|
|
38
|
-
"@vitejs/plugin-vue": "^5.0.
|
|
39
|
-
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
|
38
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
40
39
|
"@vue/test-utils": "2.4.4",
|
|
41
40
|
"eslint": "^8.55.0",
|
|
42
41
|
"eslint-config-prettier": "^9.1.0",
|
|
@@ -49,10 +48,10 @@
|
|
|
49
48
|
"postcss-preset-env": "^9.3.0",
|
|
50
49
|
"prettier": "^3.1.1",
|
|
51
50
|
"typescript": "^5.3.3",
|
|
52
|
-
"vite": "^5.
|
|
51
|
+
"vite": "^5.2.0",
|
|
53
52
|
"vite-plugin-dts": "^3.7.3",
|
|
54
53
|
"vitest": "^1.1.0",
|
|
55
|
-
"vue": "^3.4.
|
|
54
|
+
"vue": "^3.4.21",
|
|
56
55
|
"vue-eslint-parser": "^9.3.2"
|
|
57
56
|
},
|
|
58
57
|
"dependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
ref="
|
|
3
|
+
ref="tableContainerRef"
|
|
4
4
|
class="stk-table"
|
|
5
5
|
:class="{
|
|
6
6
|
virtual,
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
'--header-row-height': (props.headerRowHeight || props.rowHeight) + 'px',
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
'--highlight-color': props.highlightConfig.color && highlightFrom,
|
|
25
24
|
'--highlight-duration': props.highlightConfig.duration && props.highlightConfig.duration + 's',
|
|
25
|
+
'--highlight-easing': highlightSteps ? `steps(${highlightSteps})` : '',
|
|
26
26
|
},
|
|
27
27
|
]"
|
|
28
28
|
@scroll="onTableScroll"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
:style="{ height: dataSourceCopy.length * virtualScroll.rowHeight + 'px' }"
|
|
36
36
|
></div>
|
|
37
37
|
-->
|
|
38
|
-
<div v-
|
|
38
|
+
<div v-if="colResizable" ref="colResizeIndicatorRef" class="column-resize-indicator"></div>
|
|
39
39
|
<!-- 表格主体 -->
|
|
40
40
|
<table
|
|
41
41
|
class="stk-table-main"
|
|
@@ -85,6 +85,9 @@
|
|
|
85
85
|
>
|
|
86
86
|
<div class="table-header-cell-wrapper">
|
|
87
87
|
<component :is="col.customHeaderCell" v-if="col.customHeaderCell" :col="col" />
|
|
88
|
+
<template v-else-if="col.type === 'seq'">
|
|
89
|
+
<span class="table-header-title">{{ col.title }}</span>
|
|
90
|
+
</template>
|
|
88
91
|
<template v-else>
|
|
89
92
|
<slot name="tableHeader" :col="col">
|
|
90
93
|
<span class="table-header-title">{{ col.title }}</span>
|
|
@@ -143,16 +146,14 @@
|
|
|
143
146
|
></template>
|
|
144
147
|
</tr>
|
|
145
148
|
<tr
|
|
146
|
-
v-for="(row,
|
|
147
|
-
:
|
|
148
|
-
:
|
|
149
|
+
v-for="(row, rowIndex) in virtual_dataSourcePart"
|
|
150
|
+
:id="stkTableId + '-' + (rowKey ? rowKeyGen(row) : rowIndex)"
|
|
151
|
+
:key="rowKey ? rowKeyGen(row) : rowIndex"
|
|
152
|
+
:data-row-key="rowKey ? rowKeyGen(row) : rowIndex"
|
|
149
153
|
:class="{
|
|
150
154
|
active: rowKey ? rowKeyGen(row) === rowKeyGen(currentItem) : row === currentItem,
|
|
151
155
|
hover: rowKey ? rowKeyGen(row) === currentHover : row === currentHover,
|
|
152
|
-
[rowClassName(row,
|
|
153
|
-
}"
|
|
154
|
-
:style="{
|
|
155
|
-
backgroundColor: highlightRowStore[rowKeyGen(row)]?.bgc,
|
|
156
|
+
[rowClassName(row, rowIndex)]: true,
|
|
156
157
|
}"
|
|
157
158
|
@click="e => onRowClick(e, row)"
|
|
158
159
|
@dblclick="e => onRowDblclick(e, row)"
|
|
@@ -162,16 +163,34 @@
|
|
|
162
163
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
163
164
|
<td v-if="virtualX_on" class="virtual-x-left" style="padding: 0"></td>
|
|
164
165
|
<td
|
|
165
|
-
v-for="col in virtualX_columnPart"
|
|
166
|
+
v-for="(col, colIndex) in virtualX_columnPart"
|
|
166
167
|
:key="col.dataIndex"
|
|
167
168
|
:data-index="col.dataIndex"
|
|
168
|
-
:class="[
|
|
169
|
+
:class="[
|
|
170
|
+
col.className,
|
|
171
|
+
fixedColClassMap.get(colKeyGen(col)),
|
|
172
|
+
showOverflow ? 'text-overflow' : '',
|
|
173
|
+
col.type === 'seq' ? 'seq-column' : '',
|
|
174
|
+
]"
|
|
169
175
|
:style="cellStyleMap[TagType.TD].get(colKeyGen(col))"
|
|
170
176
|
@click="e => onCellClick(e, row, col)"
|
|
171
177
|
>
|
|
172
|
-
<component
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
<component
|
|
179
|
+
:is="col.customCell"
|
|
180
|
+
v-if="col.customCell"
|
|
181
|
+
:col="col"
|
|
182
|
+
:row="row"
|
|
183
|
+
:row-index="rowIndex"
|
|
184
|
+
:col-index="colIndex"
|
|
185
|
+
:cell-value="row[col.dataIndex]"
|
|
186
|
+
/>
|
|
187
|
+
<div v-else class="table-cell-wrapper" :title="!col.type ? row[col.dataIndex] : ''">
|
|
188
|
+
<template v-if="col.type === 'seq'">
|
|
189
|
+
{{ (props.seqConfig.startIndex || 0) + rowIndex + 1 }}
|
|
190
|
+
</template>
|
|
191
|
+
<template v-else>
|
|
192
|
+
{{ row[col.dataIndex] ?? getEmptyCellText(col, row) }}
|
|
193
|
+
</template>
|
|
175
194
|
</div>
|
|
176
195
|
</td>
|
|
177
196
|
</tr>
|
|
@@ -195,7 +214,7 @@
|
|
|
195
214
|
*/
|
|
196
215
|
import { CSSProperties, computed, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
197
216
|
import { DEFAULT_ROW_HEIGHT } from './const';
|
|
198
|
-
import { HighlightConfig, Order, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKeyProp } from './types/index';
|
|
217
|
+
import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKeyProp } from './types/index';
|
|
199
218
|
import { useAutoResize } from './useAutoResize';
|
|
200
219
|
import { useColResize } from './useColResize';
|
|
201
220
|
import { useFixedCol } from './useFixedCol';
|
|
@@ -204,10 +223,12 @@ import { useHighlight } from './useHighlight';
|
|
|
204
223
|
import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
|
|
205
224
|
import { useThDrag } from './useThDrag';
|
|
206
225
|
import { useVirtualScroll } from './useVirtualScroll';
|
|
207
|
-
import { getColWidth,
|
|
226
|
+
import { createStkTableId, getCalculatedColWidth, getColWidth, transformWidthToStr, howDeepTheHeader, tableSort } from './utils';
|
|
208
227
|
|
|
209
228
|
/** Generic stands for DataType */
|
|
210
229
|
type DT = any;
|
|
230
|
+
/** 自己生成实例id */
|
|
231
|
+
const stkTableId = createStkTableId();
|
|
211
232
|
/**
|
|
212
233
|
* props 不能放在单独的文件中。vue2.7 compiler 构建会出错。
|
|
213
234
|
*/
|
|
@@ -232,7 +253,7 @@ const props = withDefaults(
|
|
|
232
253
|
headerRowHeight?: number | null;
|
|
233
254
|
/** 虚拟滚动 */
|
|
234
255
|
virtual?: boolean;
|
|
235
|
-
/** x轴虚拟滚动
|
|
256
|
+
/** x轴虚拟滚动(必须设置列宽)*/
|
|
236
257
|
virtualX?: boolean;
|
|
237
258
|
/** 表格列配置 */
|
|
238
259
|
columns?: StkTableColumn<DT>[];
|
|
@@ -295,6 +316,8 @@ const props = withDefaults(
|
|
|
295
316
|
hideHeaderTitle?: boolean | string[];
|
|
296
317
|
/** 高亮配置 */
|
|
297
318
|
highlightConfig?: HighlightConfig;
|
|
319
|
+
/** 序号列配置 */
|
|
320
|
+
seqConfig?: SeqConfig;
|
|
298
321
|
}>(),
|
|
299
322
|
{
|
|
300
323
|
width: '',
|
|
@@ -333,6 +356,7 @@ const props = withDefaults(
|
|
|
333
356
|
}),
|
|
334
357
|
hideHeaderTitle: false,
|
|
335
358
|
highlightConfig: () => ({}),
|
|
359
|
+
seqConfig: () => ({}),
|
|
336
360
|
},
|
|
337
361
|
);
|
|
338
362
|
|
|
@@ -414,8 +438,8 @@ const emits = defineEmits<{
|
|
|
414
438
|
// empty(): void;
|
|
415
439
|
// }>();
|
|
416
440
|
|
|
417
|
-
const
|
|
418
|
-
const
|
|
441
|
+
const tableContainerRef = ref<HTMLDivElement>();
|
|
442
|
+
const colResizeIndicatorRef = ref<HTMLDivElement>();
|
|
419
443
|
/** 当前选中的一行*/
|
|
420
444
|
const currentItem = ref<DT | null>(null);
|
|
421
445
|
/**
|
|
@@ -424,7 +448,7 @@ const currentItem = ref<DT | null>(null);
|
|
|
424
448
|
*/
|
|
425
449
|
const currentItemKey = ref<any>(null);
|
|
426
450
|
/** 当前hover的行 */
|
|
427
|
-
const currentHover = ref<
|
|
451
|
+
const currentHover = ref<any | null>(null);
|
|
428
452
|
|
|
429
453
|
/** 排序的列dataIndex*/
|
|
430
454
|
let sortCol = ref<string | null>();
|
|
@@ -444,9 +468,9 @@ const sortSwitchOrder: Order[] = [null, 'desc', 'asc'];
|
|
|
444
468
|
* ]
|
|
445
469
|
* ```
|
|
446
470
|
*/
|
|
447
|
-
const tableHeaders =
|
|
471
|
+
const tableHeaders = shallowRef<StkTableColumn<DT>[][]>([]);
|
|
448
472
|
/** 若有多级表头时,最后一行的tableHeaders.内容是 props.columns 的引用集合 */
|
|
449
|
-
const tableHeaderLast =
|
|
473
|
+
const tableHeaderLast = shallowRef<StkTableColumn<DT>[]>([]);
|
|
450
474
|
|
|
451
475
|
const dataSourceCopy = shallowRef<DT[]>([...props.dataSource]);
|
|
452
476
|
|
|
@@ -469,8 +493,8 @@ const { isColResizing, onThResizeMouseDown } = useColResize({
|
|
|
469
493
|
props,
|
|
470
494
|
emits,
|
|
471
495
|
colKeyGen,
|
|
472
|
-
|
|
473
|
-
|
|
496
|
+
colResizeIndicatorRef,
|
|
497
|
+
tableContainerRef,
|
|
474
498
|
tableHeaderLast,
|
|
475
499
|
});
|
|
476
500
|
|
|
@@ -490,7 +514,7 @@ const {
|
|
|
490
514
|
initVirtualScrollX,
|
|
491
515
|
updateVirtualScrollY,
|
|
492
516
|
updateVirtualScrollX,
|
|
493
|
-
} = useVirtualScroll({
|
|
517
|
+
} = useVirtualScroll({ tableContainerRef, props, dataSourceCopy, tableHeaderLast, tableHeaders });
|
|
494
518
|
|
|
495
519
|
const { getFixedStyle } = useFixedStyle<DT>({
|
|
496
520
|
props,
|
|
@@ -504,26 +528,27 @@ const { getFixedStyle } = useFixedStyle<DT>({
|
|
|
504
528
|
/**
|
|
505
529
|
* 高亮行,高亮单元格
|
|
506
530
|
*/
|
|
507
|
-
const {
|
|
531
|
+
const { highlightSteps, setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, stkTableId, tableContainerRef });
|
|
508
532
|
|
|
509
533
|
if (props.autoResize) {
|
|
510
|
-
useAutoResize({
|
|
534
|
+
useAutoResize({ tableContainerRef, initVirtualScroll, props, debounceMs: 200 });
|
|
511
535
|
}
|
|
512
536
|
|
|
513
537
|
/** 键盘箭头滚动 */
|
|
514
|
-
useKeyboardArrowScroll(
|
|
538
|
+
useKeyboardArrowScroll(tableContainerRef, {
|
|
515
539
|
props,
|
|
516
540
|
scrollTo,
|
|
517
541
|
virtualScroll,
|
|
518
542
|
virtualScrollX,
|
|
519
543
|
tableHeaders,
|
|
544
|
+
virtual_on,
|
|
520
545
|
});
|
|
521
546
|
|
|
522
547
|
/** 固定列处理 */
|
|
523
548
|
const { fixedColClassMap, dealFixedColShadow, updateFixedShadow } = useFixedCol({
|
|
524
549
|
props,
|
|
525
550
|
colKeyGen,
|
|
526
|
-
|
|
551
|
+
tableContainerRef,
|
|
527
552
|
tableHeaders,
|
|
528
553
|
tableHeaderLast,
|
|
529
554
|
});
|
|
@@ -535,6 +560,10 @@ watch(
|
|
|
535
560
|
initVirtualScrollX();
|
|
536
561
|
},
|
|
537
562
|
);
|
|
563
|
+
watch(
|
|
564
|
+
() => props.virtualX,
|
|
565
|
+
() => dealColumns(),
|
|
566
|
+
);
|
|
538
567
|
|
|
539
568
|
dealColumns();
|
|
540
569
|
|
|
@@ -586,7 +615,6 @@ function dealDefaultSorter() {
|
|
|
586
615
|
function dealColumns() {
|
|
587
616
|
// reset
|
|
588
617
|
tableHeaders.value = [];
|
|
589
|
-
tableHeaderLast.value = [];
|
|
590
618
|
const copyColumn = props.columns; // do not deep clone
|
|
591
619
|
const deep = howDeepTheHeader(copyColumn);
|
|
592
620
|
const tempHeaderLast: StkTableColumn<DT>[] = [];
|
|
@@ -595,9 +623,8 @@ function dealColumns() {
|
|
|
595
623
|
console.error('多级表头不支持横向虚拟滚动');
|
|
596
624
|
}
|
|
597
625
|
|
|
598
|
-
// 展开columns
|
|
599
|
-
|
|
600
626
|
/**
|
|
627
|
+
* 展开columns
|
|
601
628
|
* @param arr
|
|
602
629
|
* @param depth 深度
|
|
603
630
|
* @param parent 父节点引用,用于构建双向链表。
|
|
@@ -640,10 +667,7 @@ function dealColumns() {
|
|
|
640
667
|
if (colSpan !== 1) {
|
|
641
668
|
col.colSpan = colSpan;
|
|
642
669
|
}
|
|
643
|
-
|
|
644
|
-
// 列赋值默认列宽。由于有些场景不需要设置width。
|
|
645
|
-
col.width = colWidth + 'px';
|
|
646
|
-
}
|
|
670
|
+
col.__WIDTH__ = colWidth; //记录计算的列宽
|
|
647
671
|
allChildrenLen += colChildrenLen;
|
|
648
672
|
allChildrenWidthSum += colWidth;
|
|
649
673
|
});
|
|
@@ -652,6 +676,8 @@ function dealColumns() {
|
|
|
652
676
|
|
|
653
677
|
flat(copyColumn, null);
|
|
654
678
|
|
|
679
|
+
// tableHeaders.value = [...tableHeaders.value];
|
|
680
|
+
|
|
655
681
|
tableHeaderLast.value = tempHeaderLast;
|
|
656
682
|
dealFixedColShadow();
|
|
657
683
|
}
|
|
@@ -684,16 +710,17 @@ const cellStyleMap = computed(() => {
|
|
|
684
710
|
tableHeaders.value.forEach((cols, depth) => {
|
|
685
711
|
cols.forEach(col => {
|
|
686
712
|
const colKey = colKeyGen(col);
|
|
687
|
-
const width =
|
|
713
|
+
const width = props.virtualX ? getCalculatedColWidth(col) + 'px' : transformWidthToStr(col.width);
|
|
688
714
|
const style: CSSProperties = {
|
|
689
715
|
width,
|
|
690
716
|
};
|
|
691
717
|
if (props.colResizable) {
|
|
718
|
+
// 如果要调整列宽,列宽必须固定。
|
|
692
719
|
style.minWidth = width;
|
|
693
720
|
style.maxWidth = width;
|
|
694
721
|
} else {
|
|
695
|
-
style.minWidth =
|
|
696
|
-
style.maxWidth =
|
|
722
|
+
style.minWidth = transformWidthToStr(col.minWidth) ?? width;
|
|
723
|
+
style.maxWidth = transformWidthToStr(col.maxWidth) ?? width;
|
|
697
724
|
}
|
|
698
725
|
|
|
699
726
|
const thStyle = {
|
|
@@ -904,9 +931,9 @@ function resetSorter() {
|
|
|
904
931
|
* @param left 传null 则不变动位置
|
|
905
932
|
*/
|
|
906
933
|
function scrollTo(top: number | null = 0, left: number | null = 0) {
|
|
907
|
-
if (!
|
|
908
|
-
if (top !== null)
|
|
909
|
-
if (left !== null)
|
|
934
|
+
if (!tableContainerRef.value) return;
|
|
935
|
+
if (top !== null) tableContainerRef.value.scrollTop = top;
|
|
936
|
+
if (left !== null) tableContainerRef.value.scrollLeft = left;
|
|
910
937
|
}
|
|
911
938
|
|
|
912
939
|
/** 获取当前状态的表格数据 */
|
package/src/StkTable/const.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const HIGHLIGHT_COLOR = {
|
|
|
12
12
|
/** 高亮持续时间 */
|
|
13
13
|
export const HIGHLIGHT_DURATION = 2000;
|
|
14
14
|
/** 高亮变更频率 */
|
|
15
|
-
export const HIGHLIGHT_FREQ =
|
|
15
|
+
export const HIGHLIGHT_FREQ = 1000 / 30;
|
|
16
16
|
|
|
17
17
|
/** 高亮行class */
|
|
18
18
|
export const HIGHLIGHT_ROW_CLASS = 'highlight-row';
|
|
@@ -30,3 +30,5 @@ try {
|
|
|
30
30
|
}
|
|
31
31
|
/** 是否兼容低版本模式 */
|
|
32
32
|
export const IS_LEGACY_MODE = _chromeVersion < 56;
|
|
33
|
+
|
|
34
|
+
export const STK_ID_PREFIX = 'stk';
|
package/src/StkTable/style.less
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
--bg-border-left: linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
|
|
25
25
|
--highlight-color: #71a2fd;
|
|
26
26
|
--highlight-duration: 2s;
|
|
27
|
+
--highlight-easing: linear;
|
|
27
28
|
/* 斑马纹颜色*/
|
|
28
29
|
--stripe-bgc: #fafafc;
|
|
29
30
|
|
|
@@ -212,7 +213,7 @@
|
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
&.highlight-cell {
|
|
215
|
-
animation: stk-table-dim var(--highlight-duration)
|
|
216
|
+
animation: stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
&.text-overflow {
|
|
@@ -222,6 +223,10 @@
|
|
|
222
223
|
text-overflow: ellipsis;
|
|
223
224
|
}
|
|
224
225
|
}
|
|
226
|
+
|
|
227
|
+
&.seq-column{
|
|
228
|
+
text-align: center;
|
|
229
|
+
}
|
|
225
230
|
}
|
|
226
231
|
|
|
227
232
|
/*固定列阴影-左*/
|
|
@@ -349,15 +354,14 @@
|
|
|
349
354
|
tr {
|
|
350
355
|
background-color: var(--td-bgc);
|
|
351
356
|
height: var(--row-height);
|
|
357
|
+
/** 一行分层,有利于高亮行重绘*/
|
|
358
|
+
transform: translateZ(0);
|
|
352
359
|
|
|
353
360
|
/* td inherit tr bgc*/
|
|
354
361
|
&.highlight-row {
|
|
355
|
-
animation: stk-table-dim var(--highlight-duration)
|
|
362
|
+
animation: stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
356
363
|
}
|
|
357
|
-
|
|
358
|
-
/* &.highlight-row-transition {
|
|
359
|
-
transition: background-color v-bind(highlightStepDuration) linear;
|
|
360
|
-
}*/
|
|
364
|
+
|
|
361
365
|
|
|
362
366
|
&.hover,
|
|
363
367
|
&:hover {
|
|
@@ -10,6 +10,11 @@ export type CustomHeaderCellFunc<T extends Record<string, any>> = (props: { col:
|
|
|
10
10
|
|
|
11
11
|
/** 表格列配置 */
|
|
12
12
|
export type StkTableColumn<T extends Record<string, any>> = {
|
|
13
|
+
/**
|
|
14
|
+
* 列类型
|
|
15
|
+
* - seq 序号列
|
|
16
|
+
*/
|
|
17
|
+
type?: 'seq';
|
|
13
18
|
/** 取值id */
|
|
14
19
|
dataIndex: keyof T & string;
|
|
15
20
|
/** 表头文字 */
|
|
@@ -57,6 +62,8 @@ export type StkTableColumn<T extends Record<string, any>> = {
|
|
|
57
62
|
children?: StkTableColumn<T>[];
|
|
58
63
|
/** 父节点引用 */
|
|
59
64
|
__PARENT__?: StkTableColumn<T> | null;
|
|
65
|
+
/** 保存计算的宽度。横向虚拟滚动用。 */
|
|
66
|
+
__WIDTH__?: number;
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
export type SortOption<T extends Record<string, any>> = Pick<StkTableColumn<T>, 'sorter' | 'dataIndex' | 'sortField' | 'sortType'>;
|
|
@@ -104,11 +111,12 @@ export const enum TagType {
|
|
|
104
111
|
export type HighlightConfig = {
|
|
105
112
|
/** 高亮持续时间(s) */
|
|
106
113
|
duration?: number;
|
|
107
|
-
/**
|
|
108
|
-
color?: {
|
|
109
|
-
light?: { from: string; to: string };
|
|
110
|
-
dark?: { from: string; to: string };
|
|
111
|
-
};
|
|
112
|
-
/** 高亮帧率(虚拟滚动生效) */
|
|
114
|
+
/** 高亮帧率 */
|
|
113
115
|
fps?: number;
|
|
114
116
|
};
|
|
117
|
+
|
|
118
|
+
/** 序号列配置 */
|
|
119
|
+
export type SeqConfig = {
|
|
120
|
+
/** 序号列起始下标 用于适配分页 */
|
|
121
|
+
startIndex?: number;
|
|
122
|
+
};
|
|
@@ -2,7 +2,7 @@ import { Ref, onBeforeUnmount, onMounted, watch } from 'vue';
|
|
|
2
2
|
|
|
3
3
|
type Options = {
|
|
4
4
|
props: any;
|
|
5
|
-
|
|
5
|
+
tableContainerRef: Ref<HTMLElement | undefined>;
|
|
6
6
|
initVirtualScroll: () => void;
|
|
7
7
|
/** 防抖延时 */
|
|
8
8
|
debounceMs: number;
|
|
@@ -11,7 +11,7 @@ type Options = {
|
|
|
11
11
|
* 窗口变化自动重置虚拟滚动
|
|
12
12
|
* @param param0
|
|
13
13
|
*/
|
|
14
|
-
export function useAutoResize({
|
|
14
|
+
export function useAutoResize({ tableContainerRef, initVirtualScroll, props, debounceMs }: Options) {
|
|
15
15
|
let resizeObserver: ResizeObserver | null = null;
|
|
16
16
|
|
|
17
17
|
onMounted(() => {
|
|
@@ -24,9 +24,9 @@ export function useAutoResize({ tableContainer, initVirtualScroll, props, deboun
|
|
|
24
24
|
|
|
25
25
|
function initResizeObserver() {
|
|
26
26
|
if (window.ResizeObserver) {
|
|
27
|
-
if (!
|
|
27
|
+
if (!tableContainerRef.value) {
|
|
28
28
|
const watchDom = watch(
|
|
29
|
-
() =>
|
|
29
|
+
() => tableContainerRef,
|
|
30
30
|
() => {
|
|
31
31
|
initResizeObserver();
|
|
32
32
|
watchDom();
|
|
@@ -35,7 +35,7 @@ export function useAutoResize({ tableContainer, initVirtualScroll, props, deboun
|
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
resizeObserver = new ResizeObserver(resizeCallback);
|
|
38
|
-
resizeObserver.observe(
|
|
38
|
+
resizeObserver.observe(tableContainerRef.value);
|
|
39
39
|
} else {
|
|
40
40
|
window.addEventListener('resize', resizeCallback);
|
|
41
41
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Ref, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
1
|
+
import { Ref, ShallowRef, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
2
2
|
import { StkTableColumn } from './types';
|
|
3
|
-
import {
|
|
3
|
+
import { getCalculatedColWidth } from './utils';
|
|
4
4
|
|
|
5
5
|
type ColResizeState<DT extends Record<string, any>> = {
|
|
6
6
|
/** 当前被拖动的列*/
|
|
@@ -18,17 +18,17 @@ type ColResizeState<DT extends Record<string, any>> = {
|
|
|
18
18
|
type Params<DT extends Record<string, any>> = {
|
|
19
19
|
props: any;
|
|
20
20
|
emits: any;
|
|
21
|
-
|
|
22
|
-
tableHeaderLast:
|
|
23
|
-
|
|
21
|
+
tableContainerRef: Ref<HTMLElement | undefined>;
|
|
22
|
+
tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
|
|
23
|
+
colResizeIndicatorRef: Ref<HTMLElement | undefined>;
|
|
24
24
|
colKeyGen: (p: any) => string;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
/** 列宽拖动 */
|
|
28
28
|
export function useColResize<DT extends Record<string, any>>({
|
|
29
|
-
|
|
29
|
+
tableContainerRef,
|
|
30
30
|
tableHeaderLast,
|
|
31
|
-
|
|
31
|
+
colResizeIndicatorRef,
|
|
32
32
|
props,
|
|
33
33
|
emits,
|
|
34
34
|
colKeyGen,
|
|
@@ -71,12 +71,12 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
71
71
|
* @param isPrev 是否要上一列
|
|
72
72
|
*/
|
|
73
73
|
function onThResizeMouseDown(e: MouseEvent, col: StkTableColumn<DT>, isPrev = false) {
|
|
74
|
-
if (!
|
|
74
|
+
if (!tableContainerRef.value) return;
|
|
75
75
|
e.stopPropagation();
|
|
76
76
|
e.preventDefault();
|
|
77
77
|
const { clientX } = e;
|
|
78
|
-
const { scrollLeft, scrollTop } =
|
|
79
|
-
const { left } =
|
|
78
|
+
const { scrollLeft, scrollTop } = tableContainerRef.value;
|
|
79
|
+
const { left } = tableContainerRef.value.getBoundingClientRect();
|
|
80
80
|
/** 列下标 */
|
|
81
81
|
let colIndex = tableHeaderLast.value.findIndex(it => colKeyGen(it) === colKeyGen(col));
|
|
82
82
|
if (isPrev) {
|
|
@@ -97,8 +97,8 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// 展示指示线,更新其位置
|
|
100
|
-
if (
|
|
101
|
-
const style =
|
|
100
|
+
if (colResizeIndicatorRef.value) {
|
|
101
|
+
const style = colResizeIndicatorRef.value.style;
|
|
102
102
|
style.display = 'block';
|
|
103
103
|
style.left = offsetTableX + 'px';
|
|
104
104
|
style.top = scrollTop + 'px';
|
|
@@ -115,15 +115,15 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
115
115
|
const { lastCol, startX, startOffsetTableX } = colResizeState;
|
|
116
116
|
const { clientX } = e;
|
|
117
117
|
let moveX = clientX - startX;
|
|
118
|
-
const currentColWidth =
|
|
118
|
+
const currentColWidth = getCalculatedColWidth(lastCol);
|
|
119
119
|
// 移动量不小于最小列宽
|
|
120
120
|
if (currentColWidth + moveX < props.colMinWidth) {
|
|
121
121
|
moveX = -currentColWidth;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const offsetTableX = startOffsetTableX + moveX;
|
|
125
|
-
if (!
|
|
126
|
-
|
|
125
|
+
if (!colResizeIndicatorRef.value) return;
|
|
126
|
+
colResizeIndicatorRef.value.style.left = offsetTableX + 'px';
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
@@ -136,7 +136,7 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
136
136
|
const moveX = clientX - startX;
|
|
137
137
|
|
|
138
138
|
// 移动量不小于最小列宽
|
|
139
|
-
let width =
|
|
139
|
+
let width = getCalculatedColWidth(lastCol) + moveX;
|
|
140
140
|
if (width < props.colMinWidth) width = props.colMinWidth;
|
|
141
141
|
|
|
142
142
|
const curCol = tableHeaderLast.value.find(it => colKeyGen(it) === colKeyGen(lastCol));
|
|
@@ -146,8 +146,8 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
146
146
|
emits('update:columns', [...props.columns]);
|
|
147
147
|
|
|
148
148
|
// 隐藏指示线
|
|
149
|
-
if (
|
|
150
|
-
const style =
|
|
149
|
+
if (colResizeIndicatorRef.value) {
|
|
150
|
+
const style = colResizeIndicatorRef.value.style;
|
|
151
151
|
style.display = 'none';
|
|
152
152
|
style.left = '0';
|
|
153
153
|
style.top = '0';
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { computed, ref, Ref, shallowRef } from 'vue';
|
|
1
|
+
import { computed, ref, Ref, ShallowRef, shallowRef } from 'vue';
|
|
2
2
|
import { StkTableColumn } from './types';
|
|
3
3
|
|
|
4
4
|
type Params<T extends Record<string, any>> = {
|
|
5
5
|
props: any;
|
|
6
6
|
colKeyGen: (col: StkTableColumn<T>) => string;
|
|
7
|
-
tableHeaders:
|
|
8
|
-
tableHeaderLast:
|
|
9
|
-
|
|
7
|
+
tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
|
|
8
|
+
tableHeaderLast: ShallowRef<StkTableColumn<T>[]>;
|
|
9
|
+
tableContainerRef: Ref<HTMLDivElement | undefined>;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* 固定列处理
|
|
14
14
|
* @returns
|
|
15
15
|
*/
|
|
16
|
-
export function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, tableHeaders, tableHeaderLast,
|
|
16
|
+
export function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, tableHeaders, tableHeaderLast, tableContainerRef }: Params<DT>) {
|
|
17
17
|
/** 固定列阴影 */
|
|
18
18
|
const fixedShadow = ref<{
|
|
19
19
|
/** 是否展示左侧固定列阴影 */
|
|
@@ -82,7 +82,7 @@ export function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen,
|
|
|
82
82
|
/** 滚动条变化时,更新需要展示阴影的列 */
|
|
83
83
|
function updateFixedShadow() {
|
|
84
84
|
if (!props.fixedColShadow) return;
|
|
85
|
-
const { clientWidth, scrollWidth, scrollLeft } =
|
|
85
|
+
const { clientWidth, scrollWidth, scrollLeft } = tableContainerRef.value as HTMLDivElement;
|
|
86
86
|
fixedShadow.value.showL = Boolean(scrollLeft);
|
|
87
87
|
fixedShadow.value.showR = Math.abs(scrollWidth - scrollLeft - clientWidth) > 0.5;
|
|
88
88
|
}
|