stk-table-vue 0.2.8 → 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 +97 -10
- package/lib/src/StkTable/StkTable.vue.d.ts +27 -4
- package/lib/src/StkTable/const.d.ts +13 -8
- package/lib/src/StkTable/types/index.d.ts +19 -0
- 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 +16 -16
- 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 +349 -199
- package/lib/style.css +8 -2
- package/package.json +4 -5
- package/src/StkTable/StkTable.vue +81 -47
- package/src/StkTable/const.ts +15 -8
- package/src/StkTable/style.less +11 -6
- package/src/StkTable/types/index.ts +21 -0
- 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 +15 -12
- package/src/StkTable/useHighlight.ts +267 -109
- package/src/StkTable/useKeyboardArrowScroll.ts +19 -7
- package/src/StkTable/useVirtualScroll.ts +25 -17
- package/src/StkTable/utils.ts +27 -9
- package/src/vite-env.d.ts +4 -0
package/lib/style.css
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
--bg-border-bottom:linear-gradient(0deg, var(--border-color) var(--border-width), transparent var(--border-width));
|
|
21
21
|
--bg-border-left:linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
|
|
22
22
|
--highlight-color:#71a2fd;
|
|
23
|
+
--highlight-duration:2s;
|
|
24
|
+
--highlight-easing:linear;
|
|
23
25
|
--stripe-bgc:#fafafc;
|
|
24
26
|
--sort-arrow-color:#757699;
|
|
25
27
|
--sort-arrow-hover-color:#8f90b5;
|
|
@@ -148,13 +150,16 @@
|
|
|
148
150
|
background-color:inherit;
|
|
149
151
|
}
|
|
150
152
|
.stk-table .stk-table-main td.highlight-cell{
|
|
151
|
-
animation:stk-table-dim
|
|
153
|
+
animation:stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
152
154
|
}
|
|
153
155
|
.stk-table .stk-table-main td.text-overflow .table-cell-wrapper{
|
|
154
156
|
white-space:nowrap;
|
|
155
157
|
overflow:hidden;
|
|
156
158
|
text-overflow:ellipsis;
|
|
157
159
|
}
|
|
160
|
+
.stk-table .stk-table-main td.seq-column{
|
|
161
|
+
text-align:center;
|
|
162
|
+
}
|
|
158
163
|
.stk-table .stk-table-main .fixed-cell--left{
|
|
159
164
|
--shadow-rotate:90deg;
|
|
160
165
|
}
|
|
@@ -245,9 +250,10 @@
|
|
|
245
250
|
.stk-table .stk-table-main tbody tr{
|
|
246
251
|
background-color:var(--td-bgc);
|
|
247
252
|
height:var(--row-height);
|
|
253
|
+
transform:translateZ(0);
|
|
248
254
|
}
|
|
249
255
|
.stk-table .stk-table-main tbody tr.highlight-row{
|
|
250
|
-
animation:stk-table-dim
|
|
256
|
+
animation:stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
251
257
|
}
|
|
252
258
|
.stk-table .stk-table-main tbody tr.hover,
|
|
253
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,
|
|
@@ -15,12 +15,16 @@
|
|
|
15
15
|
'border-body-v': props.bordered === 'body-v',
|
|
16
16
|
stripe: props.stripe,
|
|
17
17
|
}"
|
|
18
|
-
:style="
|
|
18
|
+
:style="[
|
|
19
19
|
virtual && {
|
|
20
20
|
'--row-height': virtualScroll.rowHeight + 'px',
|
|
21
21
|
'--header-row-height': (props.headerRowHeight || props.rowHeight) + 'px',
|
|
22
|
-
}
|
|
23
|
-
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
'--highlight-duration': props.highlightConfig.duration && props.highlightConfig.duration + 's',
|
|
25
|
+
'--highlight-easing': highlightSteps ? `steps(${highlightSteps})` : '',
|
|
26
|
+
},
|
|
27
|
+
]"
|
|
24
28
|
@scroll="onTableScroll"
|
|
25
29
|
@wheel="onTableWheel"
|
|
26
30
|
>
|
|
@@ -31,7 +35,7 @@
|
|
|
31
35
|
:style="{ height: dataSourceCopy.length * virtualScroll.rowHeight + 'px' }"
|
|
32
36
|
></div>
|
|
33
37
|
-->
|
|
34
|
-
<div v-
|
|
38
|
+
<div v-if="colResizable" ref="colResizeIndicatorRef" class="column-resize-indicator"></div>
|
|
35
39
|
<!-- 表格主体 -->
|
|
36
40
|
<table
|
|
37
41
|
class="stk-table-main"
|
|
@@ -81,6 +85,9 @@
|
|
|
81
85
|
>
|
|
82
86
|
<div class="table-header-cell-wrapper">
|
|
83
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>
|
|
84
91
|
<template v-else>
|
|
85
92
|
<slot name="tableHeader" :col="col">
|
|
86
93
|
<span class="table-header-title">{{ col.title }}</span>
|
|
@@ -139,16 +146,14 @@
|
|
|
139
146
|
></template>
|
|
140
147
|
</tr>
|
|
141
148
|
<tr
|
|
142
|
-
v-for="(row,
|
|
143
|
-
:
|
|
144
|
-
:
|
|
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"
|
|
145
153
|
:class="{
|
|
146
154
|
active: rowKey ? rowKeyGen(row) === rowKeyGen(currentItem) : row === currentItem,
|
|
147
155
|
hover: rowKey ? rowKeyGen(row) === currentHover : row === currentHover,
|
|
148
|
-
[rowClassName(row,
|
|
149
|
-
}"
|
|
150
|
-
:style="{
|
|
151
|
-
backgroundColor: highlightRowStore[rowKeyGen(row)]?.bgc,
|
|
156
|
+
[rowClassName(row, rowIndex)]: true,
|
|
152
157
|
}"
|
|
153
158
|
@click="e => onRowClick(e, row)"
|
|
154
159
|
@dblclick="e => onRowDblclick(e, row)"
|
|
@@ -158,16 +163,34 @@
|
|
|
158
163
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
159
164
|
<td v-if="virtualX_on" class="virtual-x-left" style="padding: 0"></td>
|
|
160
165
|
<td
|
|
161
|
-
v-for="col in virtualX_columnPart"
|
|
166
|
+
v-for="(col, colIndex) in virtualX_columnPart"
|
|
162
167
|
:key="col.dataIndex"
|
|
163
168
|
:data-index="col.dataIndex"
|
|
164
|
-
:class="[
|
|
169
|
+
:class="[
|
|
170
|
+
col.className,
|
|
171
|
+
fixedColClassMap.get(colKeyGen(col)),
|
|
172
|
+
showOverflow ? 'text-overflow' : '',
|
|
173
|
+
col.type === 'seq' ? 'seq-column' : '',
|
|
174
|
+
]"
|
|
165
175
|
:style="cellStyleMap[TagType.TD].get(colKeyGen(col))"
|
|
166
176
|
@click="e => onCellClick(e, row, col)"
|
|
167
177
|
>
|
|
168
|
-
<component
|
|
169
|
-
|
|
170
|
-
|
|
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>
|
|
171
194
|
</div>
|
|
172
195
|
</td>
|
|
173
196
|
</tr>
|
|
@@ -190,8 +213,8 @@
|
|
|
190
213
|
* [] highlight-row 颜色不能恢复到active的颜色
|
|
191
214
|
*/
|
|
192
215
|
import { CSSProperties, computed, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
193
|
-
import {
|
|
194
|
-
import { Order, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKeyProp } from './types/index';
|
|
216
|
+
import { DEFAULT_ROW_HEIGHT } from './const';
|
|
217
|
+
import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKeyProp } from './types/index';
|
|
195
218
|
import { useAutoResize } from './useAutoResize';
|
|
196
219
|
import { useColResize } from './useColResize';
|
|
197
220
|
import { useFixedCol } from './useFixedCol';
|
|
@@ -200,10 +223,12 @@ import { useHighlight } from './useHighlight';
|
|
|
200
223
|
import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
|
|
201
224
|
import { useThDrag } from './useThDrag';
|
|
202
225
|
import { useVirtualScroll } from './useVirtualScroll';
|
|
203
|
-
import { getColWidth,
|
|
226
|
+
import { createStkTableId, getCalculatedColWidth, getColWidth, transformWidthToStr, howDeepTheHeader, tableSort } from './utils';
|
|
204
227
|
|
|
205
228
|
/** Generic stands for DataType */
|
|
206
229
|
type DT = any;
|
|
230
|
+
/** 自己生成实例id */
|
|
231
|
+
const stkTableId = createStkTableId();
|
|
207
232
|
/**
|
|
208
233
|
* props 不能放在单独的文件中。vue2.7 compiler 构建会出错。
|
|
209
234
|
*/
|
|
@@ -228,7 +253,7 @@ const props = withDefaults(
|
|
|
228
253
|
headerRowHeight?: number | null;
|
|
229
254
|
/** 虚拟滚动 */
|
|
230
255
|
virtual?: boolean;
|
|
231
|
-
/** x轴虚拟滚动
|
|
256
|
+
/** x轴虚拟滚动(必须设置列宽)*/
|
|
232
257
|
virtualX?: boolean;
|
|
233
258
|
/** 表格列配置 */
|
|
234
259
|
columns?: StkTableColumn<DT>[];
|
|
@@ -289,6 +314,10 @@ const props = withDefaults(
|
|
|
289
314
|
sortConfig?: SortConfig<DT>;
|
|
290
315
|
/** 隐藏头部title。可传入dataIndex数组 */
|
|
291
316
|
hideHeaderTitle?: boolean | string[];
|
|
317
|
+
/** 高亮配置 */
|
|
318
|
+
highlightConfig?: HighlightConfig;
|
|
319
|
+
/** 序号列配置 */
|
|
320
|
+
seqConfig?: SeqConfig;
|
|
292
321
|
}>(),
|
|
293
322
|
{
|
|
294
323
|
width: '',
|
|
@@ -298,7 +327,7 @@ const props = withDefaults(
|
|
|
298
327
|
maxWidth: '',
|
|
299
328
|
headless: false,
|
|
300
329
|
theme: 'light',
|
|
301
|
-
rowHeight:
|
|
330
|
+
rowHeight: DEFAULT_ROW_HEIGHT,
|
|
302
331
|
headerRowHeight: null,
|
|
303
332
|
virtual: false,
|
|
304
333
|
virtualX: false,
|
|
@@ -326,6 +355,8 @@ const props = withDefaults(
|
|
|
326
355
|
stringLocaleCompare: true,
|
|
327
356
|
}),
|
|
328
357
|
hideHeaderTitle: false,
|
|
358
|
+
highlightConfig: () => ({}),
|
|
359
|
+
seqConfig: () => ({}),
|
|
329
360
|
},
|
|
330
361
|
);
|
|
331
362
|
|
|
@@ -407,8 +438,8 @@ const emits = defineEmits<{
|
|
|
407
438
|
// empty(): void;
|
|
408
439
|
// }>();
|
|
409
440
|
|
|
410
|
-
const
|
|
411
|
-
const
|
|
441
|
+
const tableContainerRef = ref<HTMLDivElement>();
|
|
442
|
+
const colResizeIndicatorRef = ref<HTMLDivElement>();
|
|
412
443
|
/** 当前选中的一行*/
|
|
413
444
|
const currentItem = ref<DT | null>(null);
|
|
414
445
|
/**
|
|
@@ -417,7 +448,7 @@ const currentItem = ref<DT | null>(null);
|
|
|
417
448
|
*/
|
|
418
449
|
const currentItemKey = ref<any>(null);
|
|
419
450
|
/** 当前hover的行 */
|
|
420
|
-
const currentHover = ref<
|
|
451
|
+
const currentHover = ref<any | null>(null);
|
|
421
452
|
|
|
422
453
|
/** 排序的列dataIndex*/
|
|
423
454
|
let sortCol = ref<string | null>();
|
|
@@ -437,9 +468,9 @@ const sortSwitchOrder: Order[] = [null, 'desc', 'asc'];
|
|
|
437
468
|
* ]
|
|
438
469
|
* ```
|
|
439
470
|
*/
|
|
440
|
-
const tableHeaders =
|
|
471
|
+
const tableHeaders = shallowRef<StkTableColumn<DT>[][]>([]);
|
|
441
472
|
/** 若有多级表头时,最后一行的tableHeaders.内容是 props.columns 的引用集合 */
|
|
442
|
-
const tableHeaderLast =
|
|
473
|
+
const tableHeaderLast = shallowRef<StkTableColumn<DT>[]>([]);
|
|
443
474
|
|
|
444
475
|
const dataSourceCopy = shallowRef<DT[]>([...props.dataSource]);
|
|
445
476
|
|
|
@@ -462,8 +493,8 @@ const { isColResizing, onThResizeMouseDown } = useColResize({
|
|
|
462
493
|
props,
|
|
463
494
|
emits,
|
|
464
495
|
colKeyGen,
|
|
465
|
-
|
|
466
|
-
|
|
496
|
+
colResizeIndicatorRef,
|
|
497
|
+
tableContainerRef,
|
|
467
498
|
tableHeaderLast,
|
|
468
499
|
});
|
|
469
500
|
|
|
@@ -483,7 +514,7 @@ const {
|
|
|
483
514
|
initVirtualScrollX,
|
|
484
515
|
updateVirtualScrollY,
|
|
485
516
|
updateVirtualScrollX,
|
|
486
|
-
} = useVirtualScroll({
|
|
517
|
+
} = useVirtualScroll({ tableContainerRef, props, dataSourceCopy, tableHeaderLast, tableHeaders });
|
|
487
518
|
|
|
488
519
|
const { getFixedStyle } = useFixedStyle<DT>({
|
|
489
520
|
props,
|
|
@@ -497,26 +528,27 @@ const { getFixedStyle } = useFixedStyle<DT>({
|
|
|
497
528
|
/**
|
|
498
529
|
* 高亮行,高亮单元格
|
|
499
530
|
*/
|
|
500
|
-
const {
|
|
531
|
+
const { highlightSteps, setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, stkTableId, tableContainerRef });
|
|
501
532
|
|
|
502
533
|
if (props.autoResize) {
|
|
503
|
-
useAutoResize({
|
|
534
|
+
useAutoResize({ tableContainerRef, initVirtualScroll, props, debounceMs: 200 });
|
|
504
535
|
}
|
|
505
536
|
|
|
506
537
|
/** 键盘箭头滚动 */
|
|
507
|
-
useKeyboardArrowScroll(
|
|
538
|
+
useKeyboardArrowScroll(tableContainerRef, {
|
|
508
539
|
props,
|
|
509
540
|
scrollTo,
|
|
510
541
|
virtualScroll,
|
|
511
542
|
virtualScrollX,
|
|
512
543
|
tableHeaders,
|
|
544
|
+
virtual_on,
|
|
513
545
|
});
|
|
514
546
|
|
|
515
547
|
/** 固定列处理 */
|
|
516
548
|
const { fixedColClassMap, dealFixedColShadow, updateFixedShadow } = useFixedCol({
|
|
517
549
|
props,
|
|
518
550
|
colKeyGen,
|
|
519
|
-
|
|
551
|
+
tableContainerRef,
|
|
520
552
|
tableHeaders,
|
|
521
553
|
tableHeaderLast,
|
|
522
554
|
});
|
|
@@ -528,6 +560,10 @@ watch(
|
|
|
528
560
|
initVirtualScrollX();
|
|
529
561
|
},
|
|
530
562
|
);
|
|
563
|
+
watch(
|
|
564
|
+
() => props.virtualX,
|
|
565
|
+
() => dealColumns(),
|
|
566
|
+
);
|
|
531
567
|
|
|
532
568
|
dealColumns();
|
|
533
569
|
|
|
@@ -579,7 +615,6 @@ function dealDefaultSorter() {
|
|
|
579
615
|
function dealColumns() {
|
|
580
616
|
// reset
|
|
581
617
|
tableHeaders.value = [];
|
|
582
|
-
tableHeaderLast.value = [];
|
|
583
618
|
const copyColumn = props.columns; // do not deep clone
|
|
584
619
|
const deep = howDeepTheHeader(copyColumn);
|
|
585
620
|
const tempHeaderLast: StkTableColumn<DT>[] = [];
|
|
@@ -588,9 +623,8 @@ function dealColumns() {
|
|
|
588
623
|
console.error('多级表头不支持横向虚拟滚动');
|
|
589
624
|
}
|
|
590
625
|
|
|
591
|
-
// 展开columns
|
|
592
|
-
|
|
593
626
|
/**
|
|
627
|
+
* 展开columns
|
|
594
628
|
* @param arr
|
|
595
629
|
* @param depth 深度
|
|
596
630
|
* @param parent 父节点引用,用于构建双向链表。
|
|
@@ -633,10 +667,7 @@ function dealColumns() {
|
|
|
633
667
|
if (colSpan !== 1) {
|
|
634
668
|
col.colSpan = colSpan;
|
|
635
669
|
}
|
|
636
|
-
|
|
637
|
-
// 列赋值默认列宽。由于有些场景不需要设置width。
|
|
638
|
-
col.width = colWidth + 'px';
|
|
639
|
-
}
|
|
670
|
+
col.__WIDTH__ = colWidth; //记录计算的列宽
|
|
640
671
|
allChildrenLen += colChildrenLen;
|
|
641
672
|
allChildrenWidthSum += colWidth;
|
|
642
673
|
});
|
|
@@ -645,6 +676,8 @@ function dealColumns() {
|
|
|
645
676
|
|
|
646
677
|
flat(copyColumn, null);
|
|
647
678
|
|
|
679
|
+
// tableHeaders.value = [...tableHeaders.value];
|
|
680
|
+
|
|
648
681
|
tableHeaderLast.value = tempHeaderLast;
|
|
649
682
|
dealFixedColShadow();
|
|
650
683
|
}
|
|
@@ -677,16 +710,17 @@ const cellStyleMap = computed(() => {
|
|
|
677
710
|
tableHeaders.value.forEach((cols, depth) => {
|
|
678
711
|
cols.forEach(col => {
|
|
679
712
|
const colKey = colKeyGen(col);
|
|
680
|
-
const width =
|
|
713
|
+
const width = props.virtualX ? getCalculatedColWidth(col) + 'px' : transformWidthToStr(col.width);
|
|
681
714
|
const style: CSSProperties = {
|
|
682
715
|
width,
|
|
683
716
|
};
|
|
684
717
|
if (props.colResizable) {
|
|
718
|
+
// 如果要调整列宽,列宽必须固定。
|
|
685
719
|
style.minWidth = width;
|
|
686
720
|
style.maxWidth = width;
|
|
687
721
|
} else {
|
|
688
|
-
style.minWidth =
|
|
689
|
-
style.maxWidth =
|
|
722
|
+
style.minWidth = transformWidthToStr(col.minWidth) ?? width;
|
|
723
|
+
style.maxWidth = transformWidthToStr(col.maxWidth) ?? width;
|
|
690
724
|
}
|
|
691
725
|
|
|
692
726
|
const thStyle = {
|
|
@@ -897,9 +931,9 @@ function resetSorter() {
|
|
|
897
931
|
* @param left 传null 则不变动位置
|
|
898
932
|
*/
|
|
899
933
|
function scrollTo(top: number | null = 0, left: number | null = 0) {
|
|
900
|
-
if (!
|
|
901
|
-
if (top !== null)
|
|
902
|
-
if (left !== null)
|
|
934
|
+
if (!tableContainerRef.value) return;
|
|
935
|
+
if (top !== null) tableContainerRef.value.scrollTop = top;
|
|
936
|
+
if (left !== null) tableContainerRef.value.scrollLeft = left;
|
|
903
937
|
}
|
|
904
938
|
|
|
905
939
|
/** 获取当前状态的表格数据 */
|
package/src/StkTable/const.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const DEFAULT_COL_WIDTH = '100';
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
3
|
+
export const DEFAULT_TABLE_HEIGHT = 100;
|
|
4
|
+
export const DEFAULT_TABLE_WIDTH = 200;
|
|
5
|
+
export const DEFAULT_ROW_HEIGHT = 28;
|
|
6
6
|
|
|
7
7
|
/** 高亮背景色 */
|
|
8
|
-
export const
|
|
8
|
+
export const HIGHLIGHT_COLOR = {
|
|
9
9
|
light: { from: '#71a2fd', to: '#fff' },
|
|
10
10
|
dark: { from: '#1e4c99', to: '#181c21' },
|
|
11
11
|
};
|
|
12
12
|
/** 高亮持续时间 */
|
|
13
|
-
export const
|
|
13
|
+
export const HIGHLIGHT_DURATION = 2000;
|
|
14
14
|
/** 高亮变更频率 */
|
|
15
|
-
export const
|
|
15
|
+
export const HIGHLIGHT_FREQ = 1000 / 30;
|
|
16
|
+
|
|
17
|
+
/** 高亮行class */
|
|
18
|
+
export const HIGHLIGHT_ROW_CLASS = 'highlight-row';
|
|
19
|
+
/** 高连单元格class */
|
|
20
|
+
export const HIGHLIGHT_CELL_CLASS = 'highlight-cell';
|
|
16
21
|
|
|
17
22
|
let _chromeVersion = 0;
|
|
18
23
|
try {
|
|
@@ -24,4 +29,6 @@ try {
|
|
|
24
29
|
console.error('Cannot get Chrome version', e);
|
|
25
30
|
}
|
|
26
31
|
/** 是否兼容低版本模式 */
|
|
27
|
-
export const
|
|
32
|
+
export const IS_LEGACY_MODE = _chromeVersion < 56;
|
|
33
|
+
|
|
34
|
+
export const STK_ID_PREFIX = 'stk';
|
package/src/StkTable/style.less
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
--bg-border-bottom: linear-gradient(0deg, var(--border-color) var(--border-width), transparent var(--border-width));
|
|
24
24
|
--bg-border-left: linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
|
|
25
25
|
--highlight-color: #71a2fd;
|
|
26
|
+
--highlight-duration: 2s;
|
|
27
|
+
--highlight-easing: linear;
|
|
26
28
|
/* 斑马纹颜色*/
|
|
27
29
|
--stripe-bgc: #fafafc;
|
|
28
30
|
|
|
@@ -211,7 +213,7 @@
|
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
&.highlight-cell {
|
|
214
|
-
animation: stk-table-dim
|
|
216
|
+
animation: stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
&.text-overflow {
|
|
@@ -221,6 +223,10 @@
|
|
|
221
223
|
text-overflow: ellipsis;
|
|
222
224
|
}
|
|
223
225
|
}
|
|
226
|
+
|
|
227
|
+
&.seq-column{
|
|
228
|
+
text-align: center;
|
|
229
|
+
}
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
/*固定列阴影-左*/
|
|
@@ -348,15 +354,14 @@
|
|
|
348
354
|
tr {
|
|
349
355
|
background-color: var(--td-bgc);
|
|
350
356
|
height: var(--row-height);
|
|
357
|
+
/** 一行分层,有利于高亮行重绘*/
|
|
358
|
+
transform: translateZ(0);
|
|
351
359
|
|
|
352
360
|
/* td inherit tr bgc*/
|
|
353
361
|
&.highlight-row {
|
|
354
|
-
animation: stk-table-dim
|
|
362
|
+
animation: stk-table-dim var(--highlight-duration) var(--highlight-easing);
|
|
355
363
|
}
|
|
356
|
-
|
|
357
|
-
/* &.highlight-row-transition {
|
|
358
|
-
transition: background-color v-bind(highlightStepDuration) linear;
|
|
359
|
-
}*/
|
|
364
|
+
|
|
360
365
|
|
|
361
366
|
&.hover,
|
|
362
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'>;
|
|
@@ -99,3 +106,17 @@ export const enum TagType {
|
|
|
99
106
|
TH,
|
|
100
107
|
TD,
|
|
101
108
|
}
|
|
109
|
+
|
|
110
|
+
/** 高亮配置 */
|
|
111
|
+
export type HighlightConfig = {
|
|
112
|
+
/** 高亮持续时间(s) */
|
|
113
|
+
duration?: number;
|
|
114
|
+
/** 高亮帧率 */
|
|
115
|
+
fps?: number;
|
|
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';
|