stk-table-vue 0.4.6 → 0.4.9

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
@@ -13,6 +13,7 @@ repo:
13
13
  ## Bug TODO:
14
14
  * [x] props.dataSource 为 shallowRef 时,高亮行不生效。(bug:2024.02.21)(resolved:0.2.3)
15
15
  * [] 固定列列宽拖动目标。
16
+ * [] 惯性滚动优化。
16
17
 
17
18
  ## Feature TODO:
18
19
  * [x] 高亮行,单元格。
@@ -587,6 +588,8 @@ export type SortConfig<T extends Record<string, any>> = {
587
588
  - 以下情况尝试开启此功能。
588
589
  - 在 `customCell` 较多且复杂时。
589
590
  - 大量 highlight 动画时。
591
+ ### props.autoResize
592
+ * 手动设置为 `props.autoResize=false`。可取消监听的性能消耗。适用于宽度高度不变的表格。
590
593
 
591
594
  ## Tips
592
595
  ### props.fixedMode
@@ -455,8 +455,6 @@ function useFixedStyle({
455
455
  if (tagType === TagType.TD && !fixed)
456
456
  return null;
457
457
  const style = {};
458
- const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
459
- const scrollRight = scrollWidth - containerWidth - scrollLeft;
460
458
  const isFixedLeft = fixed === "left";
461
459
  if (tagType === TagType.TH) {
462
460
  if (isRelativeMode.value) {
@@ -465,6 +463,8 @@ function useFixedStyle({
465
463
  style.top = depth * props.rowHeight + "px";
466
464
  }
467
465
  }
466
+ const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
467
+ const scrollRight = scrollWidth - containerWidth - scrollLeft;
468
468
  if (fixed === "left" || fixed === "right") {
469
469
  if (isRelativeMode.value) {
470
470
  if (isFixedLeft) {
@@ -740,7 +740,16 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
740
740
  setHighlightDimCell
741
741
  };
742
742
  }
743
- const SCROLL_CODES = ["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft", "PageUp", "PageDown"];
743
+ var ScrollCodes = /* @__PURE__ */ ((ScrollCodes2) => {
744
+ ScrollCodes2["ArrowUp"] = "ArrowUp";
745
+ ScrollCodes2["ArrowRight"] = "ArrowRight";
746
+ ScrollCodes2["ArrowDown"] = "ArrowDown";
747
+ ScrollCodes2["ArrowLeft"] = "ArrowLeft";
748
+ ScrollCodes2["PageUp"] = "PageUp";
749
+ ScrollCodes2["PageDown"] = "PageDown";
750
+ return ScrollCodes2;
751
+ })(ScrollCodes || {});
752
+ const ScrollCodesValues = Object.values(ScrollCodes);
744
753
  function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on }) {
745
754
  let isMouseOver = false;
746
755
  watch(virtual_on, (val) => {
@@ -767,7 +776,9 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
767
776
  (_c = targetElement.value) == null ? void 0 : _c.removeEventListener("mousedown", handleMouseDown);
768
777
  }
769
778
  function handleKeydown(e) {
770
- if (!SCROLL_CODES.includes(e.code))
779
+ if (!virtual_on.value)
780
+ return;
781
+ if (!ScrollCodesValues.includes(e.code))
771
782
  return;
772
783
  if (!isMouseOver)
773
784
  return;
@@ -777,17 +788,17 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
777
788
  const { headless, headerRowHeight } = props;
778
789
  const headerHeight = headless ? 0 : tableHeaders.value.length * (headerRowHeight || rowHeight);
779
790
  const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
780
- if (e.code === SCROLL_CODES[0]) {
791
+ if (e.code === "ArrowUp") {
781
792
  scrollTo(scrollTop - rowHeight, null);
782
- } else if (e.code === SCROLL_CODES[1]) {
793
+ } else if (e.code === "ArrowRight") {
783
794
  scrollTo(null, scrollLeft + rowHeight);
784
- } else if (e.code === SCROLL_CODES[2]) {
795
+ } else if (e.code === "ArrowDown") {
785
796
  scrollTo(scrollTop + rowHeight, null);
786
- } else if (e.code === SCROLL_CODES[3]) {
797
+ } else if (e.code === "ArrowLeft") {
787
798
  scrollTo(null, scrollLeft - rowHeight);
788
- } else if (e.code === SCROLL_CODES[4]) {
799
+ } else if (e.code === "PageUp") {
789
800
  scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
790
- } else if (e.code === SCROLL_CODES[5]) {
801
+ } else if (e.code === "PageDown") {
791
802
  scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
792
803
  }
793
804
  }
@@ -937,6 +948,10 @@ function useVirtualScroll({
937
948
  const { headerRowHeight } = props;
938
949
  return headerRowHeight * tableHeaders.value.length;
939
950
  }
951
+ function initVirtualScroll(height) {
952
+ initVirtualScrollY(height);
953
+ initVirtualScrollX();
954
+ }
940
955
  function initVirtualScrollY(height) {
941
956
  var _a;
942
957
  if (height !== void 0 && typeof height !== "number") {
@@ -954,7 +969,7 @@ function useVirtualScroll({
954
969
  const headerHeight = getTableHeaderHeight();
955
970
  tableHeaderHeight.value = headerHeight;
956
971
  if (!headless) {
957
- const headerToBodyRowHeightCount = Math.floor(headerHeight) / rowHeight;
972
+ const headerToBodyRowHeightCount = Math.floor(headerHeight / rowHeight);
958
973
  pageSize -= headerToBodyRowHeightCount;
959
974
  }
960
975
  const maxScrollTop = dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight;
@@ -970,31 +985,21 @@ function useVirtualScroll({
970
985
  virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
971
986
  updateVirtualScrollX(scrollLeft);
972
987
  }
973
- function initVirtualScroll(height) {
974
- initVirtualScrollY(height);
975
- initVirtualScrollX();
976
- }
977
988
  let vue2ScrollYTimeout = null;
978
989
  function updateVirtualScrollY(sTop = 0) {
979
- const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
990
+ const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex } = virtualScroll.value;
980
991
  virtualScroll.value.scrollTop = sTop;
981
992
  if (!virtual_on.value) {
982
993
  return;
983
994
  }
984
995
  let startIndex = Math.floor(sTop / rowHeight);
985
- if (startIndex < 0) {
986
- startIndex = 0;
987
- }
996
+ let endIndex = startIndex + pageSize;
988
997
  if (props.stripe && startIndex !== 0) {
989
- const scrollRows = Math.abs(oldStartIndex - startIndex);
990
- if (scrollRows % 2) {
998
+ if (startIndex % 2) {
991
999
  startIndex -= 1;
992
1000
  }
993
1001
  }
994
- let endIndex = startIndex + pageSize;
995
- if (props.stripe) {
996
- endIndex += 1;
997
- }
1002
+ startIndex = Math.max(0, startIndex);
998
1003
  endIndex = Math.min(endIndex, dataSourceCopy.value.length);
999
1004
  if (startIndex >= endIndex) {
1000
1005
  startIndex = endIndex - pageSize;
@@ -1002,13 +1007,12 @@ function useVirtualScroll({
1002
1007
  if (vue2ScrollYTimeout) {
1003
1008
  window.clearTimeout(vue2ScrollYTimeout);
1004
1009
  }
1010
+ if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
1011
+ return;
1012
+ }
1005
1013
  const offsetTop = startIndex * rowHeight;
1006
1014
  if (!props.optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
1007
- Object.assign(virtualScroll.value, {
1008
- startIndex,
1009
- endIndex,
1010
- offsetTop
1011
- });
1015
+ Object.assign(virtualScroll.value, { startIndex, endIndex, offsetTop });
1012
1016
  } else {
1013
1017
  virtualScroll.value.endIndex = endIndex;
1014
1018
  vue2ScrollYTimeout = window.setTimeout(() => {
@@ -1087,17 +1091,16 @@ function useVirtualScroll({
1087
1091
  };
1088
1092
  }
1089
1093
  const _hoisted_1 = ["data-col-key", "draggable", "rowspan", "colspan", "title", "onClick"];
1090
- const _hoisted_2 = { class: "table-header-cell-wrapper" };
1091
- const _hoisted_3 = {
1094
+ const _hoisted_2 = {
1092
1095
  key: 1,
1093
1096
  class: "table-header-title"
1094
1097
  };
1095
- const _hoisted_4 = { class: "table-header-title" };
1096
- const _hoisted_5 = {
1098
+ const _hoisted_3 = { class: "table-header-title" };
1099
+ const _hoisted_4 = {
1097
1100
  key: 3,
1098
1101
  class: "table-header-sorter"
1099
1102
  };
1100
- const _hoisted_6 = /* @__PURE__ */ createElementVNode("svg", {
1103
+ const _hoisted_5 = /* @__PURE__ */ createElementVNode("svg", {
1101
1104
  xmlns: "http://www.w3.org/2000/svg",
1102
1105
  width: "16px",
1103
1106
  height: "16px",
@@ -1114,31 +1117,23 @@ const _hoisted_6 = /* @__PURE__ */ createElementVNode("svg", {
1114
1117
  points: "8 10 4.8 14 11.2 14"
1115
1118
  })
1116
1119
  ], -1);
1117
- const _hoisted_7 = [
1118
- _hoisted_6
1120
+ const _hoisted_6 = [
1121
+ _hoisted_5
1119
1122
  ];
1123
+ const _hoisted_7 = ["onMousedown"];
1120
1124
  const _hoisted_8 = ["onMousedown"];
1121
- const _hoisted_9 = ["onMousedown"];
1125
+ const _hoisted_9 = { class: "stk-tbody-main" };
1122
1126
  const _hoisted_10 = {
1123
- key: 1,
1124
- class: "virtual-top"
1125
- };
1126
- const _hoisted_11 = {
1127
1127
  key: 0,
1128
- class: "virtual-x-left"
1128
+ class: "vt-x-left"
1129
1129
  };
1130
- const _hoisted_12 = { class: "stk-tbody-main" };
1131
- const _hoisted_13 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1132
- const _hoisted_14 = {
1130
+ const _hoisted_11 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1131
+ const _hoisted_12 = {
1133
1132
  key: 0,
1134
- class: "virtual-x-left"
1135
- };
1136
- const _hoisted_15 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1137
- const _hoisted_16 = ["title"];
1138
- const _hoisted_17 = {
1139
- key: 2,
1140
- class: "virtual-bottom"
1133
+ class: "vt-x-left"
1141
1134
  };
1135
+ const _hoisted_13 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1136
+ const _hoisted_14 = ["title"];
1142
1137
  const _sfc_main = /* @__PURE__ */ defineComponent({
1143
1138
  __name: "StkTable",
1144
1139
  props: {
@@ -1283,6 +1278,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1283
1278
  });
1284
1279
  }
1285
1280
  );
1281
+ watch(
1282
+ () => props.virtual,
1283
+ () => {
1284
+ nextTick(() => {
1285
+ initVirtualScrollY();
1286
+ });
1287
+ }
1288
+ );
1286
1289
  watch(
1287
1290
  () => props.virtualX,
1288
1291
  () => {
@@ -1466,9 +1469,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1466
1469
  const sortConfig = props.sortConfig;
1467
1470
  const defaultSort = sortConfig.defaultSort;
1468
1471
  if (!order && defaultSort) {
1469
- order = defaultSort.order;
1472
+ if (!defaultSort.dataIndex) {
1473
+ console.error("sortConfig.defaultSort.dataIndex is required");
1474
+ return;
1475
+ }
1476
+ order = defaultSort.order || "desc";
1470
1477
  sortOrderIndex.value = sortSwitchOrder.indexOf(order);
1471
1478
  sortCol.value = defaultSort.dataIndex;
1479
+ const c = props.columns.find((item) => item.dataIndex === defaultSort.dataIndex);
1480
+ if (c) {
1481
+ col = c;
1482
+ } else {
1483
+ console.error("defaultSort.dataIndex not found in columns");
1484
+ col = props.columns[0];
1485
+ }
1486
+ if (!col)
1487
+ return;
1472
1488
  }
1473
1489
  if (!props.sortRemote || options.force) {
1474
1490
  dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
@@ -1580,15 +1596,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1580
1596
  function setCurrentRow(rowKey, option = { silent: false }) {
1581
1597
  if (!dataSourceCopy.value.length)
1582
1598
  return;
1583
- currentRow.value = dataSourceCopy.value.find((it) => rowKeyGen(it) === rowKey);
1584
- currentRowKey.value = rowKeyGen(currentRow.value);
1599
+ const row = dataSourceCopy.value.find((it) => rowKeyGen(it) === rowKey);
1600
+ if (!row) {
1601
+ console.warn("setCurrentRow failed.rowKey:", rowKey);
1602
+ return;
1603
+ }
1604
+ currentRow.value = row;
1605
+ currentRowKey.value = rowKey;
1585
1606
  if (!option.silent) {
1586
1607
  emits(
1587
1608
  "current-change",
1588
1609
  /** no Event */
1589
1610
  null,
1590
1611
  currentRow.value,
1591
- { select: Boolean(currentRowKey.value) }
1612
+ { select: true }
1592
1613
  );
1593
1614
  }
1594
1615
  }
@@ -1661,6 +1682,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1661
1682
  class: normalizeClass(["stk-table", {
1662
1683
  virtual: _ctx.virtual,
1663
1684
  "virtual-x": _ctx.virtualX,
1685
+ "vt-on": unref(virtual_on),
1664
1686
  dark: _ctx.theme === "dark",
1665
1687
  headless: _ctx.headless,
1666
1688
  "is-col-resizing": unref(isColResizing),
@@ -1710,11 +1732,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1710
1732
  }, [
1711
1733
  unref(virtualX_on) ? (openBlock(), createElementBlock("th", {
1712
1734
  key: 0,
1713
- class: "virtual-x-left",
1714
- style: normalizeStyle({
1715
- minWidth: unref(virtualScrollX).offsetLeft + "px",
1716
- width: unref(virtualScrollX).offsetLeft + "px"
1717
- })
1735
+ class: "vt-x-left",
1736
+ style: normalizeStyle(`min-width:${unref(virtualScrollX).offsetLeft}px;width:${unref(virtualScrollX).offsetLeft}px`)
1718
1737
  }, null, 4)) : createCommentVNode("", true),
1719
1738
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_on) && rowIndex === tableHeaders.value.length - 1 ? unref(virtualX_columnPart) : row, (col, colIndex) => {
1720
1739
  return openBlock(), createElementBlock("th", {
@@ -1742,65 +1761,64 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1742
1761
  onDragover: _cache[2] || (_cache[2] = //@ts-ignore
1743
1762
  (...args) => unref(onThDragOver) && unref(onThDragOver)(...args))
1744
1763
  }, [
1745
- createElementVNode("div", _hoisted_2, [
1764
+ createElementVNode("div", {
1765
+ class: "table-header-cell-wrapper",
1766
+ style: normalizeStyle(`--row-span:${unref(virtualX_on) ? 1 : col.rowSpan}`)
1767
+ }, [
1746
1768
  col.customHeaderCell ? (openBlock(), createBlock(resolveDynamicComponent(col.customHeaderCell), {
1747
1769
  key: 0,
1748
1770
  col,
1749
1771
  colIndex,
1750
1772
  rowIndex
1751
- }, null, 8, ["col", "colIndex", "rowIndex"])) : col.type === "seq" ? (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(col.title), 1)) : renderSlot(_ctx.$slots, "tableHeader", {
1773
+ }, null, 8, ["col", "colIndex", "rowIndex"])) : col.type === "seq" ? (openBlock(), createElementBlock("span", _hoisted_2, toDisplayString(col.title), 1)) : renderSlot(_ctx.$slots, "tableHeader", {
1752
1774
  key: 2,
1753
1775
  col
1754
1776
  }, () => [
1755
- createElementVNode("span", _hoisted_4, toDisplayString(col.title), 1)
1777
+ createElementVNode("span", _hoisted_3, toDisplayString(col.title), 1)
1756
1778
  ]),
1757
- col.sorter ? (openBlock(), createElementBlock("span", _hoisted_5, _hoisted_7)) : createCommentVNode("", true),
1779
+ col.sorter ? (openBlock(), createElementBlock("span", _hoisted_4, _hoisted_6)) : createCommentVNode("", true),
1758
1780
  _ctx.colResizable && colIndex > 0 ? (openBlock(), createElementBlock("div", {
1759
1781
  key: 4,
1760
1782
  class: "table-header-resizer left",
1761
1783
  onMousedown: (e) => unref(onThResizeMouseDown)(e, col, true)
1762
- }, null, 40, _hoisted_8)) : createCommentVNode("", true),
1784
+ }, null, 40, _hoisted_7)) : createCommentVNode("", true),
1763
1785
  _ctx.colResizable ? (openBlock(), createElementBlock("div", {
1764
1786
  key: 5,
1765
1787
  class: "table-header-resizer right",
1766
1788
  onMousedown: (e) => unref(onThResizeMouseDown)(e, col)
1767
- }, null, 40, _hoisted_9)) : createCommentVNode("", true)
1768
- ])
1789
+ }, null, 40, _hoisted_8)) : createCommentVNode("", true)
1790
+ ], 4)
1769
1791
  ], 46, _hoisted_1);
1770
1792
  }), 128)),
1771
1793
  unref(virtualX_on) ? (openBlock(), createElementBlock("th", {
1772
1794
  key: 1,
1773
- class: "virtual-x-right",
1774
- style: normalizeStyle({
1775
- minWidth: unref(virtualX_offsetRight) + "px",
1776
- width: unref(virtualX_offsetRight) + "px"
1777
- })
1795
+ class: "vt-x-right",
1796
+ style: normalizeStyle(`min-width:${unref(virtualX_offsetRight)}px;width:${unref(virtualX_offsetRight)}px`)
1778
1797
  }, null, 4)) : createCommentVNode("", true)
1779
1798
  ], 32);
1780
1799
  }), 128))
1781
1800
  ], 512)) : createCommentVNode("", true),
1782
- unref(virtual_on) ? (openBlock(), createElementBlock("tbody", _hoisted_10, [
1783
- createElementVNode("tr", {
1784
- style: normalizeStyle({ height: `${unref(virtualScroll).offsetTop}px` }),
1801
+ createElementVNode("tbody", _hoisted_9, [
1802
+ unref(virtual_on) ? (openBlock(), createElementBlock("tr", {
1803
+ key: 0,
1804
+ style: normalizeStyle(`height:${unref(virtualScroll).offsetTop}px`),
1785
1805
  class: "padding-top-tr"
1786
1806
  }, [
1787
- unref(virtualX_on) && _ctx.fixedMode && _ctx.headless ? (openBlock(), createElementBlock("td", _hoisted_11)) : createCommentVNode("", true),
1807
+ unref(virtualX_on) && _ctx.fixedMode && _ctx.headless ? (openBlock(), createElementBlock("td", _hoisted_10)) : createCommentVNode("", true),
1788
1808
  _ctx.fixedMode && _ctx.headless ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(virtualX_columnPart), (col) => {
1789
1809
  return openBlock(), createElementBlock("td", {
1790
1810
  key: col.dataIndex,
1791
1811
  style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen.value(col)))
1792
1812
  }, null, 4);
1793
1813
  }), 128)) : createCommentVNode("", true)
1794
- ], 4)
1795
- ])) : createCommentVNode("", true),
1796
- createElementVNode("tbody", _hoisted_12, [
1814
+ ], 4)) : createCommentVNode("", true),
1797
1815
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtual_dataSourcePart), (row, rowIndex) => {
1798
1816
  return openBlock(), createElementBlock("tr", {
1799
1817
  id: unref(stkTableId) + "-" + (_ctx.rowKey ? rowKeyGen(row) : rowIndex),
1800
1818
  key: _ctx.rowKey ? rowKeyGen(row) : rowIndex,
1801
1819
  "data-row-key": _ctx.rowKey ? rowKeyGen(row) : rowIndex,
1802
1820
  class: normalizeClass({
1803
- active: _ctx.rowKey ? rowKeyGen(row) === rowKeyGen(currentRow.value) : row === currentRow.value,
1821
+ active: _ctx.rowKey ? rowKeyGen(row) === currentRowKey.value : row === currentRow.value,
1804
1822
  hover: props.showTrHoverClass && (_ctx.rowKey ? rowKeyGen(row) === currentHoverRowKey.value : row === currentHoverRowKey.value),
1805
1823
  [_ctx.rowClassName(row, rowIndex)]: true
1806
1824
  }),
@@ -1809,7 +1827,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1809
1827
  onContextmenu: (e) => onRowMenu(e, row),
1810
1828
  onMouseover: (e) => onTrMouseOver(e, row)
1811
1829
  }, [
1812
- unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_14)) : createCommentVNode("", true),
1830
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_12)) : createCommentVNode("", true),
1813
1831
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_columnPart), (col, colIndex) => {
1814
1832
  return openBlock(), createElementBlock("td", {
1815
1833
  key: col.dataIndex,
@@ -1838,17 +1856,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1838
1856
  ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
1839
1857
  createTextVNode(toDisplayString((row == null ? void 0 : row[col.dataIndex]) ?? getEmptyCellText.value(col, row)), 1)
1840
1858
  ], 64))
1841
- ], 8, _hoisted_16))
1842
- ], 46, _hoisted_15);
1859
+ ], 8, _hoisted_14))
1860
+ ], 46, _hoisted_13);
1843
1861
  }), 128))
