@veltra/desktop 1.6.0 → 1.6.1

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.
@@ -5,13 +5,16 @@
5
5
  * - `explicitWidth` 列:保持配置/拖拽锁定的宽度
6
6
  * - `fixed` 且未显式设宽的列:保持 `minWidth`,不参与剩余宽度均分
7
7
  * - 其余未显式设宽的列:均分容器剩余宽度
8
+ *
9
+ * 分配结果写入列节点自身的 `allocatedWidth`,不回写列配置的 `data.width`,
10
+ * 避免污染用户数据后在列森林重建时被误判为显式宽度。
8
11
  */
9
12
  function allocateLeafColumnWidths(leafColumns, containerWidth) {
10
13
  if (!containerWidth || !leafColumns.length) return;
11
14
  const growColumns = leafColumns.filter((column) => !column.explicitWidth && !column.fixed);
12
15
  const fixedFlexColumns = leafColumns.filter((column) => !column.explicitWidth && column.fixed);
13
16
  fixedFlexColumns.forEach((column) => {
14
- column.data.width = void 0;
17
+ column.allocatedWidth = void 0;
15
18
  });
16
19
  const explicitWidthTotal = leafColumns.filter((column) => column.explicitWidth).reduce((acc, cur) => acc + (cur.width ?? cur.minWidth), 0);
17
20
  const growBaseTotal = growColumns.reduce((acc, cur) => acc + cur.minWidth, 0);
@@ -20,10 +23,10 @@ function allocateLeafColumnWidths(leafColumns, containerWidth) {
20
23
  if (growColumns.length > 0 && totalBase < containerWidth) {
21
24
  const allocatedWidth = (containerWidth - totalBase) / growColumns.length;
22
25
  growColumns.forEach((column) => {
23
- column.width = column.minWidth + allocatedWidth;
26
+ column.allocatedWidth = column.minWidth + allocatedWidth;
24
27
  });
25
28
  } else growColumns.forEach((column) => {
26
- column.data.width = void 0;
29
+ column.allocatedWidth = void 0;
27
30
  });
28
31
  }
29
32
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"allocate-column-widths.js","names":[],"sources":["../../../src/components/table/allocate-column-widths.ts"],"sourcesContent":["import type { ColumnNode } from './node/col'\n\n/**\n * 按容器宽度分配叶子列宽。\n *\n * - `explicitWidth` 列:保持配置/拖拽锁定的宽度\n * - `fixed` 且未显式设宽的列:保持 `minWidth`,不参与剩余宽度均分\n * - 其余未显式设宽的列:均分容器剩余宽度\n */\nexport function allocateLeafColumnWidths(leafColumns: ColumnNode[], containerWidth: number): void {\n if (!containerWidth || !leafColumns.length) return\n\n const growColumns = leafColumns.filter((column) => !column.explicitWidth && !column.fixed)\n // fixed 且未显式设宽:清掉可能被旧算法撑大的残留 width,回落到 minWidth\n const fixedFlexColumns = leafColumns.filter((column) => !column.explicitWidth && column.fixed)\n\n fixedFlexColumns.forEach((column) => {\n column.data.width = undefined\n })\n\n const explicitWidthTotal = leafColumns\n .filter((column) => column.explicitWidth)\n .reduce((acc, cur) => acc + (cur.width ?? cur.minWidth!), 0)\n const growBaseTotal = growColumns.reduce((acc, cur) => acc + cur.minWidth!, 0)\n const fixedFlexBaseTotal = fixedFlexColumns.reduce((acc, cur) => acc + cur.minWidth!, 0)\n const totalBase = explicitWidthTotal + growBaseTotal + fixedFlexBaseTotal\n\n if (growColumns.length > 0 && totalBase < containerWidth) {\n const allocatedWidth = (containerWidth - totalBase) / growColumns.length\n growColumns.forEach((column) => {\n column.width = column.minWidth! + allocatedWidth\n })\n } else {\n growColumns.forEach((column) => {\n column.data.width = undefined\n })\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,yBAAyB,aAA2B,gBAA8B;CAChG,IAAI,CAAC,kBAAkB,CAAC,YAAY,QAAQ;CAE5C,MAAM,cAAc,YAAY,QAAQ,WAAW,CAAC,OAAO,iBAAiB,CAAC,OAAO,KAAK;CAEzF,MAAM,mBAAmB,YAAY,QAAQ,WAAW,CAAC,OAAO,iBAAiB,OAAO,KAAK;CAE7F,iBAAiB,SAAS,WAAW;EACnC,OAAO,KAAK,QAAQ,KAAA;CACtB,CAAC;CAED,MAAM,qBAAqB,YACxB,QAAQ,WAAW,OAAO,aAAa,CAAC,CACxC,QAAQ,KAAK,QAAQ,OAAO,IAAI,SAAS,IAAI,WAAY,CAAC;CAC7D,MAAM,gBAAgB,YAAY,QAAQ,KAAK,QAAQ,MAAM,IAAI,UAAW,CAAC;CAC7E,MAAM,qBAAqB,iBAAiB,QAAQ,KAAK,QAAQ,MAAM,IAAI,UAAW,CAAC;CACvF,MAAM,YAAY,qBAAqB,gBAAgB;CAEvD,IAAI,YAAY,SAAS,KAAK,YAAY,gBAAgB;EACxD,MAAM,kBAAkB,iBAAiB,aAAa,YAAY;EAClE,YAAY,SAAS,WAAW;GAC9B,OAAO,QAAQ,OAAO,WAAY;EACpC,CAAC;CACH,OACE,YAAY,SAAS,WAAW;EAC9B,OAAO,KAAK,QAAQ,KAAA;CACtB,CAAC;AAEL"}
1
+ {"version":3,"file":"allocate-column-widths.js","names":[],"sources":["../../../src/components/table/allocate-column-widths.ts"],"sourcesContent":["import type { ColumnNode } from './node/col'\n\n/**\n * 按容器宽度分配叶子列宽。\n *\n * - `explicitWidth` 列:保持配置/拖拽锁定的宽度\n * - `fixed` 且未显式设宽的列:保持 `minWidth`,不参与剩余宽度均分\n * - 其余未显式设宽的列:均分容器剩余宽度\n *\n * 分配结果写入列节点自身的 `allocatedWidth`,不回写列配置的 `data.width`,\n * 避免污染用户数据后在列森林重建时被误判为显式宽度。\n */\nexport function allocateLeafColumnWidths(leafColumns: ColumnNode[], containerWidth: number): void {\n if (!containerWidth || !leafColumns.length) return\n\n const growColumns = leafColumns.filter((column) => !column.explicitWidth && !column.fixed)\n // fixed 且未显式设宽:清掉之前的分配结果,回落到 minWidth\n const fixedFlexColumns = leafColumns.filter((column) => !column.explicitWidth && column.fixed)\n\n fixedFlexColumns.forEach((column) => {\n column.allocatedWidth = undefined\n })\n\n const explicitWidthTotal = leafColumns\n .filter((column) => column.explicitWidth)\n .reduce((acc, cur) => acc + (cur.width ?? cur.minWidth!), 0)\n const growBaseTotal = growColumns.reduce((acc, cur) => acc + cur.minWidth!, 0)\n const fixedFlexBaseTotal = fixedFlexColumns.reduce((acc, cur) => acc + cur.minWidth!, 0)\n const totalBase = explicitWidthTotal + growBaseTotal + fixedFlexBaseTotal\n\n if (growColumns.length > 0 && totalBase < containerWidth) {\n const allocatedWidth = (containerWidth - totalBase) / growColumns.length\n growColumns.forEach((column) => {\n column.allocatedWidth = column.minWidth! + allocatedWidth\n })\n } else {\n growColumns.forEach((column) => {\n column.allocatedWidth = undefined\n })\n }\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,yBAAyB,aAA2B,gBAA8B;CAChG,IAAI,CAAC,kBAAkB,CAAC,YAAY,QAAQ;CAE5C,MAAM,cAAc,YAAY,QAAQ,WAAW,CAAC,OAAO,iBAAiB,CAAC,OAAO,KAAK;CAEzF,MAAM,mBAAmB,YAAY,QAAQ,WAAW,CAAC,OAAO,iBAAiB,OAAO,KAAK;CAE7F,iBAAiB,SAAS,WAAW;EACnC,OAAO,iBAAiB,KAAA;CAC1B,CAAC;CAED,MAAM,qBAAqB,YACxB,QAAQ,WAAW,OAAO,aAAa,CAAC,CACxC,QAAQ,KAAK,QAAQ,OAAO,IAAI,SAAS,IAAI,WAAY,CAAC;CAC7D,MAAM,gBAAgB,YAAY,QAAQ,KAAK,QAAQ,MAAM,IAAI,UAAW,CAAC;CAC7E,MAAM,qBAAqB,iBAAiB,QAAQ,KAAK,QAAQ,MAAM,IAAI,UAAW,CAAC;CACvF,MAAM,YAAY,qBAAqB,gBAAgB;CAEvD,IAAI,YAAY,SAAS,KAAK,YAAY,gBAAgB;EACxD,MAAM,kBAAkB,iBAAiB,aAAa,YAAY;EAClE,YAAY,SAAS,WAAW;GAC9B,OAAO,iBAAiB,OAAO,WAAY;EAC7C,CAAC;CACH,OACE,YAAY,SAAS,WAAW;EAC9B,OAAO,iBAAiB,KAAA;CAC1B,CAAC;AAEL"}
@@ -37,13 +37,26 @@ var ColumnNode = class extends TreeNode {
37
37
  }
38
38
  /** 宽度 */
39
39
  get width() {
40
- const { width } = this.data;
40
+ const width = this.data.width ?? this.derived.allocatedWidth;
41
41
  if (!width || this.minWidth && width && width < this.minWidth) return this.minWidth;
42
42
  return width;
43
43
  }
44
44
  set width(val) {
45
45
  if (this.minWidth && val && val < this.minWidth) val = this.minWidth;
46
46
  this.data.width = val;
47
+ this.derived.allocatedWidth = void 0;
48
+ }
49
+ /**
50
+ * 列宽分配算法写入的宽度。
51
+ * 与用户列配置 `data.width` 隔离:分配结果不回写列配置对象,
52
+ * 避免列森林重建(重挂载 / v-if / columns 引用变化)时把上一次的
53
+ * 分配残留误判为显式 width,导致列宽被永久锁定、容器变窄后出现滚动条。
54
+ */
55
+ get allocatedWidth() {
56
+ return this.derived.allocatedWidth;
57
+ }
58
+ set allocatedWidth(val) {
59
+ this.derived.allocatedWidth = val;
47
60
  }
48
61
  /** 最小宽度 */
49
62
  get minWidth() {
@@ -80,6 +93,8 @@ var ColumnNode = class extends TreeNode {
80
93
  */
81
94
  explicitWidth;
82
95
  style = reactive({});
96
+ /** 派生状态(响应式),与用户列配置 data 隔离 */
97
+ derived = shallowReactive({ allocatedWidth: void 0 });
83
98
  constructor(val, index, depth, parent) {
84
99
  super(isReactive(val) ? val : shallowReactive(val), index, depth, parent);
85
100
  this.explicitWidth = val.width !== void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"col.js","names":[],"sources":["../../../../src/components/table/node/col.ts"],"sourcesContent":["import { TreeNode } from '@cat-kit/core'\nimport { isReactive, reactive, shallowReactive } from 'vue'\n\nimport type { TableColumn, TableColumnAlign } from '../../../types'\n\nexport class ColumnNode extends TreeNode<TableColumn, ColumnNode> {\n declare children?: ColumnNode[] | undefined\n declare parent?: ColumnNode\n /** 叶子节点数量 */\n leafs?: number\n\n nextNode: ColumnNode | null = null\n\n /** 列key */\n get key(): string {\n return this.data.key\n }\n set key(val) {\n this.data.key = val\n }\n /** 列名 */\n get name(): string {\n return this.data.name\n }\n set name(val) {\n this.data.name = val\n }\n\n /**\n * 列对齐方式\n * @default 'left'\n */\n get align(): TableColumnAlign {\n return this.data.align ?? 'left'\n }\n set align(val) {\n this.data.align = val\n }\n\n get headerAlign(): TableColumnAlign {\n return this.data.headerAlign ?? this.align\n }\n set headerAlign(val) {\n this.data.headerAlign = val\n }\n\n /** 宽度 */\n get width(): number | undefined {\n const { width } = this.data\n if (!width || (this.minWidth && width && width < this.minWidth)) {\n return this.minWidth\n }\n return width\n }\n set width(val) {\n if (this.minWidth && val && val < this.minWidth) {\n val = this.minWidth\n }\n this.data.width = val\n }\n /** 最小宽度 */\n get minWidth(): number | undefined {\n const minWidth = this.data.minWidth\n if (!minWidth && this.isLeaf) {\n return 100\n }\n return minWidth\n }\n set minWidth(val) {\n this.data.minWidth = val\n }\n\n /** 列固定方向 */\n get fixed(): 'left' | 'right' | undefined {\n if (this.depth > 0) return\n return this.data.fixed\n }\n set fixed(val) {\n this.data.fixed = val\n }\n\n /** 是否是左侧的最后一个固定列 */\n get isLastFixed(): boolean {\n return this.data.isLastFixed ?? false\n }\n\n /** 是否是右侧的第一个固定列 */\n get isFirstFixed(): boolean {\n return this.data.isFirstFixed ?? false\n }\n\n /** 是否是可调整宽度的列 */\n get resizable(): boolean {\n return this.data.resizable ?? true\n }\n\n /**\n * 是否在列配置上显式指定了 width,或经表头拖拽锁定。\n * 此类列不参与剩余宽度的均分。\n */\n explicitWidth: boolean\n\n style: Record<string, number> = reactive({})\n\n constructor(val: TableColumn, index: number, depth: number, parent?: ColumnNode) {\n super(isReactive(val) ? val : shallowReactive(val), index, depth, parent)\n this.explicitWidth = val.width !== undefined\n }\n}\n"],"mappings":";;;AAKA,IAAa,aAAb,cAAgC,SAAkC;;CAIhE;CAEA,WAA8B;;CAG9B,IAAI,MAAc;EAChB,OAAO,KAAK,KAAK;CACnB;CACA,IAAI,IAAI,KAAK;EACX,KAAK,KAAK,MAAM;CAClB;;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,KAAK;CACnB;CACA,IAAI,KAAK,KAAK;EACZ,KAAK,KAAK,OAAO;CACnB;;;;;CAMA,IAAI,QAA0B;EAC5B,OAAO,KAAK,KAAK,SAAS;CAC5B;CACA,IAAI,MAAM,KAAK;EACb,KAAK,KAAK,QAAQ;CACpB;CAEA,IAAI,cAAgC;EAClC,OAAO,KAAK,KAAK,eAAe,KAAK;CACvC;CACA,IAAI,YAAY,KAAK;EACnB,KAAK,KAAK,cAAc;CAC1B;;CAGA,IAAI,QAA4B;EAC9B,MAAM,EAAE,UAAU,KAAK;EACvB,IAAI,CAAC,SAAU,KAAK,YAAY,SAAS,QAAQ,KAAK,UACpD,OAAO,KAAK;EAEd,OAAO;CACT;CACA,IAAI,MAAM,KAAK;EACb,IAAI,KAAK,YAAY,OAAO,MAAM,KAAK,UACrC,MAAM,KAAK;EAEb,KAAK,KAAK,QAAQ;CACpB;;CAEA,IAAI,WAA+B;EACjC,MAAM,WAAW,KAAK,KAAK;EAC3B,IAAI,CAAC,YAAY,KAAK,QACpB,OAAO;EAET,OAAO;CACT;CACA,IAAI,SAAS,KAAK;EAChB,KAAK,KAAK,WAAW;CACvB;;CAGA,IAAI,QAAsC;EACxC,IAAI,KAAK,QAAQ,GAAG;EACpB,OAAO,KAAK,KAAK;CACnB;CACA,IAAI,MAAM,KAAK;EACb,KAAK,KAAK,QAAQ;CACpB;;CAGA,IAAI,cAAuB;EACzB,OAAO,KAAK,KAAK,eAAe;CAClC;;CAGA,IAAI,eAAwB;EAC1B,OAAO,KAAK,KAAK,gBAAgB;CACnC;;CAGA,IAAI,YAAqB;EACvB,OAAO,KAAK,KAAK,aAAa;CAChC;;;;;CAMA;CAEA,QAAgC,SAAS,CAAC,CAAC;CAE3C,YAAY,KAAkB,OAAe,OAAe,QAAqB;EAC/E,MAAM,WAAW,GAAG,IAAI,MAAM,gBAAgB,GAAG,GAAG,OAAO,OAAO,MAAM;EACxE,KAAK,gBAAgB,IAAI,UAAU,KAAA;CACrC;AACF"}
1
+ {"version":3,"file":"col.js","names":[],"sources":["../../../../src/components/table/node/col.ts"],"sourcesContent":["import { TreeNode } from '@cat-kit/core'\nimport { isReactive, reactive, shallowReactive } from 'vue'\n\nimport type { TableColumn, TableColumnAlign } from '../../../types'\n\nexport class ColumnNode extends TreeNode<TableColumn, ColumnNode> {\n declare children?: ColumnNode[] | undefined\n declare parent?: ColumnNode\n /** 叶子节点数量 */\n leafs?: number\n\n nextNode: ColumnNode | null = null\n\n /** 列key */\n get key(): string {\n return this.data.key\n }\n set key(val) {\n this.data.key = val\n }\n /** 列名 */\n get name(): string {\n return this.data.name\n }\n set name(val) {\n this.data.name = val\n }\n\n /**\n * 列对齐方式\n * @default 'left'\n */\n get align(): TableColumnAlign {\n return this.data.align ?? 'left'\n }\n set align(val) {\n this.data.align = val\n }\n\n get headerAlign(): TableColumnAlign {\n return this.data.headerAlign ?? this.align\n }\n set headerAlign(val) {\n this.data.headerAlign = val\n }\n\n /** 宽度 */\n get width(): number | undefined {\n const width = this.data.width ?? this.derived.allocatedWidth\n if (!width || (this.minWidth && width && width < this.minWidth)) {\n return this.minWidth\n }\n return width\n }\n set width(val) {\n if (this.minWidth && val && val < this.minWidth) {\n val = this.minWidth\n }\n this.data.width = val\n // 显式宽度(配置/拖拽)优先于分配宽度,旧的分配结果直接作废\n this.derived.allocatedWidth = undefined\n }\n\n /**\n * 列宽分配算法写入的宽度。\n * 与用户列配置 `data.width` 隔离:分配结果不回写列配置对象,\n * 避免列森林重建(重挂载 / v-if / columns 引用变化)时把上一次的\n * 分配残留误判为显式 width,导致列宽被永久锁定、容器变窄后出现滚动条。\n */\n get allocatedWidth(): number | undefined {\n return this.derived.allocatedWidth\n }\n set allocatedWidth(val: number | undefined) {\n this.derived.allocatedWidth = val\n }\n /** 最小宽度 */\n get minWidth(): number | undefined {\n const minWidth = this.data.minWidth\n if (!minWidth && this.isLeaf) {\n return 100\n }\n return minWidth\n }\n set minWidth(val) {\n this.data.minWidth = val\n }\n\n /** 列固定方向 */\n get fixed(): 'left' | 'right' | undefined {\n if (this.depth > 0) return\n return this.data.fixed\n }\n set fixed(val) {\n this.data.fixed = val\n }\n\n /** 是否是左侧的最后一个固定列 */\n get isLastFixed(): boolean {\n return this.data.isLastFixed ?? false\n }\n\n /** 是否是右侧的第一个固定列 */\n get isFirstFixed(): boolean {\n return this.data.isFirstFixed ?? false\n }\n\n /** 是否是可调整宽度的列 */\n get resizable(): boolean {\n return this.data.resizable ?? true\n }\n\n /**\n * 是否在列配置上显式指定了 width,或经表头拖拽锁定。\n * 此类列不参与剩余宽度的均分。\n */\n explicitWidth: boolean\n\n style: Record<string, number> = reactive({})\n\n /** 派生状态(响应式),与用户列配置 data 隔离 */\n private readonly derived = shallowReactive<{ allocatedWidth?: number }>({\n allocatedWidth: undefined\n })\n\n constructor(val: TableColumn, index: number, depth: number, parent?: ColumnNode) {\n super(isReactive(val) ? val : shallowReactive(val), index, depth, parent)\n this.explicitWidth = val.width !== undefined\n }\n}\n"],"mappings":";;;AAKA,IAAa,aAAb,cAAgC,SAAkC;;CAIhE;CAEA,WAA8B;;CAG9B,IAAI,MAAc;EAChB,OAAO,KAAK,KAAK;CACnB;CACA,IAAI,IAAI,KAAK;EACX,KAAK,KAAK,MAAM;CAClB;;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,KAAK;CACnB;CACA,IAAI,KAAK,KAAK;EACZ,KAAK,KAAK,OAAO;CACnB;;;;;CAMA,IAAI,QAA0B;EAC5B,OAAO,KAAK,KAAK,SAAS;CAC5B;CACA,IAAI,MAAM,KAAK;EACb,KAAK,KAAK,QAAQ;CACpB;CAEA,IAAI,cAAgC;EAClC,OAAO,KAAK,KAAK,eAAe,KAAK;CACvC;CACA,IAAI,YAAY,KAAK;EACnB,KAAK,KAAK,cAAc;CAC1B;;CAGA,IAAI,QAA4B;EAC9B,MAAM,QAAQ,KAAK,KAAK,SAAS,KAAK,QAAQ;EAC9C,IAAI,CAAC,SAAU,KAAK,YAAY,SAAS,QAAQ,KAAK,UACpD,OAAO,KAAK;EAEd,OAAO;CACT;CACA,IAAI,MAAM,KAAK;EACb,IAAI,KAAK,YAAY,OAAO,MAAM,KAAK,UACrC,MAAM,KAAK;EAEb,KAAK,KAAK,QAAQ;EAElB,KAAK,QAAQ,iBAAiB,KAAA;CAChC;;;;;;;CAQA,IAAI,iBAAqC;EACvC,OAAO,KAAK,QAAQ;CACtB;CACA,IAAI,eAAe,KAAyB;EAC1C,KAAK,QAAQ,iBAAiB;CAChC;;CAEA,IAAI,WAA+B;EACjC,MAAM,WAAW,KAAK,KAAK;EAC3B,IAAI,CAAC,YAAY,KAAK,QACpB,OAAO;EAET,OAAO;CACT;CACA,IAAI,SAAS,KAAK;EAChB,KAAK,KAAK,WAAW;CACvB;;CAGA,IAAI,QAAsC;EACxC,IAAI,KAAK,QAAQ,GAAG;EACpB,OAAO,KAAK,KAAK;CACnB;CACA,IAAI,MAAM,KAAK;EACb,KAAK,KAAK,QAAQ;CACpB;;CAGA,IAAI,cAAuB;EACzB,OAAO,KAAK,KAAK,eAAe;CAClC;;CAGA,IAAI,eAAwB;EAC1B,OAAO,KAAK,KAAK,gBAAgB;CACnC;;CAGA,IAAI,YAAqB;EACvB,OAAO,KAAK,KAAK,aAAa;CAChC;;;;;CAMA;CAEA,QAAgC,SAAS,CAAC,CAAC;;CAG3C,UAA2B,gBAA6C,EACtE,gBAAgB,KAAA,EAClB,CAAC;CAED,YAAY,KAAkB,OAAe,OAAe,QAAqB;EAC/E,MAAM,WAAW,GAAG,IAAI,MAAM,gBAAgB,GAAG,GAAG,OAAO,OAAO,MAAM;EACxE,KAAK,gBAAgB,IAAI,UAAU,KAAA;CACrC;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -71,12 +71,12 @@
71
71
  "peerDependencies": {
72
72
  "@cat-kit/core": ">=1.1.8",
73
73
  "@cat-kit/fe": ">=1.1.8",
74
- "@veltra/compositions": "^1.6.0",
75
- "@veltra/directives": "^1.6.0",
74
+ "@veltra/compositions": "^1.6.1",
75
+ "@veltra/directives": "^1.6.1",
76
76
  "@veltra/icons": "^1.5.0",
77
77
  "@veltra/sheet-core": "^2.3.0",
78
- "@veltra/styles": "^1.6.0",
79
- "@veltra/utils": "^1.6.0",
78
+ "@veltra/styles": "^1.6.1",
79
+ "@veltra/utils": "^1.6.1",
80
80
  "vue": ">=3.5.41"
81
81
  },
82
82
  "peerDependenciesMeta": {
@@ -13,14 +13,14 @@ describe('allocateLeafColumnWidths', () => {
13
13
  const name = createLeaf({ key: 'name', name: '姓名', width: 200 })
14
14
  const age = createLeaf({ key: 'age', name: '年龄', width: 200 })
15
15
  const action = createLeaf({ key: 'action', name: '操作', fixed: 'right' })
16
- // 模拟旧算法曾把操作列撑大
17
- action.data.width = 800
16
+ // 模拟上一次分配曾把操作列撑大
17
+ action.allocatedWidth = 800
18
18
 
19
19
  allocateLeafColumnWidths([name, age, action], 1000)
20
20
 
21
21
  expect(name.width).toBe(200)
22
22
  expect(age.width).toBe(200)
23
- expect(action.data.width).toBeUndefined()
23
+ expect(action.allocatedWidth).toBeUndefined()
24
24
  expect(action.width).toBe(100)
25
25
  })
26
26
 
@@ -45,7 +45,7 @@ describe('allocateLeafColumnWidths', () => {
45
45
  // totalBase = 100 + 100 + 100 = 300,剩余 700 只分给 age/score
46
46
  allocateLeafColumnWidths([name, age, score], 1000)
47
47
 
48
- expect(name.data.width).toBeUndefined()
48
+ expect(name.allocatedWidth).toBeUndefined()
49
49
  expect(name.width).toBe(100)
50
50
  expect(age.width).toBe(450)
51
51
  expect(score.width).toBe(450)
@@ -66,12 +66,53 @@ describe('allocateLeafColumnWidths', () => {
66
66
  it('无剩余空间时 grow 列回落到 minWidth', () => {
67
67
  const a = createLeaf({ key: 'a', name: 'A', width: 400 })
68
68
  const b = createLeaf({ key: 'b', name: 'B' })
69
- b.data.width = 300
69
+ b.allocatedWidth = 300
70
70
 
71
71
  allocateLeafColumnWidths([a, b], 450)
72
72
 
73
73
  expect(a.width).toBe(400)
74
- expect(b.data.width).toBeUndefined()
74
+ expect(b.allocatedWidth).toBeUndefined()
75
75
  expect(b.width).toBe(100)
76
76
  })
77
+
78
+ it('分配结果不写入列配置,森林重建后容器变窄仍会收缩', () => {
79
+ const config: TableColumn[] = [
80
+ { key: 'a', name: 'A' },
81
+ { key: 'b', name: 'B' }
82
+ ]
83
+
84
+ // 第一次挂载:宽容器
85
+ const first = config.map((c, i) => new ColumnNode(c, i, 0))
86
+ allocateLeafColumnWidths(first, 2000)
87
+ expect(first[0]!.width).toBe(1000)
88
+ expect(first[1]!.width).toBe(1000)
89
+
90
+ // 分配结果不得污染用户的列配置对象
91
+ expect(config[0]!.width).toBeUndefined()
92
+ expect(config[1]!.width).toBeUndefined()
93
+
94
+ // 模拟森林重建(路由返回 / v-if / columns 引用变化):
95
+ // 同一批列配置对象不应被误判为显式设宽
96
+ const rebuilt = config.map((c, i) => new ColumnNode(c, i, 0))
97
+ expect(rebuilt.every((node) => !node.explicitWidth)).toBe(true)
98
+
99
+ // 容器变窄后重新分配,列宽相应收缩
100
+ allocateLeafColumnWidths(rebuilt, 800)
101
+ expect(rebuilt[0]!.width).toBe(400)
102
+ expect(rebuilt[1]!.width).toBe(400)
103
+ })
104
+
105
+ it('显式宽度(配置/拖拽锁定)优先于分配宽度并使其作废', () => {
106
+ const col = createLeaf({ key: 'a', name: 'A' })
107
+ col.allocatedWidth = 500
108
+ expect(col.width).toBe(500)
109
+
110
+ // 模拟表头拖拽锁定
111
+ col.width = 300
112
+ col.explicitWidth = true
113
+
114
+ expect(col.data.width).toBe(300)
115
+ expect(col.allocatedWidth).toBeUndefined()
116
+ expect(col.width).toBe(300)
117
+ })
77
118
  })
@@ -6,16 +6,19 @@ import type { ColumnNode } from './node/col'
6
6
  * - `explicitWidth` 列:保持配置/拖拽锁定的宽度
7
7
  * - `fixed` 且未显式设宽的列:保持 `minWidth`,不参与剩余宽度均分
8
8
  * - 其余未显式设宽的列:均分容器剩余宽度
9
+ *
10
+ * 分配结果写入列节点自身的 `allocatedWidth`,不回写列配置的 `data.width`,
11
+ * 避免污染用户数据后在列森林重建时被误判为显式宽度。
9
12
  */
10
13
  export function allocateLeafColumnWidths(leafColumns: ColumnNode[], containerWidth: number): void {
11
14
  if (!containerWidth || !leafColumns.length) return
12
15
 
13
16
  const growColumns = leafColumns.filter((column) => !column.explicitWidth && !column.fixed)
14
- // fixed 且未显式设宽:清掉可能被旧算法撑大的残留 width,回落到 minWidth
17
+ // fixed 且未显式设宽:清掉之前的分配结果,回落到 minWidth
15
18
  const fixedFlexColumns = leafColumns.filter((column) => !column.explicitWidth && column.fixed)
16
19
 
17
20
  fixedFlexColumns.forEach((column) => {
18
- column.data.width = undefined
21
+ column.allocatedWidth = undefined
19
22
  })
20
23
 
21
24
  const explicitWidthTotal = leafColumns
@@ -28,11 +31,11 @@ export function allocateLeafColumnWidths(leafColumns: ColumnNode[], containerWid
28
31
  if (growColumns.length > 0 && totalBase < containerWidth) {
29
32
  const allocatedWidth = (containerWidth - totalBase) / growColumns.length
30
33
  growColumns.forEach((column) => {
31
- column.width = column.minWidth! + allocatedWidth
34
+ column.allocatedWidth = column.minWidth! + allocatedWidth
32
35
  })
33
36
  } else {
34
37
  growColumns.forEach((column) => {
35
- column.data.width = undefined
38
+ column.allocatedWidth = undefined
36
39
  })
37
40
  }
38
41
  }
@@ -46,7 +46,7 @@ export class ColumnNode extends TreeNode<TableColumn, ColumnNode> {
46
46
 
47
47
  /** 宽度 */
48
48
  get width(): number | undefined {
49
- const { width } = this.data
49
+ const width = this.data.width ?? this.derived.allocatedWidth
50
50
  if (!width || (this.minWidth && width && width < this.minWidth)) {
51
51
  return this.minWidth
52
52
  }
@@ -57,6 +57,21 @@ export class ColumnNode extends TreeNode<TableColumn, ColumnNode> {
57
57
  val = this.minWidth
58
58
  }
59
59
  this.data.width = val
60
+ // 显式宽度(配置/拖拽)优先于分配宽度,旧的分配结果直接作废
61
+ this.derived.allocatedWidth = undefined
62
+ }
63
+
64
+ /**
65
+ * 列宽分配算法写入的宽度。
66
+ * 与用户列配置 `data.width` 隔离:分配结果不回写列配置对象,
67
+ * 避免列森林重建(重挂载 / v-if / columns 引用变化)时把上一次的
68
+ * 分配残留误判为显式 width,导致列宽被永久锁定、容器变窄后出现滚动条。
69
+ */
70
+ get allocatedWidth(): number | undefined {
71
+ return this.derived.allocatedWidth
72
+ }
73
+ set allocatedWidth(val: number | undefined) {
74
+ this.derived.allocatedWidth = val
60
75
  }
61
76
  /** 最小宽度 */
62
77
  get minWidth(): number | undefined {
@@ -102,6 +117,11 @@ export class ColumnNode extends TreeNode<TableColumn, ColumnNode> {
102
117
 
103
118
  style: Record<string, number> = reactive({})
104
119
 
120
+ /** 派生状态(响应式),与用户列配置 data 隔离 */
121
+ private readonly derived = shallowReactive<{ allocatedWidth?: number }>({
122
+ allocatedWidth: undefined
123
+ })
124
+
105
125
  constructor(val: TableColumn, index: number, depth: number, parent?: ColumnNode) {
106
126
  super(isReactive(val) ? val : shallowReactive(val), index, depth, parent)
107
127
  this.explicitWidth = val.width !== undefined