@ticatec/uniface-element 0.3.18 → 0.3.19

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,33 @@ 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
+ set children(value) {
135
+ this._children = value;
136
+ }
147
137
  /**
148
138
  * 添加子节点
149
139
  * @param childItem 子节点数据
150
140
  * @returns 新创建的子节点
151
- *
152
- * @example
153
- * const childNode = parentNode.append({ id: 1, name: "子节点" });
154
141
  */
155
142
  append(childItem) {
156
- return new TreeNode(childItem, this);
143
+ if (Array.isArray(childItem)) {
144
+ let list = [];
145
+ this._children = [];
146
+ childItem.forEach(item => {
147
+ list.push(new TreeNode(item, this.nodes, this));
148
+ });
149
+ return list;
150
+ }
151
+ else {
152
+ return new TreeNode(childItem, this.nodes, this);
153
+ }
157
154
  }
158
155
  /**
159
156
  * 从父节点中分离(删除当前节点)
160
- *
161
- * @example
162
- * node.detach();
163
157
  */
164
158
  detach() {
165
159
  if (!this.parent) {
@@ -191,19 +185,13 @@ export class TreeNode {
191
185
  * 移动到另一个父节点下
192
186
  * @param newParent 新的父节点
193
187
  * @returns 新创建的节点(注意:返回的是新节点,不是原节点)
194
- *
195
- * @example
196
- * node.moveTo(newParentNode);
197
188
  */
198
189
  moveTo(newParent) {
199
- return new TreeNode(this._item, newParent, this._children);
190
+ return new TreeNode(this._item, this.nodes, newParent, this._children);
200
191
  }
201
192
  /**
202
193
  * 替换当前节点的数据
203
194
  * @param newItem 新的数据对象
204
- *
205
- * @example
206
- * node.replace({ id: 1, name: "新名称", ...otherFields });
207
195
  */
208
196
  replace(newItem) {
209
197
  this._item = newItem;
@@ -217,9 +205,6 @@ export class TreeNode {
217
205
  }
218
206
  /**
219
207
  * 删除所有子节点
220
- *
221
- * @example
222
- * node.removeChildren();
223
208
  */
224
209
  removeChildren() {
225
210
  this._children = [];
@@ -267,28 +252,6 @@ export class TreeNode {
267
252
  * - 节点的映射和查找
268
253
  * - 版本管理(用于响应式更新)
269
254
  * - 层级展开控制
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
255
  */
293
256
  export default class TreeNodes {
294
257
  checkIsRoot;
@@ -326,13 +289,6 @@ export default class TreeNodes {
326
289
  * 3. 根据 parentKeyField 建立父子关系
327
290
  * 4. 应用展开深度设置
328
291
  * 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
292
  */
337
293
  setData(list) {
338
294
  this.nodeMap = new Map();
@@ -340,7 +296,7 @@ export default class TreeNodes {
340
296
  // 第一遍:创建所有节点(不设置父子关系)
341
297
  for (const item of list) {
342
298
  const key = item[this.keyField];
343
- const node = new TreeNode(item); // 创建节点时不设置 parent
299
+ const node = new TreeNode(item, this); // 创建节点时不设置 parent
344
300
  this.nodeMap.set(key, node);
345
301
  }
346
302
  // 第二遍:建立父子关系
@@ -402,12 +358,19 @@ export default class TreeNodes {
402
358
  }
403
359
  /**
404
360
  * 获取展开的展示列表
361
+ * @returns 展开的所有节点(按层级顺序)
405
362
  */
406
363
  getHierarchyList() {
407
364
  let arr = [];
408
- this.addExpandNodesToList(this.nodes, arr);
365
+ this.addExpandNodesToList(this._nodes, arr);
409
366
  return arr;
410
367
  }
368
+ /**
369
+ * 将展开的节点添加到列表
370
+ * @param nodes 节点数组
371
+ * @param list 结果列表
372
+ * @private
373
+ */
411
374
  addExpandNodesToList(nodes, list) {
412
375
  for (let node of nodes) {
413
376
  list.push(node);
@@ -416,16 +379,6 @@ export default class TreeNodes {
416
379
  }
417
380
  }
418
381
  }
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
382
  /**
430
383
  * 增加一个新节点(便捷方法,会自动根据 parentKeyField 找到父节点)
431
384
  * @param item 要添加的节点数据
@@ -433,7 +386,7 @@ export default class TreeNodes {
433
386
  append(item) {
434
387
  if (parent) {
435
388
  if (this.checkIsRoot(item)) {
436
- let node = new TreeNode(item);
389
+ let node = new TreeNode(item, this);
437
390
  this._nodes.push(node);
438
391
  this._version++;
439
392
  return node;
@@ -443,9 +396,9 @@ export default class TreeNodes {
443
396
  }
444
397
  }
445
398
  else {
446
- let parent = this.findParent(this.nodes, item);
399
+ let parent = this._findNodeFromList(this.nodes, item, this.parentKeyField);
447
400
  if (!parent) {
448
- let node = new TreeNode(item, parent);
401
+ let node = new TreeNode(item, this, parent);
449
402
  this._version++;
450
403
  return node;
451
404
  }
@@ -454,18 +407,76 @@ export default class TreeNodes {
454
407
  }
455
408
  }
456
409
  }
457
- findParent(nodes, item) {
410
+ replace(item) {
411
+ let node = this._findNodeFromList(this.nodes, item, this.keyField);
412
+ if (node) {
413
+ node.replace(item);
414
+ this._version++;
415
+ }
416
+ return node;
417
+ }
418
+ remove(item) {
419
+ let node = this._findNodeFromList(this.nodes, item, this.keyField);
420
+ if (node) {
421
+ if (node.parent) {
422
+ node.detach();
423
+ }
424
+ else {
425
+ let pos = this.nodes.indexOf(node);
426
+ if (pos > -1) {
427
+ this.nodes.splice(pos, 1);
428
+ }
429
+ }
430
+ this._version++;
431
+ }
432
+ return node;
433
+ }
434
+ /**
435
+ * 从节点列表中查找节点
436
+ * @param nodes 节点数组
437
+ * @param item 要查找的数据项
438
+ * @param matchedKey 匹配的字段名
439
+ * @returns 找到的节点或 null
440
+ * @private
441
+ */
442
+ _findNodeFromList(nodes, item, matchedKey) {
458
443
  let found = null;
459
444
  for (const node of nodes) {
460
445
  if (!found) {
461
- if (node.item[this.keyField] == item[this.parentKeyField]) {
446
+ if (node.item[this.keyField] == item[matchedKey]) {
462
447
  found = node;
463
448
  }
464
449
  else {
465
- found = node.children && node.children.length > 0 ? this.findParent(node.children, item) : null;
450
+ found = node.children && node.children.length > 0 ? this._findNodeFromList(node.children, item, matchedKey) : null;
466
451
  }
467
452
  }
468
453
  }
469
454
  return found;
470
455
  }
471
456
  }
457
+ export class MenuNodes extends TreeNodes {
458
+ /**
459
+ * 删除所有属于分支但没有子节点的节点
460
+ */
461
+ cleanEmptyDirectories() {
462
+ console.log("原始菜单", this._nodes);
463
+ this._nodes = this._nodes.filter(n => this.pruneEmptyDirs(n) !== null);
464
+ }
465
+ pruneEmptyDirs(node) {
466
+ if (!node)
467
+ return null;
468
+ // 先递归处理子节点
469
+ if (node.children) {
470
+ node.children = node.children
471
+ .map((sn) => this.pruneEmptyDirs(sn))
472
+ .filter((child) => child !== null);
473
+ }
474
+ const isDir = this.checkIsDirectory?.(node);
475
+ const hasNoChildren = !node.children || node.children.length === 0;
476
+ // 如果是目录且没有子节点,则删除该节点
477
+ if (isDir && hasNoChildren) {
478
+ return null;
479
+ }
480
+ return node;
481
+ }
482
+ }
@@ -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;