stk-table-vue 0.4.15 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -8
- package/lib/src/StkTable/StkTable.vue.d.ts +60 -16
- package/lib/src/StkTable/components/DragHandle.vue.d.ts +2 -0
- package/lib/src/StkTable/components/SortIcon.vue.d.ts +2 -0
- package/lib/src/StkTable/const.d.ts +6 -7
- package/lib/src/StkTable/types/index.d.ts +71 -22
- package/lib/src/StkTable/useThDrag.d.ts +10 -8
- package/lib/src/StkTable/useTrDrag.d.ts +21 -0
- package/lib/src/StkTable/useVirtualScroll.d.ts +1 -2
- package/lib/src/StkTable/utils/index.d.ts +2 -2
- package/lib/stk-table-vue.js +347 -88
- package/lib/style.css +50 -1
- package/package.json +1 -1
- package/src/StkTable/StkTable.vue +225 -78
- package/src/StkTable/components/DragHandle.vue +9 -0
- package/src/StkTable/components/SortIcon.vue +6 -0
- package/src/StkTable/const.ts +7 -7
- package/src/StkTable/style.less +80 -3
- package/src/StkTable/types/index.ts +78 -24
- package/src/StkTable/useThDrag.ts +64 -34
- package/src/StkTable/useTrDrag.ts +118 -0
- package/src/StkTable/useVirtualScroll.ts +0 -2
- package/src/StkTable/utils/index.ts +2 -2
package/lib/style.css
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
--col-resize-indicator-color:#87879c;
|
|
33
33
|
--fixed-col-shadow-color-from:rgba(0, 0, 0, 0.1);
|
|
34
34
|
--fixed-col-shadow-color-to:rgba(0, 0, 0, 0);
|
|
35
|
+
--drag-handle-hover-color:#d0d1e0;
|
|
35
36
|
position:relative;
|
|
36
37
|
overflow:auto;
|
|
37
38
|
display:flex;
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
--col-resize-indicator-color:#5d6064;
|
|
62
63
|
--fixed-col-shadow-color-from:rgba(135, 135, 156, 0.1);
|
|
63
64
|
--fixed-col-shadow-color-to:rgba(135, 135, 156, 0);
|
|
65
|
+
--drag-handle-hover-color:#5d6064;
|
|
64
66
|
color:#d1d1e0;
|
|
65
67
|
}
|
|
66
68
|
.stk-table.headless{
|
|
@@ -134,7 +136,8 @@
|
|
|
134
136
|
}
|
|
135
137
|
.stk-table.virtual .table-header-cell-wrapper{
|
|
136
138
|
overflow:hidden;
|
|
137
|
-
max-height:calc(var(--header-row-height) *
|
|
139
|
+
max-height:calc(var(--header-row-height) * 1);
|
|
140
|
+
max-height:calc(var(--header-row-height) * var(--row-span, 1));
|
|
138
141
|
}
|
|
139
142
|
.stk-table.virtual tbody td{
|
|
140
143
|
height:var(--row-height);
|
|
@@ -146,6 +149,9 @@
|
|
|
146
149
|
.stk-table.virtual .padding-top-tr td{
|
|
147
150
|
height:0;
|
|
148
151
|
}
|
|
152
|
+
.stk-table.virtual .expand-cell .table-cell-wrapper{
|
|
153
|
+
white-space:nowrap;
|
|
154
|
+
}
|
|
149
155
|
.stk-table.fixed-relative-mode th{
|
|
150
156
|
position:relative;
|
|
151
157
|
}
|
|
@@ -236,6 +242,49 @@
|
|
|
236
242
|
.stk-table .seq-column{
|
|
237
243
|
text-align:center;
|
|
238
244
|
}
|
|
245
|
+
.stk-table .expand-cell{
|
|
246
|
+
cursor:pointer;
|
|
247
|
+
}
|
|
248
|
+
.stk-table .expand-cell .table-cell-wrapper.expanded-cell-wrapper::before{
|
|
249
|
+
content:'';
|
|
250
|
+
display:inline-block;
|
|
251
|
+
margin:0 2px;
|
|
252
|
+
width:0;
|
|
253
|
+
height:0;
|
|
254
|
+
border-left:5px solid #757699;
|
|
255
|
+
border-top:4px solid transparent;
|
|
256
|
+
border-bottom:4px solid transparent;
|
|
257
|
+
transition:transform 0.2s ease;
|
|
258
|
+
}
|
|
259
|
+
.stk-table .expand-cell .table-cell-wrapper.expanded-cell-wrapper > span{
|
|
260
|
+
margin-left:var(--cell-padding-x);
|
|
261
|
+
}
|
|
262
|
+
.stk-table .expand-cell.expanded .table-cell-wrapper::before{
|
|
263
|
+
transform:rotate(90deg);
|
|
264
|
+
}
|
|
265
|
+
.stk-table .drag-row-cell .table-cell-wrapper{
|
|
266
|
+
display:inline-flex;
|
|
267
|
+
align-items:center;
|
|
268
|
+
}
|
|
269
|
+
.stk-table .drag-row-cell .drag-row-handle{
|
|
270
|
+
cursor:grab;
|
|
271
|
+
border-radius:4px;
|
|
272
|
+
}
|
|
273
|
+
.stk-table .drag-row-cell .drag-row-handle:hover{
|
|
274
|
+
background-color:var(--drag-handle-hover-color);
|
|
275
|
+
}
|
|
276
|
+
.stk-table .drag-row-cell .drag-row-handle:active{
|
|
277
|
+
cursor:grabbing;
|
|
278
|
+
}
|
|
279
|
+
.stk-table .drag-row-cell .drag-row-handle > svg{
|
|
280
|
+
vertical-align:-2px;
|
|
281
|
+
}
|
|
282
|
+
.stk-table .tr-dragging{
|
|
283
|
+
opacity:0.5;
|
|
284
|
+
}
|
|
285
|
+
.stk-table .tr-dragging-over{
|
|
286
|
+
background-color:var(--tr-hover-bgc);
|
|
287
|
+
}
|
|
239
288
|
.stk-table .fixed-cell--left{
|
|
240
289
|
--shadow-rotate:90deg;
|
|
241
290
|
}
|
package/package.json
CHANGED
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
@drop="onThDrop"
|
|
85
85
|
@dragover="onThDragOver"
|
|
86
86
|
>
|
|
87
|
-
<div class="table-header-cell-wrapper" :style="
|
|
87
|
+
<div class="table-header-cell-wrapper" :style="{ '--row-span': virtualX_on ? 1 : col.rowSpan }">
|
|
88
88
|
<component :is="col.customHeaderCell" v-if="col.customHeaderCell" :col="col" :colIndex="colIndex" :rowIndex="rowIndex" />
|
|
89
89
|
<template v-else-if="col.type === 'seq'">
|
|
90
90
|
<span class="table-header-title">{{ col.title }}</span>
|
|
@@ -97,14 +97,7 @@
|
|
|
97
97
|
|
|
98
98
|
<!-- 排序图图标 -->
|
|
99
99
|
<span v-if="col.sorter" class="table-header-sorter">
|
|
100
|
-
<
|
|
101
|
-
<polygon class="arrow-up" fill="#757699" points="8 2 4.8 6 11.2 6"></polygon>
|
|
102
|
-
<polygon
|
|
103
|
-
class="arrow-down"
|
|
104
|
-
transform="translate(8, 12) rotate(-180) translate(-8, -12) "
|
|
105
|
-
points="8 10 4.8 14 11.2 14"
|
|
106
|
-
></polygon>
|
|
107
|
-
</svg>
|
|
100
|
+
<SortIcon />
|
|
108
101
|
</span>
|
|
109
102
|
<!-- 列宽拖动handler -->
|
|
110
103
|
<div
|
|
@@ -123,7 +116,7 @@
|
|
|
123
116
|
<!-- 用于虚拟滚动表格内容定位 @deprecated 有兼容问题-->
|
|
124
117
|
<!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }"></tbody> -->
|
|
125
118
|
<!-- <tbody :style="{ transform: `translateY(${virtualScroll.offsetTop}px)` }"> -->
|
|
126
|
-
<tbody class="stk-tbody-main">
|
|
119
|
+
<tbody class="stk-tbody-main" @dragover="onTrDragOver" @dragenter="onTrDragEnter" @dragend="onTrDragEnd">
|
|
127
120
|
<tr v-if="virtual_on" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
|
|
128
121
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
129
122
|
<td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
|
|
@@ -133,58 +126,103 @@
|
|
|
133
126
|
</tr>
|
|
134
127
|
<tr
|
|
135
128
|
v-for="(row, rowIndex) in virtual_dataSourcePart"
|
|
136
|
-
:id="stkTableId + '-' + (rowKey ? rowKeyGen(row) : rowIndex)"
|
|
137
|
-
:key="rowKey ? rowKeyGen(row) : rowIndex"
|
|
138
|
-
:data-row-key="rowKey ? rowKeyGen(row) : rowIndex"
|
|
129
|
+
:id="stkTableId + '-' + (rowKey ? rowKeyGen(row) : virtualScroll.startIndex + rowIndex)"
|
|
130
|
+
:key="rowKey ? rowKeyGen(row) : virtualScroll.startIndex + rowIndex"
|
|
131
|
+
:data-row-key="rowKey ? rowKeyGen(row) : virtualScroll.startIndex + rowIndex"
|
|
139
132
|
:class="{
|
|
140
133
|
active: rowKey ? rowKeyGen(row) === currentRowKey : row === currentRow,
|
|
141
134
|
hover: props.showTrHoverClass && (rowKey ? rowKeyGen(row) === currentHoverRowKey : row === currentHoverRowKey),
|
|
142
|
-
[rowClassName(row, rowIndex)]: true,
|
|
135
|
+
[rowClassName(row, virtualScroll.startIndex + rowIndex)]: true,
|
|
136
|
+
expanded: row?.__EXPANDED__,
|
|
137
|
+
'expanded-row': row && (row as ExpandedRow).__EXPANDED_ROW__,
|
|
138
|
+
}"
|
|
139
|
+
:style="{
|
|
140
|
+
'--row-height':
|
|
141
|
+
row &&
|
|
142
|
+
(row as ExpandedRow).__EXPANDED_ROW__ &&
|
|
143
|
+
virtual_on &&
|
|
144
|
+
props.expandConfig?.height &&
|
|
145
|
+
props.expandConfig?.height + 'px',
|
|
143
146
|
}"
|
|
144
147
|
@click="e => onRowClick(e, row)"
|
|
145
148
|
@dblclick="e => onRowDblclick(e, row)"
|
|
146
149
|
@contextmenu="e => onRowMenu(e, row)"
|
|
147
150
|
@mouseover="e => onTrMouseOver(e, row)"
|
|
151
|
+
@drop="e => onTrDrop(e, virtualScroll.startIndex + rowIndex)"
|
|
148
152
|
>
|
|
149
153
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
150
154
|
<td v-if="virtualX_on" class="vt-x-left"></td>
|
|
151
|
-
<td
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
:class="[
|
|
158
|
-
col.className,
|
|
159
|
-
fixedColClassMap.get(colKeyGen(col)),
|
|
160
|
-
{
|
|
161
|
-
'seq-column': col.type === 'seq',
|
|
162
|
-
active: currentSelectedCellKey === cellKeyGen(row, col),
|
|
163
|
-
},
|
|
164
|
-
]"
|
|
165
|
-
@click="e => onCellClick(e, row, col)"
|
|
166
|
-
@mouseenter="e => onCellMouseEnter(e, row, col)"
|
|
167
|
-
@mouseleave="e => onCellMouseLeave(e, row, col)"
|
|
168
|
-
@mouseover="e => onCellMouseOver(e, row, col)"
|
|
169
|
-
>
|
|
170
|
-
<component
|
|
171
|
-
:is="col.customCell"
|
|
172
|
-
v-if="col.customCell"
|
|
173
|
-
:col="col"
|
|
174
|
-
:row="row"
|
|
175
|
-
:rowIndex="rowIndex"
|
|
176
|
-
:colIndex="colIndex"
|
|
177
|
-
:cellValue="row?.[col.dataIndex]"
|
|
178
|
-
/>
|
|
179
|
-
<div v-else class="table-cell-wrapper" :title="!col.type ? row?.[col.dataIndex] : ''">
|
|
180
|
-
<template v-if="col.type === 'seq'">
|
|
181
|
-
{{ (props.seqConfig.startIndex || 0) + rowIndex + 1 }}
|
|
182
|
-
</template>
|
|
183
|
-
<template v-else>
|
|
184
|
-
{{ row?.[col.dataIndex] ?? getEmptyCellText(col, row) }}
|
|
185
|
-
</template>
|
|
155
|
+
<td v-if="row && (row as ExpandedRow).__EXPANDED_ROW__" :colspan="virtualX_columnPart.length">
|
|
156
|
+
<!-- TODO: support wheel -->
|
|
157
|
+
<div class="table-cell-wrapper">
|
|
158
|
+
<slot name="expand" :row="(row as ExpandedRow).__EXPANDED_ROW__" :col="(row as ExpandedRow).__EXPANDED_COL__">
|
|
159
|
+
{{ (row as ExpandedRow).__EXPANDED_ROW__?.[(row as ExpandedRow).__EXPANDED_COL__.dataIndex] ?? '' }}
|
|
160
|
+
</slot>
|
|
186
161
|
</div>
|
|
187
162
|
</td>
|
|
163
|
+
<template v-else>
|
|
164
|
+
<td
|
|
165
|
+
v-for="(col, colIndex) in virtualX_columnPart"
|
|
166
|
+
:key="col.dataIndex"
|
|
167
|
+
:data-index="col.dataIndex"
|
|
168
|
+
:cell-key="rowKeyGen(row) + '--' + colKeyGen(col)"
|
|
169
|
+
:style="cellStyleMap[TagType.TD].get(colKeyGen(col))"
|
|
170
|
+
:class="[
|
|
171
|
+
col.className,
|
|
172
|
+
fixedColClassMap.get(colKeyGen(col)),
|
|
173
|
+
{
|
|
174
|
+
'seq-column': col.type === 'seq',
|
|
175
|
+
active: currentSelectedCellKey === cellKeyGen(row, col),
|
|
176
|
+
'expand-cell': col.type === 'expand',
|
|
177
|
+
expanded: col.type === 'expand' && colKeyGen(row?.__EXPANDED__) === colKeyGen(col),
|
|
178
|
+
'drag-row-cell': col.type === 'dragRow',
|
|
179
|
+
},
|
|
180
|
+
]"
|
|
181
|
+
@click="
|
|
182
|
+
e => {
|
|
183
|
+
col.type === 'expand' && toggleExpandRow(row, col);
|
|
184
|
+
onCellClick(e, row, col);
|
|
185
|
+
}
|
|
186
|
+
"
|
|
187
|
+
@mouseenter="e => onCellMouseEnter(e, row, col)"
|
|
188
|
+
@mouseleave="e => onCellMouseLeave(e, row, col)"
|
|
189
|
+
@mouseover="e => onCellMouseOver(e, row, col)"
|
|
190
|
+
>
|
|
191
|
+
<component
|
|
192
|
+
:is="col.customCell"
|
|
193
|
+
v-if="col.customCell"
|
|
194
|
+
class="table-cell-wrapper"
|
|
195
|
+
:col="col"
|
|
196
|
+
:row="row"
|
|
197
|
+
:rowIndex="virtualScroll.startIndex + rowIndex"
|
|
198
|
+
:colIndex="colIndex"
|
|
199
|
+
:cellValue="row?.[col.dataIndex]"
|
|
200
|
+
:expanded="row?.__EXPANDED__ || null"
|
|
201
|
+
/>
|
|
202
|
+
<div
|
|
203
|
+
v-else
|
|
204
|
+
class="table-cell-wrapper"
|
|
205
|
+
:class="{ 'expanded-cell-wrapper': col.type === 'expand' }"
|
|
206
|
+
:title="col.type !== 'seq' ? row?.[col.dataIndex] : ''"
|
|
207
|
+
>
|
|
208
|
+
<template v-if="col.type === 'seq'">
|
|
209
|
+
{{ (props.seqConfig.startIndex || 0) + virtualScroll.startIndex + rowIndex + 1 }}
|
|
210
|
+
</template>
|
|
211
|
+
<span v-else-if="col.type === 'expand'">
|
|
212
|
+
{{ row?.[col.dataIndex] ?? '' }}
|
|
213
|
+
</span>
|
|
214
|
+
<template v-else-if="col.type === 'dragRow'">
|
|
215
|
+
<DragHandle @dragstart="e => onTrDragStart(e, virtualScroll.startIndex + rowIndex)" />
|
|
216
|
+
<span>
|
|
217
|
+
{{ row?.[col.dataIndex] ?? '' }}
|
|
218
|
+
</span>
|
|
219
|
+
</template>
|
|
220
|
+
<template v-else>
|
|
221
|
+
{{ row?.[col.dataIndex] ?? getEmptyCellText(col, row) }}
|
|
222
|
+
</template>
|
|
223
|
+
</div>
|
|
224
|
+
</td>
|
|
225
|
+
</template>
|
|
188
226
|
</tr>
|
|
189
227
|
<tr v-if="virtual_on" :style="`height: ${virtual_offsetBottom}px`"></tr>
|
|
190
228
|
</tbody>
|
|
@@ -197,13 +235,29 @@
|
|
|
197
235
|
|
|
198
236
|
<script setup lang="ts">
|
|
199
237
|
/**
|
|
200
|
-
* @author
|
|
201
|
-
* TODO:存在的问题:
|
|
202
|
-
* [] column.dataIndex 作为唯一键,不能重复
|
|
238
|
+
* @author japlus
|
|
203
239
|
*/
|
|
204
240
|
import { CSSProperties, computed, nextTick, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
205
|
-
import
|
|
206
|
-
import
|
|
241
|
+
import DragHandle from './components/DragHandle.vue';
|
|
242
|
+
import SortIcon from './components/SortIcon.vue';
|
|
243
|
+
import { DEFAULT_ROW_HEIGHT, DEFAULT_SMOOTH_SCROLL, EXPANDED_ROW_KEY_PREFIX, IS_LEGACY_MODE } from './const';
|
|
244
|
+
import {
|
|
245
|
+
DragRowConfig,
|
|
246
|
+
ExpandConfig,
|
|
247
|
+
ExpandedRow,
|
|
248
|
+
HeaderDragConfig,
|
|
249
|
+
HighlightConfig,
|
|
250
|
+
Order,
|
|
251
|
+
PrivateRowDT,
|
|
252
|
+
PrivateStkTableColumn,
|
|
253
|
+
SeqConfig,
|
|
254
|
+
SortConfig,
|
|
255
|
+
SortOption,
|
|
256
|
+
SortState,
|
|
257
|
+
StkTableColumn,
|
|
258
|
+
TagType,
|
|
259
|
+
UniqKeyProp,
|
|
260
|
+
} from './types/index';
|
|
207
261
|
import { useAutoResize } from './useAutoResize';
|
|
208
262
|
import { useColResize } from './useColResize';
|
|
209
263
|
import { useFixedCol } from './useFixedCol';
|
|
@@ -212,15 +266,18 @@ import { useGetFixedColPosition } from './useGetFixedColPosition';
|
|
|
212
266
|
import { useHighlight } from './useHighlight';
|
|
213
267
|
import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
|
|
214
268
|
import { useThDrag } from './useThDrag';
|
|
269
|
+
import { useTrDrag } from './useTrDrag';
|
|
215
270
|
import { useVirtualScroll } from './useVirtualScroll';
|
|
216
271
|
import { createStkTableId, getCalculatedColWidth, getColWidth, howDeepTheHeader, tableSort, transformWidthToStr } from './utils/index';
|
|
217
272
|
|
|
218
273
|
/** Generic stands for DataType */
|
|
219
|
-
type DT = any;
|
|
220
|
-
|
|
274
|
+
type DT = any & PrivateRowDT;
|
|
275
|
+
|
|
276
|
+
/** generate table instance id */
|
|
221
277
|
const stkTableId = createStkTableId();
|
|
278
|
+
|
|
222
279
|
/**
|
|
223
|
-
* props
|
|
280
|
+
* props cannot be placed in a separate file. It will cause compilation errors with vue 2.7 compiler.
|
|
224
281
|
*/
|
|
225
282
|
const props = withDefaults(
|
|
226
283
|
defineProps<{
|
|
@@ -280,16 +337,17 @@ const props = withDefaults(
|
|
|
280
337
|
/** 单元格再次点击否可以取消选中 (cellActive=true)*/
|
|
281
338
|
selectedCellRevokable?: boolean;
|
|
282
339
|
/** 表头是否可拖动。支持回调函数。 */
|
|
283
|
-
headerDrag?:
|
|
340
|
+
headerDrag?: HeaderDragConfig;
|
|
284
341
|
/**
|
|
285
342
|
* 给行附加className<br>
|
|
286
343
|
* FIXME: 是否需要优化,因为不传此prop会使表格行一直执行空函数,是否有影响
|
|
287
344
|
*/
|
|
288
345
|
rowClassName?: (row: DT, i: number) => string;
|
|
289
346
|
/**
|
|
290
|
-
*
|
|
347
|
+
* 列宽是否可拖动(需要设置v-model:columns)<br>
|
|
291
348
|
* **不要设置**列minWidth,**必须**设置width<br>
|
|
292
349
|
* 列宽拖动时,每一列都必须要有width,且minWidth/maxWidth不生效。table width会变为"fit-content"。
|
|
350
|
+
* - 会自动更新props.columns中的with属性
|
|
293
351
|
*/
|
|
294
352
|
colResizable?: boolean;
|
|
295
353
|
/** 可拖动至最小的列宽 */
|
|
@@ -320,6 +378,10 @@ const props = withDefaults(
|
|
|
320
378
|
highlightConfig?: HighlightConfig;
|
|
321
379
|
/** 序号列配置 */
|
|
322
380
|
seqConfig?: SeqConfig;
|
|
381
|
+
/** 展开行配置 */
|
|
382
|
+
expandConfig?: ExpandConfig;
|
|
383
|
+
/** 列拖动配置 */
|
|
384
|
+
dragRowConfig?: DragRowConfig;
|
|
323
385
|
/**
|
|
324
386
|
* 固定头,固定列实现方式。(非响应式)
|
|
325
387
|
*
|
|
@@ -381,6 +443,8 @@ const props = withDefaults(
|
|
|
381
443
|
hideHeaderTitle: false,
|
|
382
444
|
highlightConfig: () => ({}),
|
|
383
445
|
seqConfig: () => ({}),
|
|
446
|
+
expandConfig: () => ({}),
|
|
447
|
+
dragRowConfig: () => ({}),
|
|
384
448
|
cellFixedMode: 'sticky',
|
|
385
449
|
smoothScroll: DEFAULT_SMOOTH_SCROLL,
|
|
386
450
|
},
|
|
@@ -472,11 +536,24 @@ const emits = defineEmits<{
|
|
|
472
536
|
* ```(targetColKey: string)```
|
|
473
537
|
*/
|
|
474
538
|
(e: 'th-drop', targetColKey: string): void;
|
|
539
|
+
/**
|
|
540
|
+
* 行拖动事件
|
|
541
|
+
* ```(dragStartKey: string, targetRowKey: string)```
|
|
542
|
+
*/
|
|
543
|
+
(e: 'row-order-change', dragStartKey: string, targetRowKey: string): void;
|
|
475
544
|
/**
|
|
476
545
|
* 列宽变动时触发
|
|
546
|
+
* ```(cols: StkTableColumn<DT>)```
|
|
477
547
|
*/
|
|
478
548
|
(e: 'col-resize', cols: StkTableColumn<DT>): void;
|
|
479
|
-
/**
|
|
549
|
+
/**
|
|
550
|
+
* 展开行触发
|
|
551
|
+
* ```( data: { expanded: boolean; row: DT; col: StkTableColumn<DT> })```
|
|
552
|
+
*/
|
|
553
|
+
(e: 'toggle-row-expand', data: { expanded: boolean; row: DT; col: StkTableColumn<DT> | null }): void;
|
|
554
|
+
/**
|
|
555
|
+
* v-model:columns col resize 时更新宽度
|
|
556
|
+
*/
|
|
480
557
|
(e: 'update:columns', cols: StkTableColumn<DT>[]): void;
|
|
481
558
|
}>();
|
|
482
559
|
|
|
@@ -542,17 +619,14 @@ const dataSourceCopy = shallowRef<DT[]>([...props.dataSource]);
|
|
|
542
619
|
* 列唯一键
|
|
543
620
|
* @param col
|
|
544
621
|
*/
|
|
545
|
-
const colKeyGen = computed(() => {
|
|
622
|
+
const colKeyGen = computed<(col: StkTableColumn<DT>) => string>(() => {
|
|
546
623
|
if (typeof props.colKey === 'function') {
|
|
547
|
-
return
|
|
624
|
+
return col => (props.colKey as (col: StkTableColumn<DT>) => string)(col);
|
|
548
625
|
} else {
|
|
549
|
-
return
|
|
626
|
+
return col => (col ? (col as any)[props.colKey as string] : null);
|
|
550
627
|
}
|
|
551
628
|
});
|
|
552
629
|
|
|
553
|
-
/**高亮帧间隔
|
|
554
|
-
const highlightStepDuration = Highlight_Color_Change_Freq / 1000 + 's';*/
|
|
555
|
-
|
|
556
630
|
/** 空单元格占位字符 */
|
|
557
631
|
const getEmptyCellText = computed(() => {
|
|
558
632
|
if (typeof props.emptyCellText === 'string') {
|
|
@@ -565,7 +639,9 @@ const getEmptyCellText = computed(() => {
|
|
|
565
639
|
/** rowKey缓存 */
|
|
566
640
|
const rowKeyGenStore = new WeakMap();
|
|
567
641
|
|
|
568
|
-
const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits });
|
|
642
|
+
const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits, colKeyGen });
|
|
643
|
+
|
|
644
|
+
const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
|
|
569
645
|
|
|
570
646
|
const {
|
|
571
647
|
virtualScroll,
|
|
@@ -581,7 +657,7 @@ const {
|
|
|
581
657
|
initVirtualScrollX,
|
|
582
658
|
updateVirtualScrollY,
|
|
583
659
|
updateVirtualScrollX,
|
|
584
|
-
} = useVirtualScroll({ tableContainerRef,
|
|
660
|
+
} = useVirtualScroll({ tableContainerRef, props, dataSourceCopy, tableHeaderLast, tableHeaders });
|
|
585
661
|
|
|
586
662
|
/** 获取固定列的位置 */
|
|
587
663
|
const getFixedColPosition = useGetFixedColPosition({ colKeyGen, tableHeaders });
|
|
@@ -753,7 +829,11 @@ function dealColumns() {
|
|
|
753
829
|
* @param parent 父节点引用,用于构建双向链表。
|
|
754
830
|
* @param parentFixed 父节点固定列继承。
|
|
755
831
|
*/
|
|
756
|
-
function flat(
|
|
832
|
+
function flat(
|
|
833
|
+
arr: PrivateStkTableColumn<DT>[],
|
|
834
|
+
parent: PrivateStkTableColumn<DT> | null,
|
|
835
|
+
depth = 0 /* , parentFixed: 'left' | 'right' | null = null */,
|
|
836
|
+
) {
|
|
757
837
|
if (!tableHeadersTemp[depth]) {
|
|
758
838
|
tableHeadersTemp[depth] = [];
|
|
759
839
|
}
|
|
@@ -810,7 +890,11 @@ function rowKeyGen(row: DT | null | undefined) {
|
|
|
810
890
|
if (!row) return row;
|
|
811
891
|
let key = rowKeyGenStore.get(row);
|
|
812
892
|
if (!key) {
|
|
813
|
-
|
|
893
|
+
if ((row as PrivateRowDT).__ROW_KEY__) {
|
|
894
|
+
key = (row as PrivateRowDT).__ROW_KEY__;
|
|
895
|
+
} else {
|
|
896
|
+
key = typeof props.rowKey === 'function' ? props.rowKey(row) : row[props.rowKey];
|
|
897
|
+
}
|
|
814
898
|
|
|
815
899
|
if (key === void 0) {
|
|
816
900
|
// key为undefined时,不应该高亮行。因此重新生成key
|
|
@@ -1069,7 +1153,7 @@ function onTableScroll(e: Event) {
|
|
|
1069
1153
|
}
|
|
1070
1154
|
}
|
|
1071
1155
|
|
|
1072
|
-
/** tr hover
|
|
1156
|
+
/** tr hover */
|
|
1073
1157
|
function onTrMouseOver(_e: MouseEvent, row: DT) {
|
|
1074
1158
|
if (currentHoverRow === row) return;
|
|
1075
1159
|
currentHoverRow = row;
|
|
@@ -1079,7 +1163,7 @@ function onTrMouseOver(_e: MouseEvent, row: DT) {
|
|
|
1079
1163
|
/**
|
|
1080
1164
|
* 选中一行
|
|
1081
1165
|
* @param {string} rowKeyOrRow selected rowKey, undefined to unselect
|
|
1082
|
-
* @param {boolean} option.silent if
|
|
1166
|
+
* @param {boolean} option.silent if set true not emit `current-change`. default:false
|
|
1083
1167
|
*/
|
|
1084
1168
|
function setCurrentRow(rowKeyOrRow: string | undefined | DT, option = { silent: false }) {
|
|
1085
1169
|
if (!dataSourceCopy.value.length) return;
|
|
@@ -1142,7 +1226,6 @@ function setSorter(dataIndex: string, order: Order, option: { sortOption?: SortO
|
|
|
1142
1226
|
return dataSourceCopy.value;
|
|
1143
1227
|
}
|
|
1144
1228
|
|
|
1145
|
-
/** 重置排序 */
|
|
1146
1229
|
function resetSorter() {
|
|
1147
1230
|
sortCol.value = void 0;
|
|
1148
1231
|
sortOrderIndex.value = 0;
|
|
@@ -1150,9 +1233,9 @@ function resetSorter() {
|
|
|
1150
1233
|
}
|
|
1151
1234
|
|
|
1152
1235
|
/**
|
|
1153
|
-
*
|
|
1154
|
-
* @param top
|
|
1155
|
-
* @param left
|
|
1236
|
+
* set scroll bar position
|
|
1237
|
+
* @param top null to not change
|
|
1238
|
+
* @param left null to not change
|
|
1156
1239
|
*/
|
|
1157
1240
|
function scrollTo(top: number | null = 0, left: number | null = 0) {
|
|
1158
1241
|
if (!tableContainerRef.value) return;
|
|
@@ -1160,18 +1243,80 @@ function scrollTo(top: number | null = 0, left: number | null = 0) {
|
|
|
1160
1243
|
if (left !== null) tableContainerRef.value.scrollLeft = left;
|
|
1161
1244
|
}
|
|
1162
1245
|
|
|
1163
|
-
/**
|
|
1246
|
+
/** get current table data */
|
|
1164
1247
|
function getTableData() {
|
|
1165
1248
|
return toRaw(dataSourceCopy.value);
|
|
1166
1249
|
}
|
|
1167
1250
|
|
|
1168
|
-
/**
|
|
1251
|
+
/** get current sort info */
|
|
1169
1252
|
function getSortColumns(): Partial<SortState<DT>>[] {
|
|
1170
1253
|
const sortOrder = sortSwitchOrder[sortOrderIndex.value];
|
|
1171
1254
|
if (!sortOrder) return [];
|
|
1172
1255
|
return [{ dataIndex: sortCol.value, order: sortOrder }];
|
|
1173
1256
|
}
|
|
1174
1257
|
|
|
1258
|
+
/** click expended icon to toggleg expand row */
|
|
1259
|
+
function toggleExpandRow(row: DT, col: StkTableColumn<DT>) {
|
|
1260
|
+
const isExpand = row?.__EXPANDED__ === col ? !row?.__EXPANDED__ : true;
|
|
1261
|
+
setRowExpand(row, isExpand, { col });
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
*
|
|
1266
|
+
* @param rowKeyOrRow rowKey or row
|
|
1267
|
+
* @param expand expand or collapse
|
|
1268
|
+
* @param data { col?: StkTableColumn<DT> }
|
|
1269
|
+
* @param data.silent if set true, not emit `toggle-row-expand`, default:false
|
|
1270
|
+
*/
|
|
1271
|
+
function setRowExpand(rowKeyOrRow: string | undefined | DT, expand?: boolean, data?: { col?: StkTableColumn<DT>; silent?: boolean }) {
|
|
1272
|
+
let rowKey: string;
|
|
1273
|
+
if (typeof rowKeyOrRow === 'string') {
|
|
1274
|
+
rowKey = rowKeyOrRow;
|
|
1275
|
+
} else {
|
|
1276
|
+
rowKey = rowKeyGen(rowKeyOrRow);
|
|
1277
|
+
}
|
|
1278
|
+
const tempData = [...dataSourceCopy.value];
|
|
1279
|
+
const index = tempData.findIndex(it => rowKeyGen(it) === rowKey);
|
|
1280
|
+
if (index === -1) {
|
|
1281
|
+
console.warn('expandRow failed.rowKey:', rowKey);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// delete other expanded row below the target row
|
|
1286
|
+
for (let i = index + 1; i < tempData.length; i++) {
|
|
1287
|
+
const item: PrivateRowDT = tempData[i];
|
|
1288
|
+
const rowKey = item.__ROW_KEY__;
|
|
1289
|
+
if (rowKey?.startsWith(EXPANDED_ROW_KEY_PREFIX)) {
|
|
1290
|
+
tempData.splice(i, 1);
|
|
1291
|
+
i--;
|
|
1292
|
+
} else {
|
|
1293
|
+
break;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const row = tempData[index];
|
|
1298
|
+
const col = data?.col || null;
|
|
1299
|
+
|
|
1300
|
+
if (expand) {
|
|
1301
|
+
// insert new expanded row
|
|
1302
|
+
const newExpandRow: ExpandedRow = {
|
|
1303
|
+
__ROW_KEY__: EXPANDED_ROW_KEY_PREFIX + rowKey,
|
|
1304
|
+
__EXPANDED_ROW__: row,
|
|
1305
|
+
__EXPANDED_COL__: col,
|
|
1306
|
+
};
|
|
1307
|
+
tempData.splice(index + 1, 0, newExpandRow);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
if (row) {
|
|
1311
|
+
row.__EXPANDED__ = expand ? col : null;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
dataSourceCopy.value = tempData;
|
|
1315
|
+
if (!data?.silent) {
|
|
1316
|
+
emits('toggle-row-expand', { expanded: Boolean(expand), row, col });
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1175
1320
|
defineExpose({
|
|
1176
1321
|
/** @see {@link initVirtualScroll} */
|
|
1177
1322
|
initVirtualScroll,
|
|
@@ -1199,5 +1344,7 @@ defineExpose({
|
|
|
1199
1344
|
scrollTo,
|
|
1200
1345
|
/** @see {@link getTableData} */
|
|
1201
1346
|
getTableData,
|
|
1347
|
+
/** @see {@link setRowExpand} */
|
|
1348
|
+
setRowExpand,
|
|
1202
1349
|
});
|
|
1203
1350
|
</script>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span class="drag-row-handle" draggable="true">
|
|
3
|
+
<svg viewBox="0 0 1024 1024" width="16" height="16" fill="currentColor">
|
|
4
|
+
<path
|
|
5
|
+
d="M640 853.3a85.3 85.3 0 1 1 85.3-85.3 85.3 85.3 0 0 1-85.3 85.3z m-256 0a85.3 85.3 0 1 1 85.3-85.3 85.3 85.3 0 0 1-85.3 85.3z m256-256a85.3 85.3 0 1 1 85.3-85.3 85.3 85.3 0 0 1-85.3 85.3z m-256 0a85.3 85.3 0 1 1 85.3-85.3 85.3 85.3 0 0 1-85.3 85.3z m256-256a85.3 85.3 0 1 1 85.3-85.3 85.3 85.3 0 0 1-85.3 85.3zM384 341.3a85.3 85.3 0 1 1 85.3-85.3 85.3 85.3 0 0 1-85.3 85.3z"
|
|
6
|
+
></path>
|
|
7
|
+
</svg>
|
|
8
|
+
</span>
|
|
9
|
+
</template>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 16 16">
|
|
3
|
+
<polygon class="arrow-up" fill="#757699" points="8 2 4.8 6 11.2 6"></polygon>
|
|
4
|
+
<polygon class="arrow-down" transform="translate(8, 12) rotate(-180) translate(-8, -12) " points="8 10 4.8 14 11.2 14"></polygon>
|
|
5
|
+
</svg>
|
|
6
|
+
</template>
|
package/src/StkTable/const.ts
CHANGED
|
@@ -6,29 +6,29 @@ export const DEFAULT_TABLE_HEIGHT = 100;
|
|
|
6
6
|
export const DEFAULT_TABLE_WIDTH = 200;
|
|
7
7
|
export const DEFAULT_ROW_HEIGHT = 28;
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/** highlight background */
|
|
10
10
|
export const HIGHLIGHT_COLOR = {
|
|
11
11
|
light: { from: '#71a2fd', to: '#fff' },
|
|
12
12
|
dark: { from: '#1e4c99', to: '#181c21' },
|
|
13
13
|
};
|
|
14
|
-
/** 高亮持续时间 */
|
|
15
14
|
export const HIGHLIGHT_DURATION = 2000;
|
|
16
15
|
|
|
17
|
-
/**
|
|
16
|
+
/** highlight change frequency 1000/30 -> 30FPS */
|
|
18
17
|
export const HIGHLIGHT_FREQ = 1000 / 30;
|
|
19
18
|
|
|
20
|
-
/** 高亮行class */
|
|
21
19
|
export const HIGHLIGHT_ROW_CLASS = 'highlight-row';
|
|
22
|
-
/** 高连单元格class */
|
|
23
20
|
export const HIGHLIGHT_CELL_CLASS = 'highlight-cell';
|
|
24
21
|
|
|
25
22
|
const _chromeVersion = getBrowsersVersion('chrome');
|
|
26
23
|
const _firefoxVersion = getBrowsersVersion('firefox');
|
|
27
24
|
|
|
28
|
-
/**
|
|
25
|
+
/** legacy sticky compatible mode */
|
|
29
26
|
export const IS_LEGACY_MODE = _chromeVersion < 56 || _firefoxVersion < 59;
|
|
30
27
|
|
|
31
|
-
/**
|
|
28
|
+
/** default props.smoothDefault */
|
|
32
29
|
export const DEFAULT_SMOOTH_SCROLL = _chromeVersion < 85;
|
|
33
30
|
|
|
34
31
|
export const STK_ID_PREFIX = 'stk';
|
|
32
|
+
|
|
33
|
+
/** expanded row key prefix */
|
|
34
|
+
export const EXPANDED_ROW_KEY_PREFIX = 'expanded-';
|