@ticatec/uniface-element 0.3.18 → 0.3.20

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.
@@ -10,12 +10,6 @@ export function compareNumber(a, b) {
10
10
  /**
11
11
  * 节点视图配置类 - 定义节点的显示方式
12
12
  * @template T 节点数据的类型
13
- *
14
- * @example
15
- * const options = new NodeViewOptions<MyData>({
16
- * keyField: 'id',
17
- * textField: 'name'
18
- * });
19
13
  */
20
14
  export class NodeViewOptions {
21
15
  /**
@@ -56,8 +50,9 @@ export class NodeViewOptions {
56
50
  }
57
51
  }
58
52
  /**
59
- * TreeNode 类 - 树节点,包含数据和方法
53
+ * TreeNode 类 - 树节点,包含数据和方法(内部类)
60
54
  * @template T 节点数据的类型
55
+ * @internal
61
56
  *
62
57
  * @description
63
58
  * TreeNode 是树结构的基本单元,每个节点包含:
@@ -68,8 +63,10 @@ export class NodeViewOptions {
68
63
  * - 加载状态(loading)
69
64
  *
70
65
  * 节点提供了丰富的操作方法,可以直接在节点上进行增删改查操作。
66
+ *
67
+ * 注意:此类为内部实现,外部应使用 ITreeNode 接口
71
68
  */
72
- export class TreeNode {
69
+ class TreeNode {
73
70
  /**
74
71
  * 节点数据(私有,只读)
75
72
  * @private
@@ -97,21 +94,17 @@ export class TreeNode {
97
94
  * @private
98
95
  */
99
96
  _children = null;
97
+ nodes;
100
98
  /**
101
99
  * 创建树节点
102
100
  * @param item 节点数据
101
+ * @param nodes
103
102
  * @param parent 父节点(可选)
104
103
  * @param children 子节点列表(可选)
105
- *
106
- * @example
107
- * // 创建根节点
108
- * const rootNode = new TreeNode({ id: 1, name: "根节点" });
109
- *
110
- * // 创建子节点
111
- * const childNode = new TreeNode({ id: 2, name: "子节点" }, rootNode);
112
104
  */
113
- constructor(item, parent = null, children = null) {
105
+ constructor(item, nodes, parent = null, children = null) {
114
106
  this._item = item;
107
+ this.nodes = nodes;
115
108
  this._level = parent ? parent._level + 1 : 0;
116
109
  this.parent = parent;
117
110
  this._children = children;
@@ -134,32 +127,36 @@ export class TreeNode {
134
127
  /**
135
128
  * 获取子节点列表(只读)
136
129
  * @returns 子节点数组的浅拷贝,如果没有子节点返回 null
137
- *
138
- * @example
139
- * const children = node.children;
140
- * if (children) {
141
- * children.forEach(child => console.log(child.item));
142
- * }
143
130
  */
144
131
  get children() {
145
132
  return this._children ? [...this._children] : null;
146
133
  }
134
+ sort(compareFn) {
135
+ this._children?.sort((c1, c2) => compareFn(c1.item, c2.item));
136
+ }
137
+ set children(value) {
138
+ this._children = value;
139
+ }
147
140
  /**
148
141
  * 添加子节点
149
142
  * @param childItem 子节点数据
150
143
  * @returns 新创建的子节点
151
- *
152
- * @example
153
- * const childNode = parentNode.append({ id: 1, name: "子节点" });
154
144
  */
155
145
  append(childItem) {
156
- return new TreeNode(childItem, this);
146
+ if (Array.isArray(childItem)) {
147
+ let list = [];
148
+ this._children = [];
149
+ childItem.forEach(item => {
150
+ list.push(new TreeNode(item, this.nodes, this));
151
+ });
152
+ return list;
153
+ }
154
+ else {
155
+ return new TreeNode(childItem, this.nodes, this);
156
+ }
157
157
  }
158
158
  /**
159
159
  * 从父节点中分离(删除当前节点)
160
- *
161
- * @example
162
- * node.detach();
163
160
  */
164
161
  detach() {
165
162
  if (!this.parent) {
@@ -191,19 +188,13 @@ export class TreeNode {
191
188
  * 移动到另一个父节点下
192
189
  * @param newParent 新的父节点
193
190
  * @returns 新创建的节点(注意:返回的是新节点,不是原节点)
194
- *
195
- * @example
196
- * node.moveTo(newParentNode);
197
191
  */
198
192
  moveTo(newParent) {
199
- return new TreeNode(this._item, newParent, this._children);
193
+ return new TreeNode(this._item, this.nodes, newParent, this._children);
200
194
  }
201
195
  /**
202
196
  * 替换当前节点的数据
203
197
  * @param newItem 新的数据对象
204
- *
205
- * @example
206
- * node.replace({ id: 1, name: "新名称", ...otherFields });
207
198
  */
208
199
  replace(newItem) {
209
200
  this._item = newItem;
@@ -217,9 +208,6 @@ export class TreeNode {
217
208
  }
218
209
  /**
219
210
  * 删除所有子节点
220
- *
221
- * @example
222
- * node.removeChildren();
223
211
  */
224
212
  removeChildren() {
225
213
  this._children = [];
@@ -267,28 +255,6 @@ export class TreeNode {
267
255
  * - 节点的映射和查找
268
256
  * - 版本管理(用于响应式更新)
269
257
  * - 层级展开控制
270
- *
271
- * @example
272
- * interface MyData {
273
- * id: number;
274
- * name: string;
275
- * parentId: number | null;
276
- * }
277
- *
278
- * const treeNodes = new TreeNodes<MyData>({
279
- * keyField: 'id',
280
- * textField: 'name',
281
- * parentKeyField: 'parentId',
282
- * checkIsRoot: (item) => item.parentId === null,
283
- * checkIsDirectory: (node) => node.children !== null,
284
- * expendDepth: 2
285
- * });
286
- *
287
- * treeNodes.setData([
288
- * { id: 1, name: "根节点", parentId: null },
289
- * { id: 2, name: "子节点1", parentId: 1 },
290
- * { id: 3, name: "子节点2", parentId: 1 }
291
- * ]);
292
258
  */
293
259
  export default class TreeNodes {
294
260
  checkIsRoot;
@@ -326,13 +292,6 @@ export default class TreeNodes {
326
292
  * 3. 根据 parentKeyField 建立父子关系
327
293
  * 4. 应用展开深度设置
328
294
  * 5. 触发版本更新
329
- *
330
- * @example
331
- * treeNodes.setData([
332
- * { id: 1, name: "根节点", parentId: null },
333
- * { id: 2, name: "子节点1", parentId: 1 },
334
- * { id: 3, name: "子节点2", parentId: 1 }
335
- * ]);
336
295
  */
337
296
  setData(list) {
338
297
  this.nodeMap = new Map();
@@ -340,7 +299,7 @@ export default class TreeNodes {
340
299
  // 第一遍:创建所有节点(不设置父子关系)
341
300
  for (const item of list) {
342
301
  const key = item[this.keyField];
343
- const node = new TreeNode(item); // 创建节点时不设置 parent
302
+ const node = new TreeNode(item, this); // 创建节点时不设置 parent
344
303
  this.nodeMap.set(key, node);
345
304
  }
346
305
  // 第二遍:建立父子关系
@@ -402,12 +361,19 @@ export default class TreeNodes {
402
361
  }
403
362
  /**
404
363
  * 获取展开的展示列表
364
+ * @returns 展开的所有节点(按层级顺序)
405
365
  */
406
366
  getHierarchyList() {
407
367
  let arr = [];
408
- this.addExpandNodesToList(this.nodes, arr);
368
+ this.addExpandNodesToList(this._nodes, arr);
409
369
  return arr;
410
370
  }
371
+ /**
372
+ * 将展开的节点添加到列表
373
+ * @param nodes 节点数组
374
+ * @param list 结果列表
375
+ * @private
376
+ */
411
377
  addExpandNodesToList(nodes, list) {
412
378
  for (let node of nodes) {
413
379
  list.push(node);
@@ -416,16 +382,6 @@ export default class TreeNodes {
416
382
  }
417
383
  }
418
384
  }
419
- /**
420
- * 不检测匹配情况,在指定的父节点下添加子节点
421
- */
422
- batchAttachToParent(parent, list) {
423
- let nodes = [];
424
- for (let item of list) {
425
- nodes.push(new TreeNode(item, parent));
426
- }
427
- return nodes;
428
- }
429
385
  /**
430
386
  * 增加一个新节点(便捷方法,会自动根据 parentKeyField 找到父节点)
431
387
  * @param item 要添加的节点数据
@@ -433,7 +389,7 @@ export default class TreeNodes {
433
389
  append(item) {
434
390
  if (parent) {
435
391
  if (this.checkIsRoot(item)) {
436
- let node = new TreeNode(item);
392
+ let node = new TreeNode(item, this);
437
393
  this._nodes.push(node);
438
394
  this._version++;
439
395
  return node;
@@ -443,9 +399,9 @@ export default class TreeNodes {
443
399
  }
444
400
  }
445
401
  else {
446
- let parent = this.findParent(this.nodes, item);
402
+ let parent = this._findNodeFromList(this.nodes, item, this.parentKeyField);
447
403
  if (!parent) {
448
- let node = new TreeNode(item, parent);
404
+ let node = new TreeNode(item, this, parent);
449
405
  this._version++;
450
406
  return node;
451
407
  }
@@ -454,18 +410,76 @@ export default class TreeNodes {
454
410
  }
455
411
  }
456
412
  }
457
- findParent(nodes, item) {
413
+ replace(item) {
414
+ let node = this._findNodeFromList(this.nodes, item, this.keyField);
415
+ if (node) {
416
+ node.replace(item);
417
+ this._version++;
418
+ }
419
+ return node;
420
+ }
421
+ remove(item) {
422
+ let node = this._findNodeFromList(this.nodes, item, this.keyField);
423
+ if (node) {
424
+ if (node.parent) {
425
+ node.detach();
426
+ }
427
+ else {
428
+ let pos = this.nodes.indexOf(node);
429
+ if (pos > -1) {
430
+ this.nodes.splice(pos, 1);
431
+ }
432
+ }
433
+ this._version++;
434
+ }
435
+ return node;
436
+ }
437
+ /**
438
+ * 从节点列表中查找节点
439
+ * @param nodes 节点数组
440
+ * @param item 要查找的数据项
441
+ * @param matchedKey 匹配的字段名
442
+ * @returns 找到的节点或 null
443
+ * @private
444
+ */
445
+ _findNodeFromList(nodes, item, matchedKey) {
458
446
  let found = null;
459
447
  for (const node of nodes) {
460
448
  if (!found) {
461
- if (node.item[this.keyField] == item[this.parentKeyField]) {
449
+ if (node.item[this.keyField] == item[matchedKey]) {
462
450
  found = node;
463
451
  }
464
452
  else {
465
- found = node.children && node.children.length > 0 ? this.findParent(node.children, item) : null;
453
+ found = node.children && node.children.length > 0 ? this._findNodeFromList(node.children, item, matchedKey) : null;
466
454
  }
467
455
  }
468
456
  }
469
457
  return found;
470
458
  }
471
459
  }
460
+ export class MenuNodes extends TreeNodes {
461
+ /**
462
+ * 删除所有属于分支但没有子节点的节点
463
+ */
464
+ cleanEmptyDirectories() {
465
+ console.log("原始菜单", this._nodes);
466
+ this._nodes = this._nodes.filter(n => this.pruneEmptyDirs(n) !== null);
467
+ }
468
+ pruneEmptyDirs(node) {
469
+ if (!node)
470
+ return null;
471
+ // 先递归处理子节点
472
+ if (node.children) {
473
+ node.children = node.children
474
+ .map((sn) => this.pruneEmptyDirs(sn))
475
+ .filter((child) => child !== null);
476
+ }
477
+ const isDir = this.checkIsDirectory?.(node);
478
+ const hasNoChildren = !node.children || node.children.length === 0;
479
+ // 如果是目录且没有子节点,则删除该节点
480
+ if (isDir && hasNoChildren) {
481
+ return null;
482
+ }
483
+ return node;
484
+ }
485
+ }
@@ -1,8 +1,10 @@
1
1
  import TreeNodes from "./TreeNodes";
2
2
  import type { ITreeNode } from "./TreeNodes";
3
3
  import type { CheckIsRoot, CompareFun, GetText, CheckIsDirectory, TreeNodeOptions } from "./TreeNodes";
4
+ import { MenuNodes } from "./TreeNodes";
4
5
  import { compareNumber } from "./TreeNodes";
5
6
  export default TreeNodes;
6
7
  export { compareNumber };
8
+ export { MenuNodes };
7
9
  export type { ITreeNode };
8
10
  export type { CheckIsRoot, CompareFun, GetText, CheckIsDirectory, TreeNodeOptions };
package/dist/lib/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import TreeNodes from "./TreeNodes";
2
+ import { MenuNodes } from "./TreeNodes";
2
3
  import { compareNumber } from "./TreeNodes";
3
4
  export default TreeNodes;
4
5
  export { compareNumber };
6
+ export { MenuNodes };
@@ -1,7 +1,7 @@
1
- import type { TreeNode } from "../lib";
1
+ import type { ITreeNode } from "../lib";
2
2
  export default interface MenuItem {
3
3
  [key: string]: any;
4
4
  }
5
- export interface MenuNode extends TreeNode<MenuItem> {
5
+ export interface MenuNode extends ITreeNode<MenuItem> {
6
6
  }
7
7
  export type OnMenuClick = (item: MenuItem) => void;