@ticatec/uniface-element 0.3.16 → 0.3.18
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/dist/lib/TreeNode_README.md +247 -119
- package/dist/lib/TreeNode_README_CN.md +248 -120
- package/dist/lib/TreeNodes.d.ts +339 -85
- package/dist/lib/TreeNodes.js +376 -206
- package/dist/lib/index.d.ts +3 -4
- package/dist/lib/index.js +1 -2
- package/dist/nav-menu/Menus.d.ts +2 -2
- package/dist/nav-menu/Menus.js +6 -4
- package/dist/ticatec-uniface-web.css +29 -5
- package/dist/tree-view/README.md +131 -6
- package/dist/tree-view/TreeNodeView.svelte +10 -11
- package/dist/tree-view/TreeNodeView.svelte.d.ts +5 -5
- package/dist/tree-view/TreeView.svelte +23 -8
- package/dist/tree-view/TreeView.svelte.d.ts +3 -2
- package/dist/tree-view/Types.d.ts +5 -5
- package/dist/tree-view/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -2,52 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
## 概述
|
|
4
4
|
|
|
5
|
-
`TreeNode`
|
|
5
|
+
`TreeNode` 是一个类,用于表示树形结构中的节点。它封装了节点的数据、子节点、展开状态,并提供了便捷的方法来操作节点本身和其子节点。
|
|
6
6
|
|
|
7
|
-
每个从 `TreeNodes`
|
|
7
|
+
每个从 `TreeNodes` 或 `CommonTreeNodes` 创建的节点都是 `TreeNode` 类的实例,具有内置的方法。
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## TreeNode 类
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
item: T;
|
|
12
|
+
class TreeNode<T> {
|
|
13
|
+
/** 节点的数据对象(只读) */
|
|
14
|
+
public readonly item: T;
|
|
15
15
|
|
|
16
16
|
/** 是否展开(显示子节点) */
|
|
17
|
-
expand
|
|
17
|
+
public expand: boolean;
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/** 是否正在加载(用于懒加载) */
|
|
20
|
+
public loading: boolean;
|
|
21
|
+
|
|
22
|
+
/** 父节点(根节点为 null) */
|
|
23
|
+
public parent: TreeNode<T> | null;
|
|
24
|
+
|
|
25
|
+
/** 子节点(只读,使用方法修改) */
|
|
26
|
+
get children(): ReadonlyArray<TreeNode<T>>;
|
|
27
|
+
|
|
28
|
+
/** 添加子节点到当前节点 */
|
|
29
|
+
append(childItem: T): void;
|
|
30
|
+
|
|
31
|
+
/** 从父节点中分离(删除当前节点) */
|
|
32
|
+
detach(): void;
|
|
33
|
+
|
|
34
|
+
/** 将当前节点移动到另一个父节点下 */
|
|
35
|
+
moveTo(newParent: TreeNode<T>): void;
|
|
36
|
+
|
|
37
|
+
/** 替换当前节点的数据 */
|
|
38
|
+
replace(newItem: T): void;
|
|
39
|
+
|
|
40
|
+
/** 根据 ID 删除指定的子节点 */
|
|
41
|
+
removeChild(childId: any): void;
|
|
42
|
+
|
|
43
|
+
/** 删除所有子节点 */
|
|
44
|
+
removeChildren(): void;
|
|
21
45
|
}
|
|
22
46
|
```
|
|
23
47
|
|
|
24
|
-
|
|
48
|
+
## NodeViewOptions 类
|
|
25
49
|
|
|
26
|
-
|
|
50
|
+
`NodeViewOptions` 类处理树节点的视图配置,将显示关注点与数据分离:
|
|
27
51
|
|
|
28
52
|
```typescript
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
|
|
53
|
+
class NodeViewOptions<T> {
|
|
54
|
+
/** 唯一标识字段名 */
|
|
55
|
+
keyField: keyof T;
|
|
32
56
|
|
|
33
|
-
/**
|
|
34
|
-
|
|
57
|
+
/** 显示文字的字段名或函数 */
|
|
58
|
+
textField: keyof T | ((data: T) => string);
|
|
35
59
|
|
|
36
|
-
/**
|
|
37
|
-
|
|
60
|
+
/** 可选:图标的字段名或函数 */
|
|
61
|
+
iconField?: keyof T | ((data: T) => string);
|
|
38
62
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
63
|
+
/** 可选:CSS 类名的字段名或函数 */
|
|
64
|
+
cssClassField?: keyof T | ((data: T) => string);
|
|
41
65
|
|
|
42
|
-
/**
|
|
43
|
-
|
|
66
|
+
/** 可选:内联样式的字段名或函数 */
|
|
67
|
+
styleField?: keyof T | ((data: T) => Record<string, string>);
|
|
44
68
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
69
|
+
/** 从数据获取显示文字 */
|
|
70
|
+
getText(data: T): string;
|
|
71
|
+
|
|
72
|
+
/** 从数据获取图标 */
|
|
73
|
+
getIcon(data: T): string | undefined;
|
|
74
|
+
|
|
75
|
+
/** 从数据获取 CSS 类名 */
|
|
76
|
+
getCssClass(data: T): string | undefined;
|
|
77
|
+
|
|
78
|
+
/** 从数据获取样式 */
|
|
79
|
+
getStyle(data: T): Record<string, string> | undefined;
|
|
80
|
+
|
|
81
|
+
/** 从数据获取唯一键 */
|
|
82
|
+
getKey(data: T): any;
|
|
47
83
|
}
|
|
48
84
|
```
|
|
49
85
|
|
|
50
|
-
##
|
|
86
|
+
## TreeNode 方法详解
|
|
51
87
|
|
|
52
88
|
### 1. append(childItem)
|
|
53
89
|
|
|
@@ -62,33 +98,31 @@ interface TreeNodeWithMethods<T> extends TreeNode<T> {
|
|
|
62
98
|
parentNode.append({
|
|
63
99
|
id: 123,
|
|
64
100
|
name: "新子节点",
|
|
65
|
-
|
|
101
|
+
parentId: parentNode.item.id // 会自动设置
|
|
66
102
|
});
|
|
67
103
|
```
|
|
68
104
|
|
|
69
105
|
**注意事项**:
|
|
70
|
-
-
|
|
71
|
-
- **手动添加子节点**:可以手动调用 `append()` 添加子节点
|
|
72
|
-
- 如果节点还未加载,会自动初始化 `children` 数组
|
|
73
|
-
- 节点会从 leaf 转为 branch,UI 会显示 branch 图标
|
|
106
|
+
- 自动初始化 children 数组(如果需要)
|
|
74
107
|
- 如果配置了排序函数,会自动排序
|
|
75
108
|
- 父节点的 `expand` 会自动设置为 `true`
|
|
109
|
+
- 触发版本更新以实现响应式更新
|
|
76
110
|
|
|
77
111
|
---
|
|
78
112
|
|
|
79
|
-
### 2.
|
|
113
|
+
### 2. detach()
|
|
80
114
|
|
|
81
|
-
|
|
115
|
+
从父节点中分离当前节点。
|
|
82
116
|
|
|
83
117
|
**参数**: 无
|
|
84
118
|
|
|
85
119
|
**示例**:
|
|
86
120
|
```typescript
|
|
87
|
-
//
|
|
88
|
-
node.
|
|
121
|
+
// 从父节点中删除该节点
|
|
122
|
+
node.detach();
|
|
89
123
|
|
|
90
124
|
// 删除后清空引用
|
|
91
|
-
activeNode.
|
|
125
|
+
activeNode.detach();
|
|
92
126
|
activeNode = null;
|
|
93
127
|
```
|
|
94
128
|
|
|
@@ -96,10 +130,40 @@ activeNode = null;
|
|
|
96
130
|
- 从父节点的 `children` 数组中移除
|
|
97
131
|
- 从内部映射表中删除
|
|
98
132
|
- 如果是根节点,从根节点列表中移除
|
|
133
|
+
- 触发版本更新以实现响应式更新
|
|
99
134
|
|
|
100
135
|
---
|
|
101
136
|
|
|
102
|
-
### 3.
|
|
137
|
+
### 3. moveTo(newParent)
|
|
138
|
+
|
|
139
|
+
将当前节点移动到另一个父节点下。
|
|
140
|
+
|
|
141
|
+
**参数**:
|
|
142
|
+
- `newParent: TreeNode<T>` - 新的父节点(不是 ID)
|
|
143
|
+
|
|
144
|
+
**示例**:
|
|
145
|
+
```typescript
|
|
146
|
+
// 移动到另一个父节点
|
|
147
|
+
node.moveTo(newParentNode);
|
|
148
|
+
|
|
149
|
+
// 先获取父节点
|
|
150
|
+
const parentNode = treeNodes.nodeMap.get(parentId);
|
|
151
|
+
if (parentNode) {
|
|
152
|
+
node.moveTo(parentNode);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**注意事项**:
|
|
157
|
+
- 接收 TreeNode 实例作为参数,不是 ID
|
|
158
|
+
- 会自动更新节点数据中的 `parentKeyField`
|
|
159
|
+
- 从原父节点的 `children` 中移除
|
|
160
|
+
- 添加到新父节点的 `children`
|
|
161
|
+
- 如果配置了排序函数,会自动重新排序
|
|
162
|
+
- 触发版本更新以实现响应式更新
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### 4. replace(newItem)
|
|
103
167
|
|
|
104
168
|
替换当前节点的数据。
|
|
105
169
|
|
|
@@ -127,32 +191,7 @@ node.replace({
|
|
|
127
191
|
- 通常需要保持 `id` 字段不变
|
|
128
192
|
- 如果配置了排序函数且排序字段改变,会自动重新排序
|
|
129
193
|
- 会自动触发视图更新
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
### 4. moveTo(newParentId)
|
|
134
|
-
|
|
135
|
-
将当前节点移动到另一个父节点下。
|
|
136
|
-
|
|
137
|
-
**参数**:
|
|
138
|
-
- `newParentId: string | number` - 新父节点的 ID
|
|
139
|
-
|
|
140
|
-
**示例**:
|
|
141
|
-
```typescript
|
|
142
|
-
// 移动到指定的父节点
|
|
143
|
-
node.moveTo("parent-123");
|
|
144
|
-
|
|
145
|
-
// 使用另一个节点的 ID
|
|
146
|
-
node.moveTo(anotherNode.item.id);
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**注意事项**:
|
|
150
|
-
- 会自动更新节点的 `parent` 字段
|
|
151
|
-
- 从原父节点的 `children` 中移除
|
|
152
|
-
- 添加到新父节点的 `children`
|
|
153
|
-
- **Lazy 节点处理**:如果新父节点是 lazy-loaded 且未加载(`children === null`),会自动初始化 `children` 数组
|
|
154
|
-
- 新父节点会从 leaf 转为 branch,UI 会显示 branch 图标
|
|
155
|
-
- 未来用户展开节点时,lazyLoader 加载的子节点会与移动过来的节点合并
|
|
194
|
+
- 保留子节点、展开状态和加载状态
|
|
156
195
|
|
|
157
196
|
---
|
|
158
197
|
|
|
@@ -177,6 +216,7 @@ parentNode.removeChild(childId);
|
|
|
177
216
|
- 只删除直接子节点,不会递归删除孙子节点
|
|
178
217
|
- 如果子节点不存在,会静默忽略
|
|
179
218
|
- 子节点的所有后代也会被删除
|
|
219
|
+
- 触发版本更新以实现响应式更新
|
|
180
220
|
|
|
181
221
|
---
|
|
182
222
|
|
|
@@ -200,37 +240,68 @@ parentNode.append(newChildData);
|
|
|
200
240
|
- 会删除所有子节点及其后代
|
|
201
241
|
- 节点本身的 `expand` 状态保持不变
|
|
202
242
|
- 内部映射表会同步更新
|
|
243
|
+
- 触发版本更新以实现响应式更新
|
|
244
|
+
|
|
245
|
+
## CommonTreeNodes 配置选项
|
|
246
|
+
|
|
247
|
+
创建树时,可以使用以下选项进行配置:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
interface TreeNodeOptions<T> {
|
|
251
|
+
/** 唯一标识字段名(默认:'id') */
|
|
252
|
+
keyField?: keyof T;
|
|
253
|
+
|
|
254
|
+
/** 显示文字的字段名或函数(默认:'text') */
|
|
255
|
+
textField?: keyof T | ((data: T) => string);
|
|
256
|
+
|
|
257
|
+
/** 父节点引用字段名(默认:'parentId') */
|
|
258
|
+
parentKeyField?: keyof T;
|
|
259
|
+
|
|
260
|
+
/** 判断数据是否为根节点的函数 */
|
|
261
|
+
checkIsRoot: (data: T) => boolean;
|
|
262
|
+
|
|
263
|
+
/** 可选:判断节点是否为分支(有子节点)的函数 */
|
|
264
|
+
checkIsDirectory?: (node: TreeNode<T>) => boolean;
|
|
265
|
+
|
|
266
|
+
/** 可选:排序比较函数 */
|
|
267
|
+
compareFun?: (o1: T, o2: T) => number | undefined;
|
|
268
|
+
|
|
269
|
+
/** 可选:展开深度(默认:1) */
|
|
270
|
+
expendDepth?: number;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
203
273
|
|
|
204
274
|
## 完整使用示例
|
|
205
275
|
|
|
206
276
|
```svelte
|
|
207
277
|
<script>
|
|
208
278
|
import TreeView from "@ticatec/uniface-element/TreeView";
|
|
209
|
-
import
|
|
279
|
+
import { CommonTreeNodes, type TreeNode } from "@ticatec/uniface-element/lib/TreeNodes";
|
|
210
280
|
|
|
211
281
|
// 数据类型定义
|
|
212
282
|
interface MyData {
|
|
213
283
|
id: number;
|
|
214
284
|
name: string;
|
|
215
|
-
|
|
285
|
+
parentId: number | null;
|
|
216
286
|
}
|
|
217
287
|
|
|
218
|
-
let activeNode:
|
|
288
|
+
let activeNode: TreeNode<MyData> | null = null;
|
|
219
289
|
|
|
220
290
|
// 创建树结构
|
|
221
|
-
const treeNodes = new
|
|
291
|
+
const treeNodes = new CommonTreeNodes<MyData>({
|
|
222
292
|
keyField: 'id',
|
|
223
293
|
textField: 'name',
|
|
224
|
-
parentKeyField: '
|
|
225
|
-
checkIsRoot: (item) => item.
|
|
226
|
-
checkIsDirectory: (node) => node.children
|
|
294
|
+
parentKeyField: 'parentId',
|
|
295
|
+
checkIsRoot: (item) => item.parentId === null,
|
|
296
|
+
checkIsDirectory: (node) => node.children.length > 0,
|
|
297
|
+
expendDepth: 2
|
|
227
298
|
});
|
|
228
299
|
|
|
229
300
|
// 初始化数据
|
|
230
301
|
treeNodes.setData([
|
|
231
|
-
{ id: 1, name: "根节点",
|
|
232
|
-
{ id: 2, name: "子节点1",
|
|
233
|
-
{ id: 3, name: "子节点2",
|
|
302
|
+
{ id: 1, name: "根节点", parentId: null },
|
|
303
|
+
{ id: 2, name: "子节点1", parentId: 1 },
|
|
304
|
+
{ id: 3, name: "子节点2", parentId: 1 }
|
|
234
305
|
]);
|
|
235
306
|
|
|
236
307
|
// 添加子节点
|
|
@@ -240,14 +311,14 @@ parentNode.append(newChildData);
|
|
|
240
311
|
activeNode.append({
|
|
241
312
|
id: Date.now(),
|
|
242
313
|
name: "新子节点",
|
|
243
|
-
|
|
314
|
+
parentId: activeNode.item.id
|
|
244
315
|
});
|
|
245
316
|
}
|
|
246
317
|
|
|
247
318
|
// 删除当前节点
|
|
248
319
|
function deleteCurrentNode() {
|
|
249
320
|
if (!activeNode) return;
|
|
250
|
-
activeNode.
|
|
321
|
+
activeNode.detach();
|
|
251
322
|
activeNode = null;
|
|
252
323
|
}
|
|
253
324
|
|
|
@@ -275,7 +346,10 @@ parentNode.append(newChildData);
|
|
|
275
346
|
// 移动节点
|
|
276
347
|
function moveNodeTo(newParentId: number) {
|
|
277
348
|
if (!activeNode) return;
|
|
278
|
-
|
|
349
|
+
const newParent = treeNodes.nodeMap.get(newParentId);
|
|
350
|
+
if (newParent) {
|
|
351
|
+
activeNode.moveTo(newParent);
|
|
352
|
+
}
|
|
279
353
|
}
|
|
280
354
|
</script>
|
|
281
355
|
|
|
@@ -285,7 +359,7 @@ parentNode.append(newChildData);
|
|
|
285
359
|
version={treeNodes.version}
|
|
286
360
|
textField="name"
|
|
287
361
|
bind:activeNode
|
|
288
|
-
checkIsDirectory={(node) => node.children
|
|
362
|
+
checkIsDirectory={(node) => node.children.length > 0}
|
|
289
363
|
/>
|
|
290
364
|
|
|
291
365
|
{#if activeNode}
|
|
@@ -303,30 +377,64 @@ parentNode.append(newChildData);
|
|
|
303
377
|
|
|
304
378
|
## 与 TreeNodes 类方法的区别
|
|
305
379
|
|
|
306
|
-
`TreeNode`
|
|
380
|
+
`TreeNode` 实例方法和 `CommonTreeNodes`/`TreeNodes` 类方法有明确的职责分工:
|
|
307
381
|
|
|
308
|
-
### TreeNode
|
|
382
|
+
### TreeNode 实例方法(用于操作特定节点实例)
|
|
309
383
|
|
|
310
384
|
```typescript
|
|
311
|
-
//
|
|
385
|
+
// 直接在节点实例上操作
|
|
312
386
|
node.append(childItem); // 添加子节点到该节点
|
|
313
|
-
node.
|
|
387
|
+
node.detach(); // 从父节点中分离该节点
|
|
314
388
|
node.replace(newItem); // 替换该节点的数据
|
|
315
|
-
node.moveTo(
|
|
389
|
+
node.moveTo(newParentNode); // 将该节点移动到另一个父节点
|
|
316
390
|
node.removeChild(childId); // 删除该节点的指定子节点
|
|
317
391
|
node.removeChildren(); // 删除该节点的所有子节点
|
|
318
392
|
```
|
|
319
393
|
|
|
320
|
-
### TreeNodes
|
|
394
|
+
### CommonTreeNodes/TreeNodes 类方法(用于树级别的操作)
|
|
321
395
|
|
|
322
396
|
```typescript
|
|
323
|
-
//
|
|
397
|
+
// 树级别操作
|
|
324
398
|
treeNodes.setData(data); // 初始化树结构
|
|
325
399
|
treeNodes.append(item); // 添加新节点(自动找父节点)
|
|
326
400
|
treeNodes.nodes; // 获取根节点列表
|
|
327
401
|
treeNodes.version; // 获取版本号
|
|
328
|
-
treeNodes.getHierarchyList(); //
|
|
329
|
-
treeNodes.extractDirectories(item); //
|
|
402
|
+
treeNodes.getHierarchyList(); // 获取展开的节点列表(仅 TreeNodes)
|
|
403
|
+
treeNodes.extractDirectories(item); // 提取目录结构(仅 TreeNodes)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## 主要属性
|
|
407
|
+
|
|
408
|
+
### item(只读)
|
|
409
|
+
节点中存储的数据对象。不能直接修改。使用 `replace()` 来更新数据。
|
|
410
|
+
|
|
411
|
+
### children(只读)
|
|
412
|
+
返回子节点的只读数组。要修改子节点,使用以下方法:
|
|
413
|
+
- `append()` - 添加子节点
|
|
414
|
+
- `removeChild()` - 删除特定子节点
|
|
415
|
+
- `removeChildren()` - 删除所有子节点
|
|
416
|
+
|
|
417
|
+
### expand
|
|
418
|
+
控制节点是否展开(显示其子节点)。可以直接设置:
|
|
419
|
+
```typescript
|
|
420
|
+
node.expand = true; // 展开节点
|
|
421
|
+
node.expand = false; // 折叠节点
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### loading
|
|
425
|
+
指示节点是否正在加载数据(用于懒加载)。可以直接设置:
|
|
426
|
+
```typescript
|
|
427
|
+
node.loading = true; // 显示加载状态
|
|
428
|
+
node.loading = false; // 隐藏加载状态
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### parent
|
|
432
|
+
父节点的引用。根节点为 `null`。
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
if (node.parent) {
|
|
436
|
+
console.log("父节点名称:", node.parent.item.name);
|
|
437
|
+
}
|
|
330
438
|
```
|
|
331
439
|
|
|
332
440
|
## 响应式更新
|
|
@@ -359,10 +467,14 @@ const node = treeNodes.nodeMap.get(nodeId);
|
|
|
359
467
|
### Q: 如何遍历子节点?
|
|
360
468
|
|
|
361
469
|
```typescript
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
470
|
+
// children 是只读的,但可以迭代
|
|
471
|
+
node.children.forEach(child => {
|
|
472
|
+
console.log(child.item.name);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// 或使用 for...of
|
|
476
|
+
for (const child of node.children) {
|
|
477
|
+
console.log(child.item.name);
|
|
366
478
|
}
|
|
367
479
|
```
|
|
368
480
|
|
|
@@ -370,40 +482,24 @@ if (node.children) {
|
|
|
370
482
|
|
|
371
483
|
```typescript
|
|
372
484
|
function findChild(node: TreeNode, childId: number) {
|
|
373
|
-
if (!node.children) return null;
|
|
374
485
|
return node.children.find(child => child.item.id === childId) || null;
|
|
375
486
|
}
|
|
376
487
|
```
|
|
377
488
|
|
|
378
|
-
### Q:
|
|
379
|
-
|
|
380
|
-
对于使用 lazyLoader 的节点(`children === null`):
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
// ✅ 当节点被选中时,TreeView 会自动触发加载
|
|
384
|
-
// 点击节点或设置 activeNode 时:
|
|
385
|
-
// - 如果是 lazy 节点且未加载
|
|
386
|
-
// - 自动调用 lazyLoader.load()
|
|
387
|
-
// - children 被设置,节点转为 branch
|
|
388
|
-
|
|
389
|
-
// 手动添加子节点
|
|
390
|
-
lazyNode.append(childItem); // 初始化 children 数组并添加子节点
|
|
391
|
-
// 节点转为 branch,显示 branch 图标
|
|
392
|
-
|
|
393
|
-
lazyNode.moveTo(parentId); // 移动到该节点会自动初始化 children
|
|
489
|
+
### Q: 可以直接修改 children 数组吗?
|
|
394
490
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
491
|
+
不可以。`children` 属性是只读的。请使用提供的方法:
|
|
492
|
+
- `append()` 添加
|
|
493
|
+
- `removeChild()` 删除特定子节点
|
|
494
|
+
- `removeChildren()` 删除所有子节点
|
|
399
495
|
|
|
400
496
|
### Q: 如何递归操作所有子孙节点?
|
|
401
497
|
|
|
402
498
|
```typescript
|
|
403
499
|
function traverse(node: TreeNode, callback: (node: TreeNode) => void) {
|
|
404
500
|
callback(node);
|
|
405
|
-
|
|
406
|
-
|
|
501
|
+
for (const child of node.children) {
|
|
502
|
+
traverse(child, callback);
|
|
407
503
|
}
|
|
408
504
|
}
|
|
409
505
|
|
|
@@ -414,36 +510,68 @@ traverse(rootNode, (node) => {
|
|
|
414
510
|
});
|
|
415
511
|
```
|
|
416
512
|
|
|
513
|
+
### Q: TreeNode 和 NodeViewOptions 有什么区别?
|
|
514
|
+
|
|
515
|
+
- **TreeNode**:表示数据结构。每个节点都是具有操作方法的实例。
|
|
516
|
+
- **NodeViewOptions**:配置类,用于定义节点如何显示(文字字段、图标、CSS 等)。
|
|
517
|
+
|
|
518
|
+
这种分离使得 TreeNode 可以在不同上下文中使用(TreeView、TreeDataGrid 等),而不会与视图特定关注点耦合。
|
|
519
|
+
|
|
417
520
|
## 最佳实践
|
|
418
521
|
|
|
419
522
|
1. **优先使用节点方法**:当你有节点实例时,直接调用节点方法更直观
|
|
420
523
|
|
|
421
524
|
2. **保持 ID 不变**:使用 `replace()` 时,确保 `id` 字段保持不变
|
|
422
525
|
|
|
423
|
-
3.
|
|
526
|
+
3. **正确使用 moveTo**:`moveTo()` 方法接收 TreeNode 实例,不是 ID:
|
|
527
|
+
```typescript
|
|
528
|
+
// ❌ 错误
|
|
529
|
+
node.moveTo(123);
|
|
424
530
|
|
|
425
|
-
|
|
531
|
+
// ✅ 正确
|
|
532
|
+
const parentNode = treeNodes.nodeMap.get(123);
|
|
533
|
+
node.moveTo(parentNode);
|
|
534
|
+
```
|
|
426
535
|
|
|
427
|
-
|
|
536
|
+
4. **版本管理**:不需要手动管理版本,节点方法会自动更新
|
|
428
537
|
|
|
429
|
-
|
|
538
|
+
5. **类型安全**:使用 TypeScript 泛型确保类型安全
|
|
430
539
|
|
|
431
540
|
```typescript
|
|
432
541
|
// ✅ 推荐:使用泛型
|
|
433
|
-
const treeNodes = new
|
|
542
|
+
const treeNodes = new CommonTreeNodes<MyData>({ ... });
|
|
434
543
|
|
|
435
|
-
//
|
|
544
|
+
// 节点类型会自动推断
|
|
436
545
|
node.append({ id: 1, name: "..." }); // 类型检查
|
|
437
546
|
```
|
|
438
547
|
|
|
548
|
+
6. **children 是只读的**:不要尝试直接修改 children 数组。始终使用提供的方法。
|
|
549
|
+
|
|
439
550
|
## 性能考虑
|
|
440
551
|
|
|
441
552
|
- 所有节点方法的时间复杂度都是 O(1) 或 O(n),其中 n 是子节点数量
|
|
442
553
|
- 删除操作会同时从内部映射表中移除,保持一致性
|
|
443
554
|
- 大批量操作时,考虑直接使用 `setData()` 重建树结构
|
|
444
555
|
|
|
556
|
+
## 架构设计
|
|
557
|
+
|
|
558
|
+
### 与视图解耦
|
|
559
|
+
|
|
560
|
+
TreeNode 设计为独立于任何特定视图组件:
|
|
561
|
+
- TreeNode 中没有视图特定的逻辑
|
|
562
|
+
- NodeViewOptions 单独处理视图配置
|
|
563
|
+
- 可用于 TreeView、TreeDataGrid 或任何其他基于树的组件
|
|
564
|
+
|
|
565
|
+
### 数据不可变性
|
|
566
|
+
|
|
567
|
+
`item` 属性是只读的,以确保数据完整性:
|
|
568
|
+
- 防止意外修改
|
|
569
|
+
- 通过 `replace()` 方法使状态更改显式化
|
|
570
|
+
- 有助于调试和状态跟踪
|
|
571
|
+
|
|
445
572
|
## 相关组件
|
|
446
573
|
|
|
447
|
-
- `
|
|
574
|
+
- `CommonTreeNodes` - 基础树管理类
|
|
575
|
+
- `TreeNodes` - 扩展的树管理类,具有层次结构功能
|
|
448
576
|
- `TreeView` - 树视图组件
|
|
449
|
-
- `
|
|
577
|
+
- `NodeViewOptions` - 视图配置类
|