1844
- ], 42, _hoisted_13);
1845
- }), 128))
1846
- ]),
1847
- unref(virtual_on) ? (openBlock(), createElementBlock("tbody", _hoisted_17, [
1848
- createElementVNode("tr", {
1849
- style: normalizeStyle({ height: `${unref(virtual_offsetBottom)}px` })
1850
- }, null, 4)
1851
- ])) : createCommentVNode("", true)
1862
+ ], 42, _hoisted_11);
1863
+ }), 128)),
1864
+ unref(virtual_on) ? (openBlock(), createElementBlock("tr", {
1865
+ key: 1,
1866
+ style: normalizeStyle(`height: ${unref(virtual_offsetBottom)}px`)
1867
+ }, null, 4)) : createCommentVNode("", true)
1868
+ ])
1852
1869
  ], 6),
1853
1870
  (!dataSourceCopy.value || !dataSourceCopy.value.length) && _ctx.showNoData ? (openBlock(), createElementBlock("div", {
1854
1871
  key: 1,
package/lib/style.css CHANGED
@@ -91,13 +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 tbody tr:nth-child(even){
94
+ .stk-table.stripe .stk-tbody-main tr:nth-child(even){
95
95
  background-color:var(--stripe-bgc);
96
96
  }
97
- .stk-table.row-hover tbody tr:hover{
97
+ .stk-table.stripe.vt-on .stk-tbody-main tr:nth-child(odd){
98
+ background-color:var(--stripe-bgc);
99
+ }
100
+ .stk-table.stripe.row-hover .stk-tbody-main tr:hover{
101
+ background-color:var(--tr-hover-bgc);
102
+ }
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{
98
107
  background-color:var(--tr-hover-bgc);
99
108
  }
100
- .stk-table.row-active tbody tr.active{
109
+ .stk-table.row-active .stk-tbody-main tr.active{
101
110
  background-color:var(--tr-active-bgc);
102
111
  }
103
112
  .stk-table.cell-hover tbody td:hover{
@@ -120,7 +129,7 @@
120
129
  }
121
130
  .stk-table.virtual .table-header-cell-wrapper{
122
131
  overflow:hidden;
123
- max-height:var(--header-row-height);
132
+ max-height:calc(var(--header-row-height) * var(--row-span));
124
133
  }
125
134
  .stk-table.virtual tbody td{
126
135
  height:var(--row-height);
@@ -187,8 +196,8 @@
187
196
  background-color:var(--td-bgc);
188
197
  height:var(--row-height);
189
198
  }
190
- .stk-table .virtual-x-left,
191
- .stk-table .virtual-x-right{
199
+ .stk-table .vt-x-left,
200
+ .stk-table .vt-x-right{
192
201
  padding:0;
193
202
  background:none;
194
203
  pointer-events:none;
package/package.json CHANGED
@@ -1,65 +1,65 @@
1
- {
2
- "name": "stk-table-vue",
3
- "version": "0.4.6",
4
- "description": "Simple realtime virtual table for vue3 and vue2.7",
5
- "main": "./lib/stk-table-vue.js",
6
- "types": "./lib/src/StkTable/index.d.ts",
7
- "packageManager": "pnpm@8.14.3",
8
- "directories": {
9
- "test": "test"
10
- },
11
- "type": "module",
12
- "scripts": {
13
- "dev": "vite",
14
- "build": "vite build",
15
- "test": "vitest"
16
- },
17
- "keywords": [
18
- "virtual table",
19
- "vue",
20
- "vue2",
21
- "vue3",
22
- "highlight",
23
- "sticky",
24
- "virtual",
25
- "table",
26
- "list"
27
- ],
28
- "files": [
29
- "lib",
30
- "src"
31
- ],
32
- "author": "japlus",
33
- "repository": {
34
- "type": "git",
35
- "url": "https://github.com/ja-plus/stk-table-vue"
36
- },
37
- "license": "MIT",
38
- "devDependencies": {
39
- "@types/d3-interpolate": "^3.0.4",
40
- "@types/node": "^20.12.10",
41
- "@typescript-eslint/eslint-plugin": "^7.7.0",
42
- "@typescript-eslint/parser": "^7.7.0",
43
- "@vitejs/plugin-vue": "^5.0.4",
44
- "@vue/test-utils": "2.4.4",
45
- "eslint": "^8.57.0",
46
- "eslint-config-prettier": "^9.1.0",
47
- "eslint-plugin-html": "^8.1.0",
48
- "eslint-plugin-prettier": "^5.1.3",
49
- "eslint-plugin-vue": "^9.25.0",
50
- "happy-dom": "^12.10.3",
51
- "less": "^4.2.0",
52
- "postcss-discard-comments": "^6.0.2",
53
- "postcss-preset-env": "^9.5.11",
54
- "prettier": "^3.2.5",
55
- "typescript": "^5.4.5",
56
- "vite": "^5.2.11",
57
- "vite-plugin-dts": "^3.9.1",
58
- "vitest": "^1.6.0",
59
- "vue": "^3.4.26",
60
- "vue-eslint-parser": "^9.4.2"
61
- },
62
- "dependencies": {
63
- "d3-interpolate": "^3.0.1"
64
- }
1
+ {
2
+ "name": "stk-table-vue",
3
+ "version": "0.4.9",
4
+ "description": "Simple realtime virtual table for vue3 and vue2.7",
5
+ "main": "./lib/stk-table-vue.js",
6
+ "types": "./lib/src/StkTable/index.d.ts",
7
+ "packageManager": "pnpm@8.14.3",
8
+ "directories": {
9
+ "test": "test"
10
+ },
11
+ "type": "module",
12
+ "scripts": {
13
+ "dev": "vite",
14
+ "build": "vite build",
15
+ "test": "vitest"
16
+ },
17
+ "keywords": [
18
+ "virtual table",
19
+ "vue",
20
+ "vue2",
21
+ "vue3",
22
+ "highlight",
23
+ "sticky",
24
+ "virtual",
25
+ "table",
26
+ "list"
27
+ ],
28
+ "files": [
29
+ "lib",
30
+ "src"
31
+ ],
32
+ "author": "japlus",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/ja-plus/stk-table-vue"
36
+ },
37
+ "license": "MIT",
38
+ "devDependencies": {
39
+ "@types/d3-interpolate": "^3.0.4",
40
+ "@types/node": "^20.12.10",
41
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
42
+ "@typescript-eslint/parser": "^7.7.0",
43
+ "@vitejs/plugin-vue": "^5.0.4",
44
+ "@vue/test-utils": "2.4.4",
45
+ "eslint": "^8.57.0",
46
+ "eslint-config-prettier": "^9.1.0",
47
+ "eslint-plugin-html": "^8.1.0",
48
+ "eslint-plugin-prettier": "^5.1.3",
49
+ "eslint-plugin-vue": "^9.25.0",
50
+ "happy-dom": "^12.10.3",
51
+ "less": "^4.2.0",
52
+ "postcss-discard-comments": "^6.0.2",
53
+ "postcss-preset-env": "^9.5.11",
54
+ "prettier": "^3.2.5",
55
+ "typescript": "^5.4.5",
56
+ "vite": "^5.2.11",
57
+ "vite-plugin-dts": "^3.9.1",
58
+ "vitest": "^1.6.0",
59
+ "vue": "^3.4.26",
60
+ "vue-eslint-parser": "^9.4.2"
61
+ },
62
+ "dependencies": {
63
+ "d3-interpolate": "^3.0.1"
64
+ }
65
65
  }
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/attribute-hyphenation -->
1
2
  <template>
2
3
  <div
3
4
  ref="tableContainerRef"
@@ -5,6 +6,7 @@
5
6
  :class="{
6
7
  virtual,
7
8
  'virtual-x': virtualX,
9
+ 'vt-on': virtual_on,
8
10
  dark: theme === 'dark',
9
11
  headless,
10
12
  'is-col-resizing': isColResizing,
@@ -52,11 +54,8 @@
52
54
  <!-- 这个th用于横向虚拟滚动表格左边距,width、maxWidth 用于兼容低版本浏览器 -->
53
55
  <th
54
56
  v-if="virtualX_on"
55
- class="virtual-x-left"
56
- :style="{
57
- minWidth: virtualScrollX.offsetLeft + 'px',
58
- width: virtualScrollX.offsetLeft + 'px',
59
- }"
57
+ class="vt-x-left"
58
+ :style="`min-width:${virtualScrollX.offsetLeft}px;width:${virtualScrollX.offsetLeft}px`"
60
59
  ></th>
61
60
  <!-- v for中最后一行才用 切割。TODO:不支持多级表头虚拟横向滚动 -->
62
61
  <th
@@ -84,7 +83,7 @@
84
83
  @drop="onThDrop"
85
84
  @dragover="onThDragOver"
86
85
  >
87
- <div class="table-header-cell-wrapper">
86
+ <div class="table-header-cell-wrapper" :style="`--row-span:${virtualX_on ? 1 : col.rowSpan}`">
88
87
  <component :is="col.customHeaderCell" v-if="col.customHeaderCell" :col="col" :colIndex="colIndex" :rowIndex="rowIndex" />
89
88
  <template v-else-if="col.type === 'seq'">
90
89
  <span class="table-header-title">{{ col.title }}</span>
@@ -116,46 +115,28 @@
116
115
  </div>
117
116
  </th>
118
117
  <!-- 这个th用于横向虚拟滚动表格右边距 width、maxWidth 用于兼容低版本浏览器-->
119
- <th
120
- v-if="virtualX_on"
121
- class="virtual-x-right"
122
- :style="{
123
- minWidth: virtualX_offsetRight + 'px',
124
- width: virtualX_offsetRight + 'px',
125
- }"
126
- ></th>
118
+ <th v-if="virtualX_on" class="vt-x-right" :style="`min-width:${virtualX_offsetRight}px;width:${virtualX_offsetRight}px`"></th>
127
119
  </tr>
128
120
  </thead>
129
121
 
130
122
  <!-- 用于虚拟滚动表格内容定位 @deprecated 有兼容问题-->
131
- <!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }">
132
- <!==这个tr兼容火狐==>
133
- <tr></tr>
134
- </tbody> -->
135
- <!-- <td
136
- v-for="col in virtualX_on ? virtualX_columnPart : tableHeaderLast"
137
- :key="col.dataIndex"
138
- class="perch-td top"
139
- ></td> -->
123
+ <!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }"></tbody> -->
140
124
  <!-- <tbody :style="{ transform: `translateY(${virtualScroll.offsetTop}px)` }"> -->
141
- <tbody v-if="virtual_on" class="virtual-top">
142
- <!-- 由于斑马纹选择器nth-child 原因,占位tr单独包在tbody中 -->
143
- <tr :style="{ height: `${virtualScroll.offsetTop}px` }" class="padding-top-tr">
125
+ <tbody class="stk-tbody-main">
126
+ <tr v-if="virtual_on" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
144
127
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
145
- <td v-if="virtualX_on && fixedMode && headless" class="virtual-x-left"></td>
128
+ <td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
146
129
  <template v-if="fixedMode && headless">
147
130
  <td v-for="col in virtualX_columnPart" :key="col.dataIndex" :style="cellStyleMap[TagType.TD].get(colKeyGen(col))"></td>
148
131
  </template>
149
132
  </tr>
150
- </tbody>
151
- <tbody class="stk-tbody-main">
152
133
  <tr
153
134
  v-for="(row, rowIndex) in virtual_dataSourcePart"
154
135
  :id="stkTableId + '-' + (rowKey ? rowKeyGen(row) : rowIndex)"
155
136
  :key="rowKey ? rowKeyGen(row) : rowIndex"
156
137
  :data-row-key="rowKey ? rowKeyGen(row) : rowIndex"
157
138
  :class="{
158
- active: rowKey ? rowKeyGen(row) === rowKeyGen(currentRow) : row === currentRow,
139
+ active: rowKey ? rowKeyGen(row) === currentRowKey : row === currentRow,
159
140
  hover: props.showTrHoverClass && (rowKey ? rowKeyGen(row) === currentHoverRowKey : row === currentHoverRowKey),
160
141
  [rowClassName(row, rowIndex)]: true,
161
142
  }"
@@ -165,7 +146,7 @@
165
146
  @mouseover="e => onTrMouseOver(e, row)"
166
147
  >
167
148
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
168
- <td v-if="virtualX_on" class="virtual-x-left"></td>
149
+ <td v-if="virtualX_on" class="vt-x-left"></td>
169
150
  <td
170
151
  v-for="(col, colIndex) in virtualX_columnPart"
171
152
  :key="col.dataIndex"
@@ -196,9 +177,7 @@
196
177
  </div>
197
178
  </td>
198
179
  </tr>
199
- </tbody>
200
- <tbody v-if="virtual_on" class="virtual-bottom">
201
- <tr :style="{ height: `${virtual_offsetBottom}px` }"></tr>
180
+ <tr v-if="virtual_on" :style="`height: ${virtual_offsetBottom}px`"></tr>
202
181
  </tbody>
203
182
  </table>
204
183
  <div v-if="(!dataSourceCopy || !dataSourceCopy.length) && showNoData" class="stk-table-no-data" :class="{ 'no-data-full': noDataFull }">
@@ -633,6 +612,14 @@ watch(
633
612
  });
634
613
  },
635
614
  );
