stk-table-vue 0.4.8 → 0.4.10

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 CHANGED
@@ -12,7 +12,7 @@ repo:
12
12
 
13
13
  ## Bug TODO:
14
14
  * [x] props.dataSource 为 shallowRef 时,高亮行不生效。(bug:2024.02.21)(resolved:0.2.3)
15
- * [] 固定列列宽拖动目标。
15
+ * [x] 右侧固定列列宽拖动目标。(v0.4.10)
16
16
  * [] 惯性滚动优化。
17
17
 
18
18
  ## Feature TODO:
@@ -251,10 +251,10 @@ export type StkProps = {
251
251
  ```js
252
252
  {
253
253
  /**
254
- * 排序变更触发
255
- * ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
254
+ * 排序变更触发。defaultSort.dataIndex 找不到时,col 将返回null。
255
+ * ```(col: StkTableColumn<DT> | null, order: Order, data: DT[])```
256
256
  */
257
- (e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig): void;
257
+ (e: 'sort-change', col: StkTableColumn<DT> | null, order: Order, data: DT[], sortConfig: SortConfig): void;
258
258
  /**
259
259
  * 一行点击事件
260
260
  * ```(ev: MouseEvent, row: DT)```
@@ -4,10 +4,10 @@ import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, S
4
4
  type DT = any;
5
5
  /**
6
6
  * 选中一行,
7
- * @param {string} rowKey selected rowKey, null to unselect
7
+ * @param {string} rowKey selected rowKey, undefined to unselect
8
8
  * @param {boolean} option.silent 是否触发回调
9
9
  */
10
- declare function setCurrentRow(rowKey: string, option?: {
10
+ declare function setCurrentRow(rowKey: string | undefined, option?: {
11
11
  silent: boolean;
12
12
  }): void;
13
13
  /**
@@ -198,8 +198,8 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
198
198
  /** 设置高亮渐暗单元格 */
199
199
  setHighlightDimCell: (rowKeyValue: string, dataIndex: string, option?: {
200
200
  className?: string | undefined; /**
201
- * 排序变更触发
202
- * ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
201
+ * 排序变更触发。defaultSort.dataIndex 找不到时,col 将返回null。
202
+ * ```(col: StkTableColumn<DT> | null, order: Order, data: DT[])```
203
203
  */
204
204
  method?: "animation" | "css" | undefined;
205
205
  keyframe?: Keyframe[] | PropertyIndexedKeyframes | null | undefined;
@@ -226,7 +226,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
226
226
  /** 获取表格数据 */
227
227
  getTableData: typeof getTableData;
228
228
  }, unknown, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
229
- "sort-change": (col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig<any>) => void;
229
+ "sort-change": (col: StkTableColumn<any> | null, order: Order, data: any[], sortConfig: SortConfig<any>) => void;
230
230
  "row-click": (ev: MouseEvent, row: any) => void;
231
231
  "current-change": (ev: MouseEvent | null, row: any, data: {
232
232
  select: boolean;
@@ -406,7 +406,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
406
406
  "onTh-drag-start"?: ((dragStartKey: string) => any) | undefined;
407
407
  "onCol-order-change"?: ((dragStartKey: string, targetColKey: string) => any) | undefined;
408
408
  "onTh-drop"?: ((targetColKey: string) => any) | undefined;
409
- "onSort-change"?: ((col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig<any>) => any) | undefined;
409
+ "onSort-change"?: ((col: StkTableColumn<any> | null, order: Order, data: any[], sortConfig: SortConfig<any>) => any) | undefined;
410
410
  "onRow-click"?: ((ev: MouseEvent, row: any) => any) | undefined;
411
411
  "onCurrent-change"?: ((ev: MouseEvent | null, row: any, data: {
412
412
  select: boolean;
@@ -121,8 +121,11 @@ export type SortConfig<T extends Record<string, any>> = {
121
121
  * 类似onMounted时,调用setSorter点了下表头。
122
122
  */
123
123
  defaultSort?: {
124
- dataIndex: keyof T;
124
+ dataIndex: StkTableColumn<T>['dataIndex'];
125
125
  order: Order;
126
+ sortField?: StkTableColumn<T>['sortField'];
127
+ sortType?: StkTableColumn<T>['sortType'];
128
+ sorter?: StkTableColumn<T>['sorter'];
126
129
  /** 是否禁止触发sort-change事件。默认false,表示触发事件。 */
127
130
  silent?: boolean;
128
131
  };
@@ -8,10 +8,11 @@ type Params<DT extends Record<string, any>> = {
8
8
  tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
9
9
  colResizeIndicatorRef: Ref<HTMLElement | undefined>;
10
10
  colKeyGen: ComputedRef<(p: any) => UniqKey>;
11
+ fixedCols: Ref<StkTableColumn<DT>[]>;
11
12
  };
12
13
  /** 列宽拖动 */
13
- export declare function useColResize<DT extends Record<string, any>>({ tableContainerRef, tableHeaderLast, colResizeIndicatorRef, props, emits, colKeyGen, }: Params<DT>): {
14
+ export declare function useColResize<DT extends Record<string, any>>({ tableContainerRef, tableHeaderLast, colResizeIndicatorRef, props, emits, colKeyGen, fixedCols, }: Params<DT>): {
14
15
  isColResizing: Ref<boolean>;
15
- onThResizeMouseDown: (e: MouseEvent, col: StkTableColumn<DT>, isPrev?: boolean) => void;
16
+ onThResizeMouseDown: (e: MouseEvent, col: StkTableColumn<DT>, leftHandle?: boolean) => void;
16
17
  };
17
18
  export {};
@@ -15,6 +15,8 @@ type Params<T extends Record<string, any>> = {
15
15
  * @returns
16
16
  */
17
17
  export declare function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, getFixedColPosition, tableHeaders, tableHeaderLast, tableContainerRef, }: Params<DT>): {
18
+ /** 正在被固定的列 */
19
+ fixedCols: ShallowRef<StkTableColumn<DT>[]>;
18
20
  /** 固定列class */
19
21
  fixedColClassMap: ComputedRef<Map<any, any>>;
20
22
  /** 滚动条变化时,更新需要展示阴影的列 */
@@ -1,7 +1,7 @@
1
1
  import { onMounted, onBeforeUnmount, watch, ref, shallowRef, computed, defineComponent, nextTick, toRaw, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createCommentVNode, createElementVNode, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, renderSlot, createTextVNode } from "vue";
2
2
  import { interpolateRgb } from "d3-interpolate";
3
3
  function isEmptyValue(val, isNumber) {
4
- let isEmpty = val === null || val === "";
4
+ let isEmpty = val === null || val === "" || val === void 0;
5
5
  if (isNumber) {
6
6
  isEmpty = isEmpty || typeof val === "boolean" || Number.isNaN(+val);
7
7
  }
@@ -69,7 +69,7 @@ function separatedData(sortOption, targetDataSource, isNumber) {
69
69
  for (let i = 0; i < targetDataSource.length; i++) {
70
70
  const row = targetDataSource[i];
71
71
  const sortField = sortOption.sortField || sortOption.dataIndex;
72
- const isEmpty = isEmptyValue(row[sortField], isNumber);
72
+ const isEmpty = isEmptyValue(row == null ? void 0 : row[sortField], isNumber);
73
73
  if (isEmpty) {
74
74
  emptyArr.push(row);
75
75
  } else {
@@ -79,7 +79,7 @@ function separatedData(sortOption, targetDataSource, isNumber) {
79
79
  return [valueArr, emptyArr];
80
80
  }
81
81
  function tableSort(sortOption, order, dataSource, sortConfig = {}) {
82
- if (!(dataSource == null ? void 0 : dataSource.length))
82
+ if (!(dataSource == null ? void 0 : dataSource.length) || !sortOption)
83
83
  return dataSource || [];
84
84
  sortConfig = { emptyToBottom: false, ...sortConfig };
85
85
  let targetDataSource = [...dataSource];
@@ -96,8 +96,8 @@ function tableSort(sortOption, order, dataSource, sortConfig = {}) {
96
96
  let { sortType } = sortOption;
97
97
  if (!sortType)
98
98
  sortType = typeof dataSource[0][sortField];
99
- const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, sortType === "number");
100
99
  const isNumber = sortType === "number";
100
+ const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, isNumber);
101
101
  if (order === "asc") {
102
102
  valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, sortConfig.stringLocaleCompare));
103
103
  } else {
@@ -234,15 +234,16 @@ function useColResize({
234
234
  colResizeIndicatorRef,
235
235
  props,
236
236
  emits,
237
- colKeyGen
237
+ colKeyGen,
238
+ fixedCols
238
239
  }) {
239
240
  const isColResizing = ref(false);
240
241
  let colResizeState = {
241
242
  currentCol: null,
242
- currentColIndex: 0,
243
243
  lastCol: null,
244
244
  startX: 0,
245
- startOffsetTableX: 0
245
+ startOffsetTableX: 0,
246
+ revertMoveX: false
246
247
  };
247
248
  onMounted(() => {
248
249
  initColResizeEvent();
@@ -258,7 +259,7 @@ function useColResize({
258
259
  window.removeEventListener("mousemove", onThResizeMouseMove);
259
260
  window.removeEventListener("mouseup", onThResizeMouseUp);
260
261
  }
261
- function onThResizeMouseDown(e, col, isPrev = false) {
262
+ function onThResizeMouseDown(e, col, leftHandle = false) {
262
263
  if (!tableContainerRef.value)
263
264
  return;
264
265
  e.stopPropagation();
@@ -267,19 +268,32 @@ function useColResize({
267
268
  const { scrollLeft, scrollTop } = tableContainerRef.value;
268
269
  const { left } = tableContainerRef.value.getBoundingClientRect();
269
270
  const tableHeaderLastValue = tableHeaderLast.value;
270
- let colIndex = tableHeaderLastValue.findIndex((it) => colKeyGen.value(it) === colKeyGen.value(col));
271
- if (isPrev) {
272
- colIndex -= 1;
273
- col = tableHeaderLastValue[colIndex];
271
+ let revertMoveX = false;
272
+ const colIndex = tableHeaderLastValue.findIndex((it) => colKeyGen.value(it) === colKeyGen.value(col));
273
+ const fixedIndex = fixedCols.value.indexOf(col);
274
+ const isFixed = fixedIndex !== -1;
275
+ if (leftHandle) {
276
+ if (isFixed && col.fixed === "right") {
277
+ revertMoveX = true;
278
+ } else {
279
+ if (colIndex - 1 >= 0) {
280
+ col = tableHeaderLastValue[colIndex - 1];
281
+ }
282
+ }
283
+ } else {
284
+ if (isFixed && col.fixed === "right") {
285
+ revertMoveX = true;
286
+ col = fixedCols.value[fixedIndex + 1] || col;
287
+ }
274
288
  }
275
289
  const offsetTableX = clientX - left + scrollLeft;
276
290
  isColResizing.value = true;
277
291
  Object.assign(colResizeState, {
278
292
  currentCol: col,
279
- currentColIndex: colIndex,
280
293
  lastCol: findLastChildCol(col),
281
294
  startX: clientX,
282
- startOffsetTableX: offsetTableX
295
+ startOffsetTableX: offsetTableX,
296
+ revertMoveX
283
297
  });
284
298
  if (colResizeIndicatorRef.value) {
285
299
  const style = colResizeIndicatorRef.value.style;
@@ -308,9 +322,9 @@ function useColResize({
308
322
  function onThResizeMouseUp(e) {
309
323
  if (!isColResizing.value)
310
324
  return;
311
- const { startX, lastCol } = colResizeState;
325
+ const { startX, lastCol, revertMoveX } = colResizeState;
312
326
  const { clientX } = e;
313
- const moveX = clientX - startX;
327
+ const moveX = revertMoveX ? startX - clientX : clientX - startX;
314
328
  let width = getCalculatedColWidth(lastCol) + moveX;
315
329
  if (width < props.colMinWidth)
316
330
  width = props.colMinWidth;
@@ -328,10 +342,10 @@ function useColResize({
328
342
  isColResizing.value = false;
329
343
  colResizeState = {
330
344
  currentCol: null,
331
- currentColIndex: 0,
332
345
  lastCol: null,
333
346
  startX: 0,
334
- startOffsetTableX: 0
347
+ startOffsetTableX: 0,
348
+ revertMoveX: false
335
349
  };
336
350
  }
337
351
  function findLastChildCol(column) {
@@ -435,6 +449,8 @@ function useFixedCol({
435
449
  fixedCols.value = fixedColsTemp;
436
450
  }
437
451
  return {
452
+ /** 正在被固定的列 */
453
+ fixedCols,
438
454
  /** 固定列class */
439
455
  fixedColClassMap,
440
456
  /** 滚动条变化时,更新需要展示阴影的列 */
@@ -1122,17 +1138,18 @@ const _hoisted_6 = [
1122
1138
  ];
1123
1139
  const _hoisted_7 = ["onMousedown"];
1124
1140
  const _hoisted_8 = ["onMousedown"];
1125
- const _hoisted_9 = {
1141
+ const _hoisted_9 = { class: "stk-tbody-main" };
1142
+ const _hoisted_10 = {
1126
1143
  key: 0,
1127
1144
  class: "vt-x-left"
1128
1145
  };
1129
- const _hoisted_10 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1130
- const _hoisted_11 = {
1146
+ const _hoisted_11 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1147
+ const _hoisted_12 = {
1131
1148
  key: 0,
1132
1149
  class: "vt-x-left"
1133
1150
  };
1134
- const _hoisted_12 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1135
- const _hoisted_13 = ["title"];
1151
+ const _hoisted_13 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1152
+ const _hoisted_14 = ["title"];
1136
1153
  const _sfc_main = /* @__PURE__ */ defineComponent({
1137
1154
  __name: "StkTable",
1138
1155
  props: {
@@ -1213,14 +1230,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1213
1230
  }
1214
1231
  });
1215
1232
  const rowKeyGenStore = /* @__PURE__ */ new WeakMap();
1216
- const { isColResizing, onThResizeMouseDown } = useColResize({
1217
- props,
1218
- emits,
1219
- colKeyGen,
1220
- colResizeIndicatorRef,
1221
- tableContainerRef,
1222
- tableHeaderLast
1223
- });
1224
1233
  const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits });
1225
1234
  const {
1226
1235
  virtualScroll,
@@ -1259,7 +1268,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1259
1268
  tableHeaders,
1260
1269
  virtual_on
1261
1270
  });
1262
- const { fixedColClassMap, updateFixedShadow } = useFixedCol({
1271
+ const { fixedCols, fixedColClassMap, updateFixedShadow } = useFixedCol({
1263
1272
  props,
1264
1273
  colKeyGen,
1265
1274
  getFixedColPosition,
@@ -1267,6 +1276,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1267
1276
  tableHeaders,
1268
1277
  tableHeaderLast
1269
1278
  });
1279
+ const { isColResizing, onThResizeMouseDown } = useColResize({
1280
+ props,
1281
+ emits,
1282
+ colKeyGen,
1283
+ colResizeIndicatorRef,
1284
+ tableContainerRef,
1285
+ tableHeaderLast,
1286
+ fixedCols
1287
+ });
1270
1288
  watch(
1271
1289
  () => props.columns,
1272
1290
  () => {
@@ -1468,12 +1486,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1468
1486
  const sortConfig = props.sortConfig;
1469
1487
  const defaultSort = sortConfig.defaultSort;
1470
1488
  if (!order && defaultSort) {
1471
- order = defaultSort.order;
1489
+ if (!defaultSort.dataIndex) {
1490
+ console.error("sortConfig.defaultSort.dataIndex is required");
1491
+ return;
1492
+ }
1493
+ order = defaultSort.order || "desc";
1472
1494
  sortOrderIndex.value = sortSwitchOrder.indexOf(order);
1473
1495
  sortCol.value = defaultSort.dataIndex;
1496
+ col = props.columns.find((item) => item.dataIndex === defaultSort.dataIndex) || null;
1474
1497
  }
1475
1498
  if (!props.sortRemote || options.force) {
1476
- dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
1499
+ const sortOption = col || defaultSort;
1500
+ if (sortOption) {
1501
+ dataSourceCopy.value = tableSort(sortOption, order, props.dataSource, sortConfig);
1502
+ }
1477
1503
  }
1478
1504
  if (click || options.emit) {
1479
1505
  emits("sort-change", col, order, toRaw(dataSourceCopy.value), sortConfig);
@@ -1582,6 +1608,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1582
1608
  function setCurrentRow(rowKey, option = { silent: false }) {
1583
1609
  if (!dataSourceCopy.value.length)
1584
1610
  return;
1611
+ if (rowKey === void 0) {
1612
+ currentRow.value = void 0;
1613
+ currentRowKey.value = void 0;
1614
+ }
1585
1615
  const row = dataSourceCopy.value.find((it) => rowKeyGen(it) === rowKey);
1586
1616
  if (!row) {
1587
1617
  console.warn("setCurrentRow failed.rowKey:", rowKey);
@@ -1668,6 +1698,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1668
1698
  class: normalizeClass(["stk-table", {
1669
1699
  virtual: _ctx.virtual,
1670
1700
  "virtual-x": _ctx.virtualX,
1701
+ "vt-on": unref(virtual_on),
1671
1702
  dark: _ctx.theme === "dark",
1672
1703
  headless: _ctx.headless,
1673
1704
  "is-col-resizing": unref(isColResizing),
@@ -1783,15 +1814,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1783
1814
  ], 32);
1784
1815
  }), 128))
1785
1816
  ], 512)) : createCommentVNode("", true),
1786
- createElementVNode("tbody", {
1787
- class: normalizeClass(["stk-tbody-main", unref(virtual_on) ? "vt-on" : "vt-off"])
1788
- }, [
1817
+ createElementVNode("tbody", _hoisted_9, [
1789
1818
  unref(virtual_on) ? (openBlock(), createElementBlock("tr", {
1790
1819
  key: 0,
1791
1820
  style: normalizeStyle(`height:${unref(virtualScroll).offsetTop}px`),
1792
1821
  class: "padding-top-tr"
1793
1822
  }, [
1794
- unref(virtualX_on) && _ctx.fixedMode && _ctx.headless ? (openBlock(), createElementBlock("td", _hoisted_9)) : createCommentVNode("", true),
1823
+ unref(virtualX_on) && _ctx.fixedMode && _ctx.headless ? (openBlock(), createElementBlock("td", _hoisted_10)) : createCommentVNode("", true),
1795
1824
  _ctx.fixedMode && _ctx.headless ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(virtualX_columnPart), (col) => {
1796
1825
  return openBlock(), createElementBlock("td", {
1797
1826
  key: col.dataIndex,
@@ -1814,7 +1843,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1814
1843
  onContextmenu: (e) => onRowMenu(e, row),
1815
1844
  onMouseover: (e) => onTrMouseOver(e, row)
1816
1845
  }, [
1817
- unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_11)) : createCommentVNode("", true),
1846
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_12)) : createCommentVNode("", true),
1818
1847
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_columnPart), (col, colIndex) => {
1819
1848
  return openBlock(), createElementBlock("td", {
1820
1849
  key: col.dataIndex,
@@ -1843,16 +1872,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1843
1872
  ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
1844
1873
  createTextVNode(toDisplayString((row == null ? void 0 : row[col.dataIndex]) ?? getEmptyCellText.value(col, row)), 1)
1845
1874
  ], 64))
1846
- ], 8, _hoisted_13))
1847
- ], 46, _hoisted_12);
1875
+ ], 8, _hoisted_14))
1876
+ ], 46, _hoisted_13);
1848
1877
  }), 128))
1849
- ], 42, _hoisted_10);
1878
+ ], 42, _hoisted_11);
1850
1879
  }), 128)),
1851
1880
  unref(virtual_on) ? (openBlock(), createElementBlock("tr", {
1852
1881
  key: 1,
1853
1882
  style: normalizeStyle(`height: ${unref(virtual_offsetBottom)}px`)
1854
1883
  }, null, 4)) : createCommentVNode("", true)
1855
- ], 2)
1884
+ ])
1856
1885
  ], 6),
1857
1886
  (!dataSourceCopy.value || !dataSourceCopy.value.length) && _ctx.showNoData ? (openBlock(), createElementBlock("div", {
1858
1887
  key: 1,
package/lib/style.css CHANGED
@@ -91,16 +91,22 @@
91
91
  .stk-table.border-body-v tbody{
92
92
  --bg-border-bottom:linear-gradient(transparent, transparent);
93
93
  }
94
- .stk-table.stripe .stk-tbody-main.vt-on tr:nth-child(odd){
94
+ .stk-table.stripe .stk-tbody-main tr:nth-child(even){
95
95
  background-color:var(--stripe-bgc);
96
96
  }
97
- .stk-table.stripe .stk-tbody-main.vt-off tr:nth-child(even){
97
+ .stk-table.stripe.vt-on .stk-tbody-main tr:nth-child(odd){
98
98
  background-color:var(--stripe-bgc);
99
99
  }
100
- .stk-table.row-hover tbody tr:hover{
100
+ .stk-table.stripe.row-hover .stk-tbody-main tr:hover{
101
101
  background-color:var(--tr-hover-bgc);
102
102
  }
103
- .stk-table.row-active tbody tr.active{
103
+ .stk-table.stripe.row-active .stk-tbody-main tr.active{
104
+ background-color:var(--tr-active-bgc);
105
+ }
106
+ .stk-table.row-hover .stk-tbody-main tr:hover{
107
+ background-color:var(--tr-hover-bgc);
108
+ }
109
+ .stk-table.row-active .stk-tbody-main tr.active{
104
110
  background-color:var(--tr-active-bgc);
105
111
  }
106
112
  .stk-table.cell-hover tbody td:hover{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "Simple realtime virtual table for vue3 and vue2.7",
5
5
  "main": "./lib/stk-table-vue.js",
6
6
  "types": "./lib/src/StkTable/index.d.ts",
@@ -6,6 +6,7 @@
6
6
  :class="{
7
7
  virtual,
8
8
  'virtual-x': virtualX,
9
+ 'vt-on': virtual_on,
9
10
  dark: theme === 'dark',
10
11
  headless,
11
12
  'is-col-resizing': isColResizing,
@@ -121,7 +122,7 @@
121
122
  <!-- 用于虚拟滚动表格内容定位 @deprecated 有兼容问题-->
122
123
  <!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }"></tbody> -->
123
124
  <!-- <tbody :style="{ transform: `translateY(${virtualScroll.offsetTop}px)` }"> -->
124
- <tbody class="stk-tbody-main" :class="virtual_on ? 'vt-on' : 'vt-off'">
125
+ <tbody class="stk-tbody-main">
125
126
  <tr v-if="virtual_on" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
126
127
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
127
128
  <td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
@@ -365,10 +366,10 @@ const props = withDefaults(
365
366
 
366
367
  const emits = defineEmits<{
367
368
  /**
368
- * 排序变更触发
369
- * ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
369
+ * 排序变更触发。defaultSort.dataIndex 找不到时,col 将返回null。
370
+ * ```(col: StkTableColumn<DT> | null, order: Order, data: DT[])```
370
371
  */
371
- (e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig<DT>): void;
372
+ (e: 'sort-change', col: StkTableColumn<DT> | null, order: Order, data: DT[], sortConfig: SortConfig<DT>): void;
372
373
  /**
373
374
  * 一行点击事件
374
375
  * ```(ev: MouseEvent, row: DT)```
@@ -531,15 +532,6 @@ const getEmptyCellText = computed(() => {
531
532
  /** rowKey缓存 */
532
533
  const rowKeyGenStore = new WeakMap();
533
534
 
534
- const { isColResizing, onThResizeMouseDown } = useColResize({
535
- props,
536
- emits,
537
- colKeyGen,
538
- colResizeIndicatorRef,
539
- tableContainerRef,
540
- tableHeaderLast,
541
- });
542
-
543
535
  const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits });
544
536
 
545
537
  const {
@@ -591,7 +583,7 @@ useKeyboardArrowScroll(tableContainerRef, {
591
583
  });
592
584
 
593
585
  /** 固定列处理 */
594
- const { fixedColClassMap, updateFixedShadow } = useFixedCol({
586
+ const { fixedCols, fixedColClassMap, updateFixedShadow } = useFixedCol({
595
587
  props,
596
588
  colKeyGen,
597
589
  getFixedColPosition,
@@ -600,6 +592,16 @@ const { fixedColClassMap, updateFixedShadow } = useFixedCol({
600
592
  tableHeaderLast,
601
593
  });
602
594
 
595
+ const { isColResizing, onThResizeMouseDown } = useColResize({
596
+ props,
597
+ emits,
598
+ colKeyGen,
599
+ colResizeIndicatorRef,
600
+ tableContainerRef,
601
+ tableHeaderLast,
602
+ fixedCols,
603
+ });
604
+
603
605
  watch(
604
606
  () => props.columns,
605
607
  () => {
@@ -839,7 +841,7 @@ function getHeaderTitle(col: StkTableColumn<DT>): string {
839
841
  * @param options.force sort-remote 开启后是否强制排序
840
842
  * @param options.emit 是否触发回调
841
843
  */
842
- function onColumnSort(col?: StkTableColumn<DT>, click = true, options: { force?: boolean; emit?: boolean } = {}) {
844
+ function onColumnSort(col: StkTableColumn<DT> | undefined | null, click = true, options: { force?: boolean; emit?: boolean } = {}) {
843
845
  if (!col?.sorter) return;
844
846
  options = { force: false, emit: false, ...options };
845
847
  if (sortCol.value !== col.dataIndex) {
@@ -855,13 +857,21 @@ function onColumnSort(col?: StkTableColumn<DT>, click = true, options: { force?:
855
857
  const defaultSort = sortConfig.defaultSort;
856
858
 
857
859
  if (!order && defaultSort) {
860
+ if (!defaultSort.dataIndex) {
861
+ console.error('sortConfig.defaultSort.dataIndex is required');
862
+ return;
863
+ }
858
864
  // 没有排序时变成默认排序
859
- order = defaultSort.order;
865
+ order = defaultSort.order || 'desc';
860
866
  sortOrderIndex.value = sortSwitchOrder.indexOf(order);
861
867
  sortCol.value = defaultSort.dataIndex as string;
868
+ col = props.columns.find(item => item.dataIndex === defaultSort.dataIndex) || null;
862
869
  }
863
870
  if (!props.sortRemote || options.force) {
864
- dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
871
+ const sortOption = col || defaultSort;
872
+ if (sortOption) {
873
+ dataSourceCopy.value = tableSort(sortOption, order, props.dataSource, sortConfig);
874
+ }
865
875
  }
866
876
  // 只有点击才触发事件
867
877
  if (click || options.emit) {
@@ -1017,11 +1027,15 @@ function onTrMouseOver(_e: MouseEvent, row: DT) {
1017
1027
 
1018
1028
  /**
1019
1029
  * 选中一行,
1020
- * @param {string} rowKey selected rowKey, null to unselect
1030
+ * @param {string} rowKey selected rowKey, undefined to unselect
1021
1031
  * @param {boolean} option.silent 是否触发回调
1022
1032
  */
1023
- function setCurrentRow(rowKey: string, option = { silent: false }) {
1033
+ function setCurrentRow(rowKey: string | undefined, option = { silent: false }) {
1024
1034
  if (!dataSourceCopy.value.length) return;
1035
+ if (rowKey === void 0) {
1036
+ currentRow.value = void 0;
1037
+ currentRowKey.value = void 0;
1038
+ }
1025
1039
  const row = dataSourceCopy.value.find(it => rowKeyGen(it) === rowKey);
1026
1040
  if (!row) {
1027
1041
  console.warn('setCurrentRow failed.rowKey:', rowKey);
@@ -134,23 +134,31 @@
134
134
  }
135
135
 
136
136
  /* 斑马纹*/
137
- &.stripe .stk-tbody-main {
138
-
139
- &.vt-on tr:nth-child(odd) {
137
+ &.stripe {
138
+ .stk-tbody-main tr:nth-child(even) {
140
139
  background-color: var(--stripe-bgc);
141
140
  }
142
141
 
143
- &.vt-off tr:nth-child(even) {
142
+ &.vt-on .stk-tbody-main tr:nth-child(odd) {
144
143
  background-color: var(--stripe-bgc);
145
144
  }
145
+
146
+ &.row-hover .stk-tbody-main tr:hover {
147
+ background-color: var(--tr-hover-bgc);
148
+ }
149
+
150
+ &.row-active .stk-tbody-main tr.active {
151
+ background-color: var(--tr-active-bgc);
152
+ }
146
153
  }
147
154
 
148
- &.row-hover tbody tr:hover {
155
+
156
+ &.row-hover .stk-tbody-main tr:hover {
149
157
  background-color: var(--tr-hover-bgc);
150
158
  }
151
159
 
152
160
 
153
- &.row-active tbody tr.active {
161
+ &.row-active .stk-tbody-main tr.active {
154
162
  background-color: var(--tr-active-bgc);
155
163
  }
156
164
 
@@ -132,8 +132,11 @@ export type SortConfig<T extends Record<string, any>> = {
132
132
  * 类似onMounted时,调用setSorter点了下表头。
133
133
  */
134
134
  defaultSort?: {
135
- dataIndex: keyof T;
135
+ dataIndex: StkTableColumn<T>['dataIndex'];
136
136
  order: Order;
137
+ sortField?: StkTableColumn<T>['sortField'];
138
+ sortType?: StkTableColumn<T>['sortType'];
139
+ sorter?: StkTableColumn<T>['sorter'];
137
140
  /** 是否禁止触发sort-change事件。默认false,表示触发事件。 */
138
141
  silent?: boolean;
139
142
  };
@@ -5,14 +5,14 @@ import { getCalculatedColWidth } from './utils';
5
5
  type ColResizeState<DT extends Record<string, any>> = {
6
6
  /** 当前被拖动的列*/
7
7
  currentCol: StkTableColumn<DT> | null;
8
- /** 当前被拖动列的下标 */
9
- currentColIndex: number;
10
8
  /** 最后一个叶子列 */
11
9
  lastCol: StkTableColumn<DT> | null;
12
10
  /** 鼠标按下开始位置 */
13
11
  startX: number;
14
12
  /** 鼠标按下时鼠标对于表格的偏移量 */
15
13
  startOffsetTableX: 0;
14
+ /** 是否反向计算,true:左增右减。false:左减右增 */
15
+ revertMoveX: boolean;
16
16
  };
17
17
 
18
18
  type Params<DT extends Record<string, any>> = {
@@ -22,6 +22,7 @@ type Params<DT extends Record<string, any>> = {
22
22
  tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
23
23
  colResizeIndicatorRef: Ref<HTMLElement | undefined>;
24
24
  colKeyGen: ComputedRef<(p: any) => UniqKey>;
25
+ fixedCols: Ref<StkTableColumn<DT>[]>;
25
26
  };
26
27
 
27
28
  /** 列宽拖动 */
@@ -32,6 +33,7 @@ export function useColResize<DT extends Record<string, any>>({
32
33
  props,
33
34
  emits,
34
35
  colKeyGen,
36
+ fixedCols,
35
37
  }: Params<DT>) {
36
38
  /** 列宽是否在拖动 */
37
39
  const isColResizing = ref(false);
@@ -39,10 +41,10 @@ export function useColResize<DT extends Record<string, any>>({
39
41
  /** 列宽调整状态 */
40
42
  let colResizeState: ColResizeState<DT> = {
41
43
  currentCol: null,
42
- currentColIndex: 0,
43
44
  lastCol: null,
44
45
  startX: 0,
45
46
  startOffsetTableX: 0,
47
+ revertMoveX: false,
46
48
  };
47
49
 
48
50
  onMounted(() => {
@@ -68,9 +70,9 @@ export function useColResize<DT extends Record<string, any>>({
68
70
  * 拖动开始
69
71
  * @param e
70
72
  * @param col 当前列配置
71
- * @param isPrev 是否要上一列
73
+ * @param leftHandle 是否是左侧的拖动条
72
74
  */
73
- function onThResizeMouseDown(e: MouseEvent, col: StkTableColumn<DT>, isPrev = false) {
75
+ function onThResizeMouseDown(e: MouseEvent, col: StkTableColumn<DT>, leftHandle = false) {
74
76
  if (!tableContainerRef.value) return;
75
77
  e.stopPropagation();
76
78
  e.preventDefault();
@@ -78,23 +80,43 @@ export function useColResize<DT extends Record<string, any>>({
78
80
  const { scrollLeft, scrollTop } = tableContainerRef.value;
79
81
  const { left } = tableContainerRef.value.getBoundingClientRect();
80
82
  const tableHeaderLastValue = tableHeaderLast.value;
83
+ let revertMoveX = false;
81
84
  /** 列下标 */
82
- let colIndex = tableHeaderLastValue.findIndex(it => colKeyGen.value(it) === colKeyGen.value(col));
83
- if (isPrev) {
84
- // 上一列
85
- colIndex -= 1;
86
- col = tableHeaderLastValue[colIndex];
85
+ const colIndex = tableHeaderLastValue.findIndex(it => colKeyGen.value(it) === colKeyGen.value(col));
86
+ const fixedIndex = fixedCols.value.indexOf(col);
87
+ /** 是否正在被固定 */
88
+ const isFixed = fixedIndex !== -1;
89
+
90
+ if (leftHandle) {
91
+ // 左侧拖动条
92
+ if (isFixed && col.fixed === 'right') {
93
+ // 对于固定右侧的列,拖动左侧的把,需要反向计算
94
+ revertMoveX = true;
95
+ } else {
96
+ // 默认拖动右侧的把,取上一列
97
+ if (colIndex - 1 >= 0) {
98
+ col = tableHeaderLastValue[colIndex - 1];
99
+ }
100
+ }
101
+ } else {
102
+ // 右侧拖动条
103
+ if (isFixed && col.fixed === 'right') {
104
+ // 对于固定右侧的列,拖动右侧的把,需要拖动下一固定的列
105
+ revertMoveX = true;
106
+ col = fixedCols.value[fixedIndex + 1] || col;
107
+ }
87
108
  }
109
+
88
110
  const offsetTableX = clientX - left + scrollLeft;
89
111
 
90
112
  // 记录拖动状态
91
113
  isColResizing.value = true;
92
114
  Object.assign(colResizeState, {
93
115
  currentCol: col,
94
- currentColIndex: colIndex,
95
116
  lastCol: findLastChildCol(col),
96
117
  startX: clientX,
97
118
  startOffsetTableX: offsetTableX,
119
+ revertMoveX,
98
120
  });
99
121
 
100
122
  // 展示指示线,更新其位置
@@ -132,9 +154,9 @@ export function useColResize<DT extends Record<string, any>>({
132
154
  */
133
155
  function onThResizeMouseUp(e: MouseEvent) {
134
156
  if (!isColResizing.value) return;
135
- const { startX, lastCol } = colResizeState;
157
+ const { startX, lastCol, revertMoveX } = colResizeState;
136
158
  const { clientX } = e;
137
- const moveX = clientX - startX;
159
+ const moveX = revertMoveX ? startX - clientX : clientX - startX;
138
160
 
139
161
  // 移动量不小于最小列宽
140
162
  let width = getCalculatedColWidth(lastCol) + moveX;
@@ -157,10 +179,10 @@ export function useColResize<DT extends Record<string, any>>({
157
179
  isColResizing.value = false;
158
180
  colResizeState = {
159
181
  currentCol: null,
160
- currentColIndex: 0,
161
182
  lastCol: null,
162
183
  startX: 0,
163
184
  startOffsetTableX: 0,
185
+ revertMoveX: false,
164
186
  };
165
187
  }
166
188
 
@@ -125,6 +125,8 @@ export function useFixedCol<DT extends Record<string, any>>({
125
125
  }
126
126
 
127
127
  return {
128
+ /** 正在被固定的列 */
129
+ fixedCols,
128
130
  /** 固定列class */
129
131
  fixedColClassMap,
130
132
  /** 滚动条变化时,更新需要展示阴影的列 */
@@ -3,7 +3,7 @@ import { Order, SortConfig, SortOption, SortState, StkTableColumn } from '../typ
3
3
 
4
4
  /** 是否空值 */
5
5
  function isEmptyValue(val: any, isNumber?: boolean) {
6
- let isEmpty = val === null || val === '';
6
+ let isEmpty = val === null || val === '' || val === void 0;
7
7
  if (isNumber) {
8
8
  isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
9
9
  }
@@ -104,7 +104,7 @@ function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>,
104
104
  for (let i = 0; i < targetDataSource.length; i++) {
105
105
  const row = targetDataSource[i];
106
106
  const sortField = sortOption.sortField || sortOption.dataIndex;
107
- const isEmpty = isEmptyValue(row[sortField], isNumber);
107
+ const isEmpty = isEmptyValue(row?.[sortField], isNumber); // deal row is null
108
108
  if (isEmpty) {
109
109
  emptyArr.push(row);
110
110
  } else {
@@ -131,7 +131,8 @@ export function tableSort<T extends Record<string, any>>(
131
131
  dataSource: T[],
132
132
  sortConfig: SortConfig<T> = {},
133
133
  ): T[] {
134
- if (!dataSource?.length) return dataSource || [];
134
+ if (!dataSource?.length || !sortOption) return dataSource || [];
135
+
135
136
  sortConfig = { emptyToBottom: false, ...sortConfig };
136
137
  let targetDataSource = [...dataSource];
137
138
  let sortField = sortOption.sortField || sortOption.dataIndex;
@@ -149,8 +150,8 @@ export function tableSort<T extends Record<string, any>>(
149
150
  let { sortType } = sortOption;
150
151
  if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
151
152
 
152
- const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, sortType === 'number');
153
153
  const isNumber = sortType === 'number';
154
+ const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, isNumber);
154
155
 
155
156
  if (order === 'asc') {
156
157
  valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, sortConfig.stringLocaleCompare));