stk-table-vue 0.3.2 → 0.3.3

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
@@ -231,6 +231,14 @@ export type StkProps = {
231
231
  /** 序号列起始下标 用于适配分页 */
232
232
  startIndex?: number;
233
233
  };
234
+ /**
235
+ * 固定头,固定列实现方式。
236
+ *
237
+ * relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
238
+ *
239
+ * 低版本浏览器只能为'relative',
240
+ */
241
+ cellFixedMode?: 'sticky' | 'relative';
234
242
  };
235
243
  ```
236
244
 
@@ -545,6 +553,9 @@ export type SortConfig<T extends Record<string, any>> = {
545
553
  * 在虚拟滚动下高亮强制使用css @keyframes 实现动画。`setHighlightDimRow`/`setHighlightCellRow` 最后一个参数传入 `{method: 'css'}` 即可。(滚动后动画会中断)
546
554
  * 指定 `{method:'animation'}` 在虚拟滚动下使用animation api实现动画。好处是动画流畅,且滚动后动画不中断。
547
555
  * 配置 `props.highlightConfig.fps` 指定高亮帧率。降低帧率有利于性能。
556
+ ### 性能
557
+ * 配置 `props.cellFixedMode` 为 `relative` 时,将使用相对定位实现固定列与固定表头,相较于`sticky`的实现,渲染合成层更少,性能更好。
558
+ * 问题:若开启了纵向虚拟滚动,不开启横向虚拟滚动,且不设置某些列宽时。如果纵向滚动导致某些列宽变化,则会导致右侧固定列计算错误。
548
559
 
549
560
  ## Other
550
561
  * `$*$` 兼容注释
@@ -1,4 +1,4 @@
1
- import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, UniqKeyProp } from './types/index';
1
+ import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, UniqKey, UniqKeyProp } from './types/index';
2
2
  /** Generic stands for DataType */
3
3
  type DT = any;
4
4
  /**
@@ -126,6 +126,14 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
126
126
  highlightConfig?: HighlightConfig | undefined;
127
127
  /** 序号列配置 */
128
128
  seqConfig?: SeqConfig | undefined;
129
+ /**
130
+ * 固定头,固定列实现方式。
131
+ *
132
+ * relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
133
+ *
134
+ * 低版本浏览器只能为'relative',
135
+ */
136
+ cellFixedMode?: "sticky" | "relative" | undefined;
129
137
  }>, {
130
138
  width: string;
131
139
  fixedMode: boolean;
@@ -165,6 +173,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
165
173
  hideHeaderTitle: boolean;
166
174
  highlightConfig: () => {};
167
175
  seqConfig: () => {};
176
+ cellFixedMode: string;
168
177
  }>, {
169
178
  /** 初始化横向纵向虚拟滚动 */
170
179
  initVirtualScroll: (height?: number | undefined) => void;
@@ -182,7 +191,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
182
191
  duration?: number | undefined;
183
192
  }) => void;
184
193
  /** 设置高亮渐暗行 */
185
- setHighlightDimRow: (rowKeyValues: import("./types/index").UniqKey[], option?: {
194
+ setHighlightDimRow: (rowKeyValues: UniqKey[], option?: {
186
195
  method?: "css" | "animation" | "js" | undefined;
187
196
  useCss?: boolean | undefined;
188
197
  className?: string | undefined;
@@ -314,6 +323,14 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
314
323
  highlightConfig?: HighlightConfig | undefined;
315
324
  /** 序号列配置 */
316
325
  seqConfig?: SeqConfig | undefined;
326
+ /**
327
+ * 固定头,固定列实现方式。
328
+ *
329
+ * relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
330
+ *
331
+ * 低版本浏览器只能为'relative',
332
+ */
333
+ cellFixedMode?: "sticky" | "relative" | undefined;
317
334
  }>, {
318
335
  width: string;
319
336
  fixedMode: boolean;
@@ -353,6 +370,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
353
370
  hideHeaderTitle: boolean;
354
371
  highlightConfig: () => {};
355
372
  seqConfig: () => {};
373
+ cellFixedMode: string;
356
374
  }>>> & {
357
375
  onScroll?: ((ev: Event, data: {
358
376
  startIndex: number;
@@ -415,6 +433,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
415
433
  hideHeaderTitle: boolean | string[];
416
434
  highlightConfig: HighlightConfig;
417
435
  seqConfig: SeqConfig;
436
+ cellFixedMode: "sticky" | "relative";
418
437
  }, {}>, {
419
438
  tableHeader?(_: {
420
439
  col: StkTableColumn<any>;
@@ -1,12 +1,12 @@
1
- import { Ref, ShallowRef } from 'vue';
2
- import { StkTableColumn } from './types';
1
+ import { ComputedRef, Ref, ShallowRef } from 'vue';
2
+ import { StkTableColumn, UniqKey } from './types';
3
3
  type Params<DT extends Record<string, any>> = {
4
4
  props: any;
5
5
  emits: any;
6
6
  tableContainerRef: Ref<HTMLElement | undefined>;
7
7
  tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
8
8
  colResizeIndicatorRef: Ref<HTMLElement | undefined>;
9
- colKeyGen: (p: any) => string;
9
+ colKeyGen: ComputedRef<(p: any) => UniqKey>;
10
10
  };
11
11
  /** 列宽拖动 */
12
12
  export declare function useColResize<DT extends Record<string, any>>({ tableContainerRef, tableHeaderLast, colResizeIndicatorRef, props, emits, colKeyGen, }: Params<DT>): {
@@ -1,8 +1,8 @@
1
- import { Ref, ShallowRef } from 'vue';
2
- import { StkTableColumn } from './types';
1
+ import { ComputedRef, Ref, ShallowRef } from 'vue';
2
+ import { StkTableColumn, UniqKey } from './types';
3
3
  type Params<T extends Record<string, any>> = {
4
4
  props: any;
5
- colKeyGen: (col: StkTableColumn<T>) => string;
5
+ colKeyGen: ComputedRef<(col: StkTableColumn<T>) => UniqKey>;
6
6
  tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
7
7
  tableHeaderLast: ShallowRef<StkTableColumn<T>[]>;
8
8
  tableContainerRef: Ref<HTMLDivElement | undefined>;
@@ -13,7 +13,7 @@ type Params<T extends Record<string, any>> = {
13
13
  */
14
14
  export declare function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, tableHeaders, tableHeaderLast, tableContainerRef }: Params<DT>): {
15
15
  /** 固定列class */
16
- fixedColClassMap: import("vue").ComputedRef<Map<any, any>>;
16
+ fixedColClassMap: ComputedRef<Map<any, any>>;
17
17
  /** 处理固定列阴影 */
18
18
  dealFixedColShadow: () => void;
19
19
  /** 滚动条变化时,更新需要展示阴影的列 */
@@ -26,8 +26,10 @@ export type VirtualScrollStore = {
26
26
  };
27
27
  /** 暂存横向虚拟滚动的数据 */
28
28
  export type VirtualScrollXStore = {
29
- /** 容器宽度 */
29
+ /** 父容器宽度 */
30
30
  containerWidth: number;
31
+ /** 滚动容器的宽度 */
32
+ scrollWidth: number;
31
33
  /** 开始位置 */
32
34
  startIndex: number;
33
35
  /** 结束始位置 */
@@ -54,6 +56,7 @@ export declare function useVirtualScroll<DT extends Record<string, any>>({ props
54
56
  }>;
55
57
  virtualScrollX: Ref<{
56
58
  containerWidth: number;
59
+ scrollWidth: number;
57
60
  startIndex: number;
58
61
  endIndex: number;
59
62
  offsetLeft: number;
@@ -1,4 +1,4 @@
1
- import { onMounted, onBeforeUnmount, watch, ref, shallowRef, computed, defineComponent, toRaw, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createCommentVNode, createElementVNode, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, renderSlot, createTextVNode } from "vue";
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
  const DEFAULT_COL_WIDTH = "100";
4
4
  const DEFAULT_TABLE_HEIGHT = 100;
@@ -260,7 +260,7 @@ function useColResize({
260
260
  const { clientX } = e;
261
261
  const { scrollLeft, scrollTop } = tableContainerRef.value;
262
262
  const { left } = tableContainerRef.value.getBoundingClientRect();
263
- let colIndex = tableHeaderLast.value.findIndex((it) => colKeyGen(it) === colKeyGen(col));
263
+ let colIndex = tableHeaderLast.value.findIndex((it) => colKeyGen.value(it) === colKeyGen.value(col));
264
264
  if (isPrev) {
265
265
  colIndex -= 1;
266
266
  col = tableHeaderLast.value[colIndex];
@@ -307,7 +307,7 @@ function useColResize({
307
307
  let width = getCalculatedColWidth(lastCol) + moveX;
308
308
  if (width < props.colMinWidth)
309
309
  width = props.colMinWidth;
310
- const curCol = tableHeaderLast.value.find((it) => colKeyGen(it) === colKeyGen(lastCol));
310
+ const curCol = tableHeaderLast.value.find((it) => colKeyGen.value(it) === colKeyGen.value(lastCol));
311
311
  if (!curCol)
312
312
  return;
313
313
  curCol.width = width + "px";
@@ -359,7 +359,7 @@ function useFixedCol({ props, colKeyGen, tableHeaders, tableHeaderLast, tableCon
359
359
  ["fixed-cell--" + col.fixed]: col.fixed,
360
360
  "fixed-cell--shadow": showShadow
361
361
  };
362
- colMap.set(colKeyGen(col), classObj);
362
+ colMap.set(colKeyGen.value(col), classObj);
363
363
  });
364
364
  });
365
365
  return colMap;
@@ -453,33 +453,45 @@ function useFixedStyle({
453
453
  const { fixed } = col;
454
454
  if (tagType === TagType.TD && !fixed)
455
455
  return null;
456
- const isFixedLeft = fixed === "left";
457
456
  const style = {};
458
457
  const { colKeyStore, refStore } = fixedColumnsPositionStore.value;
458
+ let isRelativeMode = true;
459
+ if (props.cellFixedMode === "sticky") {
460
+ isRelativeMode = false;
461
+ }
459
462
  if (IS_LEGACY_MODE) {
463
+ isRelativeMode = true;
464
+ }
465
+ const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
466
+ const scrollRight = scrollWidth - containerWidth - scrollLeft;
467
+ if (virtualScrollX.value.scrollLeft === 0 && fixed === "left" && tagType === TagType.TD) {
468
+ style.position = void 0;
469
+ } else if (scrollRight === 0 && fixed === "right" && tagType === TagType.TD) {
470
+ style.position = void 0;
471
+ } else if (isRelativeMode) {
460
472
  style.position = "relative";
461
473
  } else {
462
474
  style.position = "sticky";
463
475
  }
476
+ const isFixedLeft = fixed === "left";
464
477
  if (tagType === TagType.TH) {
465
- if (IS_LEGACY_MODE) {
478
+ if (isRelativeMode) {
466
479
  style.top = virtualScroll.value.scrollTop + "px";
467
480
  } else {
468
481
  style.top = depth * props.rowHeight + "px";
469
482
  }
470
483
  style.zIndex = isFixedLeft ? "3" : "2";
471
484
  } else {
472
- style.zIndex = isFixedLeft ? "2" : "1";
485
+ if (isFixedLeft) {
486
+ style.zIndex = "2";
487
+ }
473
488
  }
474
489
  if (fixed === "left" || fixed === "right") {
475
- if (IS_LEGACY_MODE) {
490
+ if (isRelativeMode) {
476
491
  if (isFixedLeft) {
477
- if (virtualX_on.value)
478
- style.left = virtualScrollX.value.scrollLeft - virtualScrollX.value.offsetLeft + "px";
479
- else
480
- style.left = virtualScrollX.value.scrollLeft + "px";
492
+ style.left = scrollLeft - (virtualX_on.value ? offsetLeft : 0) + "px";
481
493
  } else {
482
- style.right = `${virtualX_offsetRight.value}px`;
494
+ style.right = Math.max(scrollRight - (virtualX_on.value ? virtualX_offsetRight.value : 0), 0) + "px";
483
495
  }
484
496
  } else {
485
497
  const lr = (col.dataIndex ? colKeyStore[col.dataIndex] : refStore.get(col)) + "px";
@@ -837,6 +849,7 @@ function useVirtualScroll({
837
849
  });
838
850
  const virtualScrollX = ref({
839
851
  containerWidth: 0,
852
+ scrollWidth: 0,
840
853
  startIndex: 0,
841
854
  endIndex: 0,
842
855
  offsetLeft: 0,
@@ -913,10 +926,9 @@ function useVirtualScroll({
913
926
  updateVirtualScrollY(scrollTop);
914
927
  }
915
928
  function initVirtualScrollX() {
916
- if (!props.virtualX)
917
- return;
918
- const { offsetWidth, scrollLeft } = tableContainerRef.value || {};
919
- virtualScrollX.value.containerWidth = offsetWidth || DEFAULT_TABLE_WIDTH;
929
+ const { clientWidth, scrollLeft, scrollWidth } = tableContainerRef.value || {};
930
+ virtualScrollX.value.containerWidth = clientWidth || DEFAULT_TABLE_WIDTH;
931
+ virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
920
932
  updateVirtualScrollX(scrollLeft);
921
933
  }
922
934
  function initVirtualScroll(height) {
@@ -969,6 +981,8 @@ function useVirtualScroll({
969
981
  let vue2ScrollXTimeout = null;
970
982
  function updateVirtualScrollX(sLeft = 0) {
971
983
  var _a;
984
+ if (!props.virtualX)
985
+ return;
972
986
  const headerLength = (_a = tableHeaderLast.value) == null ? void 0 : _a.length;
973
987
  const { scrollLeft } = virtualScrollX.value;
974
988
  if (!headerLength)
@@ -1068,14 +1082,12 @@ const _hoisted_9 = ["onMousedown"];
1068
1082
  const _hoisted_10 = ["onMousedown"];
1069
1083
  const _hoisted_11 = {
1070
1084
  key: 0,
1071
- class: "virtual-x-left",
1072
- style: { "padding": "0" }
1085
+ class: "virtual-x-left"
1073
1086
  };
1074
1087
  const _hoisted_12 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1075
1088
  const _hoisted_13 = {
1076
1089
  key: 0,
1077
- class: "virtual-x-left",
1078
- style: { "padding": "0" }
1090
+ class: "virtual-x-left"
1079
1091
  };
1080
1092
  const _hoisted_14 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1081
1093
  const _hoisted_15 = ["title"];
@@ -1119,7 +1131,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1119
1131
  }) },
1120
1132
  hideHeaderTitle: { type: [Boolean, Array], default: false },
1121
1133
  highlightConfig: { default: () => ({}) },
1122
- seqConfig: { default: () => ({}) }
1134
+ seqConfig: { default: () => ({}) },
1135
+ cellFixedMode: { default: "sticky" }
1123
1136
  },
1124
1137
  emits: ["sort-change", "row-click", "current-change", "row-dblclick", "header-row-menu", "row-menu", "cell-click", "cell-mouseenter", "cell-mouseleave", "cell-mouseover", "header-cell-click", "scroll", "scroll-x", "col-order-change", "th-drag-start", "th-drop", "update:columns"],
1125
1138
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -1138,6 +1151,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1138
1151
  const tableHeaders = shallowRef([]);
1139
1152
  const tableHeaderLast = shallowRef([]);
1140
1153
  const dataSourceCopy = shallowRef([...props.dataSource]);
1154
+ const colKeyGen = computed(() => {
1155
+ if (typeof props.colKey === "function") {
1156
+ return (col) => props.colKey(col);
1157
+ } else {
1158
+ return (col) => col[props.colKey];
1159
+ }
1160
+ });
1141
1161
  const getEmptyCellText = computed(() => {
1142
1162
  if (typeof props.emptyCellText === "string") {
1143
1163
  return () => props.emptyCellText;
@@ -1201,14 +1221,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1201
1221
  () => props.columns,
1202
1222
  () => {
1203
1223
  dealColumns();
1204
- initVirtualScrollX();
1224
+ nextTick(initVirtualScrollX);
1205
1225
  }
1206
1226
  );
1207
1227
  watch(
1208
1228
  () => props.virtualX,
1209
- () => dealColumns()
1229
+ () => {
1230
+ dealColumns();
1231
+ nextTick(initVirtualScrollX);
1232
+ }
1210
1233
  );
1211
- dealColumns();
1212
1234
  watch(
1213
1235
  () => props.dataSource,
1214
1236
  (val) => {
@@ -1234,6 +1256,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1234
1256
  }
1235
1257
  );
1236
1258
  watch(() => props.fixedColShadow, dealFixedColShadow);
1259
+ dealColumns();
1237
1260
  onMounted(() => {
1238
1261
  initVirtualScroll();
1239
1262
  updateFixedShadow();
@@ -1305,15 +1328,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1305
1328
  }
1306
1329
  return key;
1307
1330
  }
1308
- function colKeyGen(col) {
1309
- return typeof props.colKey === "function" ? props.colKey(col) : col[props.colKey];
1310
- }
1311
1331
  const cellStyleMap = computed(() => {
1312
1332
  const thMap = /* @__PURE__ */ new Map();
1313
1333
  const tdMap = /* @__PURE__ */ new Map();
1314
1334
  tableHeaders.value.forEach((cols, depth) => {
1315
1335
  cols.forEach((col) => {
1316
- const colKey = colKeyGen(col);
1336
+ const colKey = colKeyGen.value(col);
1317
1337
  const width = props.virtualX ? getCalculatedColWidth(col) + "px" : transformWidthToStr(col.width);
1318
1338
  const style = {
1319
1339
  width
@@ -1592,17 +1612,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1592
1612
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_on) && rowIndex === tableHeaders.value.length - 1 ? unref(virtualX_columnPart) : row, (col, colIndex) => {
1593
1613
  return openBlock(), createElementBlock("th", {
1594
1614
  key: col.dataIndex,
1595
- "data-col-key": colKeyGen(col),
1615
+ "data-col-key": colKeyGen.value(col),
1596
1616
  draggable: unref(isHeaderDraggable)(col) ? "true" : "false",
1597
1617
  rowspan: unref(virtualX_on) ? 1 : col.rowSpan,
1598
1618
  colspan: col.colSpan,
1599
- style: normalizeStyle(cellStyleMap.value[unref(TagType).TH].get(colKeyGen(col))),
1619
+ style: normalizeStyle(cellStyleMap.value[unref(TagType).TH].get(colKeyGen.value(col))),
1600
1620
  title: getHeaderTitle(col),
1601
1621
  class: normalizeClass([
1602
1622
  col.sorter ? "sortable" : "",
1603
1623
  col.dataIndex === unref(sortCol) && unref(sortOrderIndex) !== 0 && "sorter-" + sortSwitchOrder[unref(sortOrderIndex)],
1604
1624
  col.headerClassName,
1605
- unref(fixedColClassMap).get(colKeyGen(col))
1625
+ unref(fixedColClassMap).get(colKeyGen.value(col))
1606
1626
  ]),
1607
1627
  onClick: (e) => {
1608
1628
  onColumnSort(col);
@@ -1660,7 +1680,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1660
1680
  _ctx.fixedMode && _ctx.headless ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(virtualX_columnPart), (col) => {
1661
1681
  return openBlock(), createElementBlock("td", {
1662
1682
  key: col.dataIndex,
1663
- style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen(col)))
1683
+ style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen.value(col)))
1664
1684
  }, null, 4);
1665
1685
  }), 128)) : createCommentVNode("", true)
1666
1686
  ], 4)) : createCommentVNode("", true),
@@ -1684,8 +1704,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1684
1704
  return openBlock(), createElementBlock("td", {
1685
1705
  key: col.dataIndex,
1686
1706
  "data-index": col.dataIndex,
1687
- style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen(col))),
1688
- class: normalizeClass([col.className, unref(fixedColClassMap).get(colKeyGen(col)), col.type === "seq" ? "seq-column" : ""]),
1707
+ style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen.value(col))),
1708
+ class: normalizeClass([col.className, unref(fixedColClassMap).get(colKeyGen.value(col)), col.type === "seq" ? "seq-column" : ""]),
1689
1709
  onClick: (e) => onCellClick(e, row, col),
1690
1710
  onMouseenter: (e) => onCellMouseEnter(e, row, col),
1691
1711
  onMouseleave: (e) => onCellMouseLeave(e, row, col),
package/lib/style.css CHANGED
@@ -88,15 +88,6 @@
88
88
  .stk-table.border thead tr:first-child th{
89
89
  background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
90
90
  }
91
- .stk-table.border.virtual-x .virtual-x-left{
92
- background:none;
93
- pointer-events:none;
94
- }
95
- .stk-table.border.virtual-x .virtual-x-right{
96
- padding:0;
97
- background:none;
98
- pointer-events:none;
99
- }
100
91
  .stk-table.border-body-v tbody{
101
92
  --bg-border-bottom:linear-gradient(transparent, transparent);
102
93
  }
@@ -136,9 +127,6 @@
136
127
  .stk-table.virtual .padding-top-tr td{
137
128
  height:0;
138
129
  }
139
- .stk-table.virtual-x .virtual-x-left{
140
- padding:0;
141
- }
142
130
  .stk-table th,
143
131
  .stk-table td{
144
132
  z-index:1;
@@ -197,6 +185,12 @@
197
185
  .stk-table .stk-table-main tbody tr.active{
198
186
  background-color:var(--tr-active-bgc);
199
187
  }
188
+ .stk-table .virtual-x-left,
189
+ .stk-table .virtual-x-right{
190
+ padding:0;
191
+ background:none;
192
+ pointer-events:none;
193
+ }
200
194
  .stk-table .column-resize-indicator{
201
195
  width:0;
202
196
  height:100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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",
@@ -142,7 +142,7 @@
142
142
  <tbody>
143
143
  <tr v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }" class="padding-top-tr">
144
144
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
145
- <td v-if="virtualX_on && fixedMode && headless" class="virtual-x-left" style="padding: 0"></td>
145
+ <td v-if="virtualX_on && fixedMode && headless" class="virtual-x-left"></td>
146
146
  <template v-if="fixedMode && headless">
147
147
  <td v-for="col in virtualX_columnPart" :key="col.dataIndex" :style="cellStyleMap[TagType.TD].get(colKeyGen(col))"></td
148
148
  ></template>
@@ -163,7 +163,7 @@
163
163
  @mouseover="e => onTrMouseOver(e, row)"
164
164
  >
165
165
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
166
- <td v-if="virtualX_on" class="virtual-x-left" style="padding: 0"></td>
166
+ <td v-if="virtualX_on" class="virtual-x-left"></td>
167
167
  <td
168
168
  v-for="(col, colIndex) in virtualX_columnPart"
169
169
  :key="col.dataIndex"
@@ -212,9 +212,9 @@
212
212
  * [] 计算的高亮颜色,挂在数据源上对象上,若多个表格使用同一个数据源对象会有问题。需要深拷贝。(解决方案:获取组件uid)
213
213
  * [] highlight-row 颜色不能恢复到active的颜色
214
214
  */
215
- import { CSSProperties, computed, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
215
+ import { CSSProperties, computed, nextTick, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
216
216
  import { DEFAULT_ROW_HEIGHT } from './const';
217
- import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKeyProp } from './types/index';
217
+ import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKey, UniqKeyProp } from './types/index';
218
218
  import { useAutoResize } from './useAutoResize';
219
219
  import { useColResize } from './useColResize';
220
220
  import { useFixedCol } from './useFixedCol';
@@ -320,6 +320,14 @@ const props = withDefaults(
320
320
  highlightConfig?: HighlightConfig;
321
321
  /** 序号列配置 */
322
322
  seqConfig?: SeqConfig;
323
+ /**
324
+ * 固定头,固定列实现方式。
325
+ *
326
+ * relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
327
+ *
328
+ * 低版本浏览器只能为'relative',
329
+ */
330
+ cellFixedMode?: 'sticky' | 'relative';
323
331
  }>(),
324
332
  {
325
333
  width: '',
@@ -360,6 +368,7 @@ const props = withDefaults(
360
368
  hideHeaderTitle: false,
361
369
  highlightConfig: () => ({}),
362
370
  seqConfig: () => ({}),
371
+ cellFixedMode: 'sticky',
363
372
  },
364
373
  );
365
374
 
@@ -496,6 +505,18 @@ const tableHeaderLast = shallowRef<StkTableColumn<DT>[]>([]);
496
505
 
497
506
  const dataSourceCopy = shallowRef<DT[]>([...props.dataSource]);
498
507
 
508
+ /**
509
+ * 列唯一键
510
+ * @param col
511
+ */
512
+ const colKeyGen = computed(() => {
513
+ if (typeof props.colKey === 'function') {
514
+ return (col: StkTableColumn<DT>) => (props.colKey as (col: StkTableColumn<DT>) => string)(col);
515
+ } else {
516
+ return (col: StkTableColumn<DT>) => (col as any)[props.colKey as string];
517
+ }
518
+ });
519
+
499
520
  /**高亮帧间隔
500
521
  const highlightStepDuration = Highlight_Color_Change_Freq / 1000 + 's';*/
501
522
 
@@ -579,16 +600,17 @@ watch(
579
600
  () => props.columns,
580
601
  () => {
581
602
  dealColumns();
582
- initVirtualScrollX();
603
+ nextTick(initVirtualScrollX);
583
604
  },
584
605
  );
585
606
  watch(
586
607
  () => props.virtualX,
587
- () => dealColumns(),
608
+ () => {
609
+ dealColumns();
610
+ nextTick(initVirtualScrollX);
611
+ },
588
612
  );
589
613
 
590
- dealColumns();
591
-
592
614
  watch(
593
615
  () => props.dataSource,
594
616
  val => {
@@ -618,6 +640,8 @@ watch(
618
640
 
619
641
  watch(() => props.fixedColShadow, dealFixedColShadow);
620
642
 
643
+ dealColumns();
644
+
621
645
  onMounted(() => {
622
646
  initVirtualScroll();
623
647
  updateFixedShadow();
@@ -717,21 +741,13 @@ function rowKeyGen(row: DT) {
717
741
  return key;
718
742
  }
719
743
 
720
- /**
721
- * 列唯一键
722
- * @param col
723
- */
724
- function colKeyGen(col: StkTableColumn<DT>) {
725
- return typeof props.colKey === 'function' ? props.colKey(col) : (col as any)[props.colKey];
726
- }
727
-
728
744
  /** 单元格样式 */
729
745
  const cellStyleMap = computed(() => {
730
746
  const thMap = new Map();
731
747
  const tdMap = new Map();
732
748
  tableHeaders.value.forEach((cols, depth) => {
733
749
  cols.forEach(col => {
734
- const colKey = colKeyGen(col);
750
+ const colKey = colKeyGen.value(col);
735
751
  const width = props.virtualX ? getCalculatedColWidth(col) + 'px' : transformWidthToStr(col.width);
736
752
  const style: CSSProperties = {
737
753
  width,
@@ -116,28 +116,8 @@
116
116
  background-image: var(--bg-border-right), var(--bg-border-bottom);
117
117
  }
118
118
 
119
- thead {
120
- tr {
121
- &:first-child th {
122
- background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
123
- }
124
-
125
- }
126
- }
127
-
128
-
129
- &.virtual-x {
130
- .virtual-x-left {
131
- background: none;
132
- pointer-events: none;
133
- }
134
-
135
- .virtual-x-right {
136
- padding: 0;
137
- background: none;
138
- pointer-events: none;
139
- }
140
-
119
+ thead tr:first-child th {
120
+ background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
141
121
  }
142
122
  }
143
123
 
@@ -191,15 +171,13 @@
191
171
  max-height: var(--header-row-height);
192
172
  }
193
173
 
194
- tbody {
195
- td {
196
- height: var(--row-height);
197
- line-height: 1;
198
-
199
- .table-cell-wrapper {
200
- max-height: var(--row-height);
201
- overflow: hidden;
202
- }
174
+ tbody td {
175
+ height: var(--row-height);
176
+ line-height: 1;
177
+
178
+ .table-cell-wrapper {
179
+ max-height: var(--row-height);
180
+ overflow: hidden;
203
181
  }
204
182
  }
205
183
 
@@ -208,12 +186,6 @@
208
186
  }
209
187
  }
210
188
 
211
- &.virtual-x {
212
- .virtual-x-left {
213
- padding: 0;
214
- }
215
- }
216
-
217
189
  th,
218
190
  td {
219
191
  z-index: 1;
@@ -294,6 +266,13 @@
294
266
  }
295
267
  }
296
268
 
269
+ .virtual-x-left,
270
+ .virtual-x-right {
271
+ padding: 0;
272
+ background: none;
273
+ pointer-events: none;
274
+ }
275
+
297
276
 
298
277
  /** 列宽调整指示器 */
299
278
  .column-resize-indicator {
@@ -1,5 +1,5 @@
1
- import { Ref, ShallowRef, onBeforeUnmount, onMounted, ref } from 'vue';
2
- import { StkTableColumn } from './types';
1
+ import { ComputedRef, Ref, ShallowRef, onBeforeUnmount, onMounted, ref } from 'vue';
2
+ import { StkTableColumn, UniqKey } from './types';
3
3
  import { getCalculatedColWidth } from './utils';
4
4
 
5
5
  type ColResizeState<DT extends Record<string, any>> = {
@@ -21,7 +21,7 @@ type Params<DT extends Record<string, any>> = {
21
21
  tableContainerRef: Ref<HTMLElement | undefined>;
22
22
  tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
23
23
  colResizeIndicatorRef: Ref<HTMLElement | undefined>;
24
- colKeyGen: (p: any) => string;
24
+ colKeyGen: ComputedRef<(p: any) => UniqKey>;
25
25
  };
26
26
 
27
27
  /** 列宽拖动 */
@@ -78,7 +78,7 @@ export function useColResize<DT extends Record<string, any>>({
78
78
  const { scrollLeft, scrollTop } = tableContainerRef.value;
79
79
  const { left } = tableContainerRef.value.getBoundingClientRect();
80
80
  /** 列下标 */
81
- let colIndex = tableHeaderLast.value.findIndex(it => colKeyGen(it) === colKeyGen(col));
81
+ let colIndex = tableHeaderLast.value.findIndex(it => colKeyGen.value(it) === colKeyGen.value(col));
82
82
  if (isPrev) {
83
83
  // 上一列
84
84
  colIndex -= 1;
@@ -139,7 +139,7 @@ export function useColResize<DT extends Record<string, any>>({
139
139
  let width = getCalculatedColWidth(lastCol) + moveX;
140
140
  if (width < props.colMinWidth) width = props.colMinWidth;
141
141
 
142
- const curCol = tableHeaderLast.value.find(it => colKeyGen(it) === colKeyGen(lastCol));
142
+ const curCol = tableHeaderLast.value.find(it => colKeyGen.value(it) === colKeyGen.value(lastCol));
143
143
  if (!curCol) return;
144
144
  curCol.width = width + 'px';
145
145
 
@@ -1,9 +1,9 @@
1
- import { computed, ref, Ref, ShallowRef, shallowRef } from 'vue';
2
- import { StkTableColumn } from './types';
1
+ import { computed, ComputedRef, ref, Ref, ShallowRef, shallowRef } from 'vue';
2
+ import { StkTableColumn, UniqKey } from './types';
3
3
 
4
4
  type Params<T extends Record<string, any>> = {
5
5
  props: any;
6
- colKeyGen: (col: StkTableColumn<T>) => string;
6
+ colKeyGen: ComputedRef<(col: StkTableColumn<T>) => UniqKey>;
7
7
  tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
8
8
  tableHeaderLast: ShallowRef<StkTableColumn<T>[]>;
9
9
  tableContainerRef: Ref<HTMLDivElement | undefined>;
@@ -42,7 +42,7 @@ export function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen,
42
42
  ['fixed-cell--' + col.fixed]: col.fixed,
43
43
  'fixed-cell--shadow': showShadow,
44
44
  };
45
- colMap.set(colKeyGen(col), classObj);
45
+ colMap.set(colKeyGen.value(col), classObj);
46
46
  });
47
47
  });
48
48
  return colMap;
@@ -75,19 +75,39 @@ export function useFixedStyle<DT extends Record<string, any>>({
75
75
  const { fixed } = col;
76
76
  if (tagType === TagType.TD && !fixed) return null;
77
77
 
78
- const isFixedLeft = fixed === 'left';
79
78
  const style: CSSProperties = {};
80
79
  const { colKeyStore, refStore } = fixedColumnsPositionStore.value;
81
80
 
81
+ /** 是否是relative模式完成固定列 */
82
+ let isRelativeMode = true;
83
+ if (props.cellFixedMode === 'sticky') {
84
+ isRelativeMode = false;
85
+ }
86
+
82
87
  if (IS_LEGACY_MODE) {
88
+ // 低版本浏览器只能为固定列设置position: sticky
89
+ isRelativeMode = true;
90
+ }
91
+
92
+ const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
93
+ const scrollRight = scrollWidth - containerWidth - scrollLeft;
94
+
95
+ if (virtualScrollX.value.scrollLeft === 0 && fixed === 'left' && tagType === TagType.TD) {
96
+ // 滚动条在最左侧时,左侧固定列不需要,防止分层
97
+ style.position = void 0;
98
+ } else if (scrollRight === 0 && fixed === 'right' && tagType === TagType.TD) {
99
+ // 滚动条在最右侧时,右侧固定列不需要,防止分层
100
+ style.position = void 0;
101
+ } else if (isRelativeMode) {
83
102
  style.position = 'relative';
84
103
  } else {
85
104
  style.position = 'sticky';
86
105
  }
87
106
 
107
+ const isFixedLeft = fixed === 'left';
88
108
  if (tagType === TagType.TH) {
89
109
  // TH
90
- if (IS_LEGACY_MODE) {
110
+ if (isRelativeMode) {
91
111
  style.top = virtualScroll.value.scrollTop + 'px';
92
112
  } else {
93
113
  style.top = depth * props.rowHeight + 'px';
@@ -95,17 +115,18 @@ export function useFixedStyle<DT extends Record<string, any>>({
95
115
  style.zIndex = isFixedLeft ? '3' : '2'; // 保证固定列高于其他单元格
96
116
  } else {
97
117
  // TD
98
- style.zIndex = isFixedLeft ? '2' : '1';
118
+ if (isFixedLeft) {
119
+ style.zIndex = '2';
120
+ }
99
121
  }
100
122
 
101
123
  if (fixed === 'left' || fixed === 'right') {
102
- if (IS_LEGACY_MODE) {
124
+ if (isRelativeMode) {
103
125
  if (isFixedLeft) {
104
- if (virtualX_on.value) style.left = virtualScrollX.value.scrollLeft - virtualScrollX.value.offsetLeft + 'px';
105
- else style.left = virtualScrollX.value.scrollLeft + 'px';
126
+ style.left = scrollLeft - (virtualX_on.value ? offsetLeft : 0) + 'px';
106
127
  } else {
107
- // TODO:计算右侧距离
108
- style.right = `${virtualX_offsetRight.value}px`;
128
+ // fixed right
129
+ style.right = Math.max(scrollRight - (virtualX_on.value ? virtualX_offsetRight.value : 0), 0) + 'px';
109
130
  }
110
131
  } else {
111
132
  const lr = (col.dataIndex ? colKeyStore[col.dataIndex] : refStore.get(col)) + 'px';
@@ -30,8 +30,10 @@ export type VirtualScrollStore = {
30
30
  };
31
31
  /** 暂存横向虚拟滚动的数据 */
32
32
  export type VirtualScrollXStore = {
33
- /** 容器宽度 */
33
+ /** 父容器宽度 */
34
34
  containerWidth: number;
35
+ /** 滚动容器的宽度 */
36
+ scrollWidth: number;
35
37
  /** 开始位置 */
36
38
  startIndex: number;
37
39
  /** 结束始位置 */
@@ -69,6 +71,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
69
71
 
70
72
  const virtualScrollX = ref<VirtualScrollXStore>({
71
73
  containerWidth: 0,
74
+ scrollWidth: 0,
72
75
  startIndex: 0,
73
76
  endIndex: 0,
74
77
  offsetLeft: 0,
@@ -162,10 +165,9 @@ export function useVirtualScroll<DT extends Record<string, any>>({
162
165
  }
163
166
 
164
167
  function initVirtualScrollX() {
165
- if (!props.virtualX) return;
166
- const { offsetWidth, scrollLeft } = tableContainerRef.value || {};
167
- // scrollTo(null, 0);
168
- virtualScrollX.value.containerWidth = offsetWidth || DEFAULT_TABLE_WIDTH;
168
+ const { clientWidth, scrollLeft, scrollWidth } = tableContainerRef.value || {};
169
+ virtualScrollX.value.containerWidth = clientWidth || DEFAULT_TABLE_WIDTH;
170
+ virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
169
171
  updateVirtualScrollX(scrollLeft);
170
172
  }
171
173
  /**
@@ -237,6 +239,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
237
239
 
238
240
  /** 通过横向滚动条位置,计算横向虚拟滚动的参数 */
239
241
  function updateVirtualScrollX(sLeft = 0) {
242
+ if (!props.virtualX) return;
240
243
  const headerLength = tableHeaderLast.value?.length;
241
244
  const { scrollLeft } = virtualScrollX.value;
242
245
  if (!headerLength) return;