615
+ watch(
616
+ () => props.virtual,
617
+ () => {
618
+ nextTick(() => {
619
+ initVirtualScrollY();
620
+ });
621
+ },
622
+ );
636
623
  watch(
637
624
  () => props.virtualX,
638
625
  () => {
@@ -869,10 +856,22 @@ function onColumnSort(col?: StkTableColumn<DT>, click = true, options: { force?:
869
856
  const defaultSort = sortConfig.defaultSort;
870
857
 
871
858
  if (!order && defaultSort) {
859
+ if (!defaultSort.dataIndex) {
860
+ console.error('sortConfig.defaultSort.dataIndex is required');
861
+ return;
862
+ }
872
863
  // 没有排序时变成默认排序
873
- order = defaultSort.order;
864
+ order = defaultSort.order || 'desc';
874
865
  sortOrderIndex.value = sortSwitchOrder.indexOf(order);
875
866
  sortCol.value = defaultSort.dataIndex as string;
867
+ const c = props.columns.find(item => item.dataIndex === defaultSort.dataIndex);
868
+ if (c) {
869
+ col = c;
870
+ } else {
871
+ console.error('defaultSort.dataIndex not found in columns');
872
+ col = props.columns[0];
873
+ }
874
+ if (!col) return;
876
875
  }
877
876
  if (!props.sortRemote || options.force) {
878
877
  dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
@@ -1036,10 +1035,15 @@ function onTrMouseOver(_e: MouseEvent, row: DT) {
1036
1035
  */
1037
1036
  function setCurrentRow(rowKey: string, option = { silent: false }) {
1038
1037
  if (!dataSourceCopy.value.length) return;
1039
- currentRow.value = dataSourceCopy.value.find(it => rowKeyGen(it) === rowKey);
1040
- currentRowKey.value = rowKeyGen(currentRow.value);
1038
+ const row = dataSourceCopy.value.find(it => rowKeyGen(it) === rowKey);
1039
+ if (!row) {
1040
+ console.warn('setCurrentRow failed.rowKey:', rowKey);
1041
+ return;
1042
+ }
1043
+ currentRow.value = row;
1044
+ currentRowKey.value = rowKey;
1041
1045
  if (!option.silent) {
1042
- emits('current-change', /** no Event */ null, currentRow.value, { select: Boolean(currentRowKey.value) });
1046
+ emits('current-change', /** no Event */ null, currentRow.value, { select: true });
1043
1047
  }
1044
1048
  }
1045
1049
 
@@ -135,18 +135,30 @@
135
135
 
136
136
  /* 斑马纹*/
137
137
  &.stripe {
138
+ .stk-tbody-main tr:nth-child(even) {
139
+ background-color: var(--stripe-bgc);
140
+ }
138
141
 
139
- tbody tr:nth-child(even) {
142
+ &.vt-on .stk-tbody-main tr:nth-child(odd) {
140
143
  background-color: var(--stripe-bgc);
141
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
+ }
142
153
  }
143
154
 
144
- &.row-hover tbody tr:hover {
155
+
156
+ &.row-hover .stk-tbody-main tr:hover {
145
157
  background-color: var(--tr-hover-bgc);
146
158
  }
147
159
 
148
160
 
149
- &.row-active tbody tr.active {
161
+ &.row-active .stk-tbody-main tr.active {
150
162
  background-color: var(--tr-active-bgc);
151
163
  }
152
164
 
@@ -183,7 +195,7 @@
183
195
  /* 为不影响布局,表头行高要定死*/
184
196
  .table-header-cell-wrapper {
185
197
  overflow: hidden;
186
- max-height: var(--header-row-height);
198
+ max-height: calc(var(--header-row-height) * var(--row-span));
187
199
  }
188
200
 
189
201
  tbody td {
@@ -279,8 +291,8 @@
279
291
  }
280
292
 
281
293
 
282
- .virtual-x-left,
283
- .virtual-x-right {
294
+ .vt-x-left,
295
+ .vt-x-right {
284
296
  padding: 0;
285
297
  background: none;
286
298
  pointer-events: none;
@@ -37,9 +37,6 @@ export function useFixedStyle<DT extends Record<string, any>>({
37
37
 
38
38
  const style: CSSProperties = {};
39
39
 
40
- const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
41
- const scrollRight = scrollWidth - containerWidth - scrollLeft;
42
-
43
40
  const isFixedLeft = fixed === 'left';
44
41
  if (tagType === TagType.TH) {
45
42
  // TH
@@ -50,6 +47,9 @@ export function useFixedStyle<DT extends Record<string, any>>({
50
47
  }
51
48
  }
52
49
 
50
+ const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
51
+ const scrollRight = scrollWidth - containerWidth - scrollLeft;
52
+
53
53
  if (fixed === 'left' || fixed === 'right') {
54
54
  if (isRelativeMode.value) {
55
55
  if (isFixedLeft) {
@@ -3,7 +3,16 @@ import { StkTableColumn } from './types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
4
4
 
5
5
  /** 翻页按键 */
6
- const SCROLL_CODES = ['ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft', 'PageUp', 'PageDown'] as const;
6
+ enum ScrollCodes {
7
+ ArrowUp = 'ArrowUp',
8
+ ArrowRight = 'ArrowRight',
9
+ ArrowDown = 'ArrowDown',
10
+ ArrowLeft = 'ArrowLeft',
11
+ PageUp = 'PageUp',
12
+ PageDown = 'PageDown',
13
+ }
14
+ /** 所有翻页按键数组 */
15
+ const ScrollCodesValues = Object.values(ScrollCodes);
7
16
 
8
17
  type Options<DT extends Record<string, any>> = {
9
18
  props: any;
@@ -52,7 +61,8 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
52
61
 
53
62
  /** 键盘按下事件 */
54
63
  function handleKeydown(e: KeyboardEvent) {
55
- if (!SCROLL_CODES.includes(e.code as any)) return;
64
+ if (!virtual_on.value) return; // 非虚拟滚动使用浏览器默认滚动
65
+ if (!ScrollCodesValues.includes(e.code as any)) return;
56
66
  if (!isMouseOver) return; // 不悬浮还是要触发键盘事件的
57
67
  e.preventDefault(); // 不触发键盘默认的箭头事件
58
68
 
@@ -65,17 +75,17 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
65
75
  /** 表体的page */
66
76
  const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
67
77
 
68
- if (e.code === SCROLL_CODES[0]) {
78
+ if (e.code === ScrollCodes.ArrowUp) {
69
79
  scrollTo(scrollTop - rowHeight, null);
70
- } else if (e.code === SCROLL_CODES[1]) {
80
+ } else if (e.code === ScrollCodes.ArrowRight) {
71
81
  scrollTo(null, scrollLeft + rowHeight);
72
- } else if (e.code === SCROLL_CODES[2]) {
82
+ } else if (e.code === ScrollCodes.ArrowDown) {
73
83
  scrollTo(scrollTop + rowHeight, null);
74
- } else if (e.code === SCROLL_CODES[3]) {
84
+ } else if (e.code === ScrollCodes.ArrowLeft) {
75
85
  scrollTo(null, scrollLeft - rowHeight);
76
- } else if (e.code === SCROLL_CODES[4]) {
86
+ } else if (e.code === ScrollCodes.PageUp) {
77
87
  scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
78
- } else if (e.code === SCROLL_CODES[5]) {
88
+ } else if (e.code === ScrollCodes.PageDown) {
79
89
  scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
80
90
  }
81
91
  }
@@ -163,6 +163,15 @@ export function useVirtualScroll<DT extends Record<string, any>>({
163
163
  // return theadRef.value?.offsetHeight || 0;
164
164
  }
165
165
 
166
+ /**
167
+ * 初始化虚拟滚动参数
168
+ * @param {number} [height] 虚拟滚动的高度
169
+ */
170
+ function initVirtualScroll(height?: number) {
171
+ initVirtualScrollY(height);
172
+ initVirtualScrollX();
173
+ }
174
+
166
175
  /**
167
176
  * 初始化Y虚拟滚动参数
168
177
  * @param {number} [height] 虚拟滚动的高度
@@ -184,7 +193,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
184
193
  tableHeaderHeight.value = headerHeight;
185
194
  if (!headless) {
186
195
  /** 表头高度占几行表体高度数 */
187
- const headerToBodyRowHeightCount = Math.floor(headerHeight) / rowHeight;
196
+ const headerToBodyRowHeightCount = Math.floor(headerHeight / rowHeight);
188
197
  pageSize -= headerToBodyRowHeightCount; //减去表头行数
189
198
  }
190
199
  /** 最大的scrollTop */
@@ -203,20 +212,12 @@ export function useVirtualScroll<DT extends Record<string, any>>({
203
212
  virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
204
213
  updateVirtualScrollX(scrollLeft);
205
214
  }
206
- /**
207
- * 初始化虚拟滚动参数
208
- * @param {number} [height] 虚拟滚动的高度
209
- */
210
- function initVirtualScroll(height?: number) {
211
- initVirtualScrollY(height);
212
- initVirtualScrollX();
213
- }
214
215
 
215
216
  let vue2ScrollYTimeout: null | number = null;
216
217
 
217
218
  /** 通过滚动条位置,计算虚拟滚动的参数 */
218
219
  function updateVirtualScrollY(sTop = 0) {
219
- const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
220
+ const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex } = virtualScroll.value;
220
221
  // 先更新滚动条位置记录,其他地方有依赖。(stripe 时ArrowUp/Down滚动依赖)
221
222
  virtualScroll.value.scrollTop = sTop;
222
223
 
@@ -226,22 +227,18 @@ export function useVirtualScroll<DT extends Record<string, any>>({
226
227
  }
227
228
 
228
229
  let startIndex = Math.floor(sTop / rowHeight);
229
- if (startIndex < 0) {
230
- startIndex = 0;
231
- }
230
+ let endIndex = startIndex + pageSize;
231
+
232
232
  if (props.stripe && startIndex !== 0) {
233
- const scrollRows = Math.abs(oldStartIndex - startIndex);
234
233
  // 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
235
- if (scrollRows % 2) {
234
+ if (startIndex % 2) {
236
235
  startIndex -= 1; // 奇数-1变成偶数
237
236
  }
238
237
  }
239
- let endIndex = startIndex + pageSize;
240
- if (props.stripe) {
241
- endIndex += 1; // 斑马纹下多渲染一些
242
- }
243
238
 
244
- // 溢出endIndex修正
239
+ startIndex = Math.max(0, startIndex);
240
+
241
+ // 溢出修正
245
242
  endIndex = Math.min(endIndex, dataSourceCopy.value.length);
246
243
 
247
244
  if (startIndex >= endIndex) {
@@ -253,6 +250,11 @@ export function useVirtualScroll<DT extends Record<string, any>>({
253
250
  window.clearTimeout(vue2ScrollYTimeout);
254
251
  }
255
252
 
253
+ if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
254
+ // 没有变化,不需要更新
255
+ return;
256
+ }
257
+
256
258
  const offsetTop = startIndex * rowHeight; // startIndex之前的高度
257
259
 
258
260
  /**
@@ -260,11 +262,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
260
262
  */
261
263
  if (!props.optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
262
264
  // 向上滚动
263
- Object.assign(virtualScroll.value, {
264
- startIndex,
265
- endIndex,
266
- offsetTop,
267
- });
265
+ Object.assign(virtualScroll.value, { startIndex, endIndex, offsetTop });
268
266
  } else {
269
267
  // vue2向下滚动优化
270
268
  virtualScroll.value.endIndex = endIndex;