@ticatec/uniface-element 0.3.14 → 0.3.16
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/data-table/parts/FixedColumnsPanel.svelte +0 -1
- package/dist/input-options-select/InputOptionsSelect.svelte +0 -3
- package/dist/lib/TreeNode_README.md +449 -0
- package/dist/lib/TreeNode_README_CN.md +449 -0
- package/dist/lib/TreeNodes.d.ts +88 -21
- package/dist/lib/TreeNodes.js +164 -77
- package/dist/list-box/ListBox.svelte +0 -1
- package/dist/list-box/index.d.ts +2 -2
- package/dist/list-box/types.d.ts +11 -1
- package/dist/message-box/MessageBoxBoard.svelte +0 -1
- package/dist/search-box/SearchBox.svelte +0 -1
- package/dist/tabs/Tabs.svelte +0 -1
- package/dist/tree-view/README.md +12 -11
- package/dist/tree-view/README_CN.md +141 -1
- package/dist/tree-view/TreeNodeView.svelte +16 -5
- package/dist/tree-view/TreeView.svelte +24 -1
- package/dist/tree-view/TreeView.svelte.d.ts +1 -0
- package/dist/tree-view/Types.d.ts +8 -4
- package/dist/tree-view/index.d.ts +2 -2
- package/package.json +1 -1
package/dist/lib/TreeNodes.js
CHANGED
|
@@ -11,6 +11,7 @@ export class CommonTreeNodes {
|
|
|
11
11
|
compareFun;
|
|
12
12
|
expendDepth;
|
|
13
13
|
_nodes = [];
|
|
14
|
+
_version = 0;
|
|
14
15
|
constructor(options) {
|
|
15
16
|
this.checkIsRoot = options.checkIsRoot;
|
|
16
17
|
this.compareFun = options.compareFun;
|
|
@@ -21,15 +22,150 @@ export class CommonTreeNodes {
|
|
|
21
22
|
this.expendDepth = options.expendDepth ?? 1;
|
|
22
23
|
this.checkIsDirectory = options.checkIsDirectory;
|
|
23
24
|
}
|
|
25
|
+
touch() {
|
|
26
|
+
this._version++;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 创建带有方法的节点
|
|
30
|
+
*/
|
|
31
|
+
createNodeWithMethods(item) {
|
|
32
|
+
const node = {
|
|
33
|
+
item,
|
|
34
|
+
expand: false,
|
|
35
|
+
append: (childItem) => this.addNodeToNode(node, childItem),
|
|
36
|
+
remove: () => this.removeNode(node),
|
|
37
|
+
replace: (newItem) => this.replaceNodeItem(node, newItem),
|
|
38
|
+
moveTo: (newParentId) => this.moveNodeTo(node, newParentId),
|
|
39
|
+
removeChild: (childId) => this.removeChildNode(node, childId),
|
|
40
|
+
removeChildren: () => this.removeChildrenNodes(node)
|
|
41
|
+
};
|
|
42
|
+
return node;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 添加子节点到指定节点
|
|
46
|
+
*/
|
|
47
|
+
addNodeToNode(parentNode, childItem) {
|
|
48
|
+
// 如果父节点还没有 children(可能是 lazy 节点)
|
|
49
|
+
// 自动触发 lazyLoader 加载或初始化 children 数组
|
|
50
|
+
if (parentNode.children == null) {
|
|
51
|
+
parentNode.children = [];
|
|
52
|
+
}
|
|
53
|
+
const childNode = this.createNodeWithMethods(childItem);
|
|
54
|
+
parentNode.children = [...parentNode.children, childNode];
|
|
55
|
+
if (this.compareFun) {
|
|
56
|
+
parentNode.children = parentNode.children.sort((n1, n2) => {
|
|
57
|
+
return this.compareFun(n1.item, n2.item);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
parentNode.expand = true;
|
|
61
|
+
this.nodeMap.set(childItem[this.keyField], childNode);
|
|
62
|
+
this.touch();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 删除指定节点
|
|
66
|
+
*/
|
|
67
|
+
removeNode(node) {
|
|
68
|
+
const idKey = node.item[this.keyField];
|
|
69
|
+
const parentKey = node.item[this.parentKeyField];
|
|
70
|
+
const parentNode = this.nodeMap.get(parentKey);
|
|
71
|
+
this.nodeMap.delete(idKey);
|
|
72
|
+
if (parentNode) {
|
|
73
|
+
if (parentNode.children) {
|
|
74
|
+
parentNode.children = parentNode.children.filter(el => el.item[this.keyField] != idKey);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// 是根节点
|
|
79
|
+
this._nodes = this._nodes.filter(el => el.item[this.keyField] != idKey);
|
|
80
|
+
}
|
|
81
|
+
this.touch();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 替换节点的数据
|
|
85
|
+
*/
|
|
86
|
+
replaceNodeItem(node, newItem) {
|
|
87
|
+
node.item = newItem;
|
|
88
|
+
// 如果排序字段改变了,需要重新排序
|
|
89
|
+
const parentKey = node.item[this.parentKeyField];
|
|
90
|
+
const parentNode = this.nodeMap.get(parentKey);
|
|
91
|
+
if (parentNode && parentNode.children && parentNode.children.length > 0) {
|
|
92
|
+
parentNode.children = parentNode.children.sort((n1, n2) => {
|
|
93
|
+
return this.compareFun?.(n1.item, n2.item);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this._nodes.sort((n1, n2) => {
|
|
98
|
+
return this.compareFun?.(n1.item, n2.item);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
this.touch();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 将节点移动到新的父节点下
|
|
105
|
+
*/
|
|
106
|
+
moveNodeTo(node, newParentId) {
|
|
107
|
+
const newParent = this.nodeMap.get(newParentId);
|
|
108
|
+
const currentParentKey = node.item[this.parentKeyField];
|
|
109
|
+
const currentParent = this.nodeMap.get(currentParentKey);
|
|
110
|
+
if (!newParent || !currentParent) {
|
|
111
|
+
console.warn('Cannot move node: parent not found');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// 从当前父节点移除
|
|
115
|
+
if (currentParent.children) {
|
|
116
|
+
currentParent.children = currentParent.children.filter(el => el.item[this.keyField] != node.item[this.keyField]);
|
|
117
|
+
}
|
|
118
|
+
// 如果新父节点还没有 children,初始化数组
|
|
119
|
+
if (newParent.children == null) {
|
|
120
|
+
newParent.children = [];
|
|
121
|
+
}
|
|
122
|
+
// 添加到新父节点
|
|
123
|
+
newParent.children = [...newParent.children, node];
|
|
124
|
+
if (this.compareFun) {
|
|
125
|
+
newParent.children = newParent.children.sort((n1, n2) => {
|
|
126
|
+
return this.compareFun(n1.item, n2.item);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// 更新节点的 parentKeyField
|
|
130
|
+
node.item[this.parentKeyField] = newParentId;
|
|
131
|
+
this.touch();
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 删除指定节点的某个子节点
|
|
135
|
+
*/
|
|
136
|
+
removeChildNode(parentNode, childId) {
|
|
137
|
+
if (!parentNode.children)
|
|
138
|
+
return;
|
|
139
|
+
const childNode = parentNode.children.find(child => child.item[this.keyField] == childId);
|
|
140
|
+
if (!childNode)
|
|
141
|
+
return;
|
|
142
|
+
// 从 children 数组中移除
|
|
143
|
+
parentNode.children = parentNode.children.filter(child => child.item[this.keyField] != childId);
|
|
144
|
+
// 从 nodeMap 中删除
|
|
145
|
+
this.nodeMap.delete(childId);
|
|
146
|
+
this.touch();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 删除指定节点的所有子节点
|
|
150
|
+
*/
|
|
151
|
+
removeChildrenNodes(parentNode) {
|
|
152
|
+
if (!parentNode.children)
|
|
153
|
+
return;
|
|
154
|
+
// 从 nodeMap 中删除所有子节点
|
|
155
|
+
for (const child of parentNode.children) {
|
|
156
|
+
const childId = child.item[this.keyField];
|
|
157
|
+
this.nodeMap.delete(childId);
|
|
158
|
+
}
|
|
159
|
+
// 清空 children 数组
|
|
160
|
+
parentNode.children = [];
|
|
161
|
+
this.touch();
|
|
162
|
+
}
|
|
24
163
|
setData(list) {
|
|
25
164
|
this.nodeMap = new Map();
|
|
26
165
|
this._nodes = [];
|
|
27
166
|
// 初始化每个节点
|
|
28
167
|
for (const item of list) {
|
|
29
|
-
const node =
|
|
30
|
-
item,
|
|
31
|
-
expand: false
|
|
32
|
-
};
|
|
168
|
+
const node = this.createNodeWithMethods(item);
|
|
33
169
|
const key = item[this.keyField];
|
|
34
170
|
this.nodeMap.set(key, node);
|
|
35
171
|
}
|
|
@@ -42,13 +178,22 @@ export class CommonTreeNodes {
|
|
|
42
178
|
if (this.expendDepth > 1) {
|
|
43
179
|
this.setExpandForDepth(this.nodes, 0);
|
|
44
180
|
}
|
|
181
|
+
this.touch();
|
|
45
182
|
}
|
|
46
183
|
/**
|
|
47
184
|
* 获取节点
|
|
48
185
|
*/
|
|
49
186
|
get nodes() {
|
|
187
|
+
// 通过访问 _version 让 Svelte 检测到变化
|
|
188
|
+
void this._version;
|
|
50
189
|
return this._nodes;
|
|
51
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* 获取版本号,用于触发 Svelte 响应式更新
|
|
193
|
+
*/
|
|
194
|
+
get version() {
|
|
195
|
+
return this._version;
|
|
196
|
+
}
|
|
52
197
|
/**
|
|
53
198
|
* 设置展开层级
|
|
54
199
|
* @param nodes
|
|
@@ -72,9 +217,11 @@ export class CommonTreeNodes {
|
|
|
72
217
|
appendNode(node, doSort = false) {
|
|
73
218
|
let item = node.item;
|
|
74
219
|
const parentKey = item[this.parentKeyField];
|
|
75
|
-
if (this.checkIsRoot(item)) {
|
|
220
|
+
if (this.checkIsRoot(item)) {
|
|
76
221
|
this._nodes.push(node);
|
|
77
|
-
this._nodes.sort(
|
|
222
|
+
this._nodes.sort((n1, n2) => {
|
|
223
|
+
return this.compareFun?.(n1.item, n2.item);
|
|
224
|
+
});
|
|
78
225
|
}
|
|
79
226
|
else {
|
|
80
227
|
const parentNode = this.nodeMap.get(parentKey);
|
|
@@ -82,7 +229,9 @@ export class CommonTreeNodes {
|
|
|
82
229
|
parentNode.expand = true;
|
|
83
230
|
parentNode.children = [...(parentNode.children ?? []), node];
|
|
84
231
|
if (doSort && this.compareFun) {
|
|
85
|
-
parentNode.children = parentNode.children.sort(
|
|
232
|
+
parentNode.children = parentNode.children.sort((n1, n2) => {
|
|
233
|
+
return this.compareFun(n1.item, n2.item);
|
|
234
|
+
});
|
|
86
235
|
}
|
|
87
236
|
}
|
|
88
237
|
else {
|
|
@@ -99,79 +248,17 @@ export default class TreeNodes extends CommonTreeNodes {
|
|
|
99
248
|
return this.collectExpandedNodes(this._nodes);
|
|
100
249
|
}
|
|
101
250
|
/**
|
|
102
|
-
*
|
|
103
|
-
* @param
|
|
104
|
-
*/
|
|
105
|
-
/**
|
|
106
|
-
* 增加一个新节点
|
|
107
|
-
* @param item
|
|
251
|
+
* 增加一个新节点(便捷方法,会自动根据 parentKeyField 找到父节点)
|
|
252
|
+
* @param item 要添加的节点数据
|
|
108
253
|
*/
|
|
109
254
|
append(item) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.nodeMap.set(item[this.keyField], node);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* 替换一个节点的数据
|
|
119
|
-
* @param item
|
|
120
|
-
*/
|
|
121
|
-
replace(item) {
|
|
122
|
-
let node = this.nodeMap.get(item[this.keyField]);
|
|
123
|
-
if (node) {
|
|
124
|
-
node.item = item;
|
|
125
|
-
let parent = this.nodeMap.get(item[this.parentKeyField]);
|
|
126
|
-
if (parent && parent.children && parent.children.length > 0) {
|
|
127
|
-
parent.children = parent.children.sort(this.compareFun);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
this._nodes.sort(this.compareFun);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
remove(item) {
|
|
135
|
-
let idKey = item[this.keyField];
|
|
136
|
-
let node = this.nodeMap.get(idKey);
|
|
137
|
-
if (node) {
|
|
138
|
-
let parent = this.nodeMap.get(node.item[this.parentKeyField]);
|
|
139
|
-
this.nodeMap.delete(idKey);
|
|
140
|
-
if (parent) {
|
|
141
|
-
if (parent.children) {
|
|
142
|
-
let pos = parent.children.findIndex(el => el.item[this.keyField] == idKey);
|
|
143
|
-
if (pos > -1) {
|
|
144
|
-
parent.children.splice(pos, 1);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
let pos = this._nodes.findIndex(el => el.item[this.keyField] == idKey);
|
|
150
|
-
if (pos > -1) {
|
|
151
|
-
this._nodes.splice(pos, 1);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* 将一个节点移动到另外一个节点下
|
|
158
|
-
* @param item
|
|
159
|
-
* @param parentId
|
|
160
|
-
*/
|
|
161
|
-
moveTo(item, parentId) {
|
|
162
|
-
let newParent = this.nodeMap.get(parentId);
|
|
163
|
-
let node = this.nodeMap.get(item[this.keyField]);
|
|
164
|
-
let currentParent = this.nodeMap.get(item[this.parentKeyField]);
|
|
165
|
-
if (newParent && node && currentParent && currentParent.children) {
|
|
166
|
-
let pos = currentParent.children.findIndex(el => el.item[this.keyField] == item[this.keyField]);
|
|
167
|
-
if (pos > -1) {
|
|
168
|
-
currentParent.children.splice(pos, 1);
|
|
169
|
-
}
|
|
170
|
-
newParent.children = [...(newParent.children ?? []), node];
|
|
171
|
-
if (this.compareFun) {
|
|
172
|
-
newParent.children = newParent.children.sort(this.compareFun);
|
|
173
|
-
}
|
|
255
|
+
const parentKey = item[this.parentKeyField];
|
|
256
|
+
const parentNode = this.nodeMap.get(parentKey);
|
|
257
|
+
if (!parentNode) {
|
|
258
|
+
console.warn(`Parent node ${parentKey} not found. Cannot add node.`);
|
|
259
|
+
return;
|
|
174
260
|
}
|
|
261
|
+
parentNode.append(item);
|
|
175
262
|
}
|
|
176
263
|
/**
|
|
177
264
|
* 获取除指定节点外的其他节点
|
package/dist/list-box/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import ListBox from "./ListBox.svelte";
|
|
2
|
-
import type { LazyLoader, LoadResult, FunFilter } from "./types";
|
|
2
|
+
import type { ListLazyLoader, LazyLoader, LoadResult, FunFilter } from "./types";
|
|
3
3
|
export default ListBox;
|
|
4
|
-
export type { LazyLoader, FunFilter, LoadResult };
|
|
4
|
+
export type { ListLazyLoader, LazyLoader, FunFilter, LoadResult };
|
package/dist/list-box/types.d.ts
CHANGED
|
@@ -3,4 +3,14 @@ export interface LoadResult {
|
|
|
3
3
|
list: Array<any>;
|
|
4
4
|
}
|
|
5
5
|
export type FunFilter = (item: any, text: string) => boolean;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* 分页加载列表数据
|
|
8
|
+
* @param text 搜索文本
|
|
9
|
+
* @param pageNo 页码(从0开始)
|
|
10
|
+
* @returns 加载结果,包含数据列表和是否有更多数据的标志
|
|
11
|
+
*/
|
|
12
|
+
export type ListLazyLoader = (text: string, pageNo: number) => Promise<LoadResult>;
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated 使用 ListLazyLoader 代替。LazyLoader 已弃用,将在未来版本中移除。
|
|
15
|
+
*/
|
|
16
|
+
export type LazyLoader = ListLazyLoader;
|
package/dist/tabs/Tabs.svelte
CHANGED
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
|
|
44
44
|
const checkOverflow = () => {
|
|
45
45
|
if (container) {
|
|
46
|
-
console.log(currentLeft, container.clientWidth, container.scrollWidth);
|
|
47
46
|
if (currentLeft + container.clientWidth >= container.scrollWidth) {
|
|
48
47
|
currentLeft = container.scrollWidth - container.clientWidth;
|
|
49
48
|
scrollX.set(currentLeft);
|
package/dist/tree-view/README.md
CHANGED
|
@@ -75,6 +75,7 @@ import TreeNodes, { type TreeNode } from "@ticatec/uniface-element/TreeNodes";
|
|
|
75
75
|
| `onblur` | `((event: FocusEvent) => void) \| null` | `null` | Callback when tree loses focus |
|
|
76
76
|
| `onContextMenu` | `OnContextMenu` | `null` | Right-click context menu handler |
|
|
77
77
|
| `checkIsDirectory` | `CheckIsDirectory<any>` | Required | Function to check if node is a directory |
|
|
78
|
+
| `version` | `number \| undefined` | `undefined` | Version number to trigger reactive updates (from TreeNodes object) |
|
|
78
79
|
| `class` | `string` | `""` | CSS class name |
|
|
79
80
|
|
|
80
81
|
## Type Definitions
|
|
@@ -119,14 +120,14 @@ type NodeVisibleFun = (node: TreeNode<any>) => boolean;
|
|
|
119
120
|
let activeNode = null;
|
|
120
121
|
|
|
121
122
|
const fileSystemData = [
|
|
122
|
-
{ id: 1, name: "=� Documents", type: "folder", parent: null },
|
|
123
|
-
{ id: 2, name: "=� Pictures", type: "folder", parent: null },
|
|
124
|
-
{ id: 3, name: "=� Work", type: "folder", parent: 1 },
|
|
125
|
-
{ id: 4, name: "=� Personal", type: "folder", parent: 1 },
|
|
126
|
-
{ id: 5, name: "=� resume.pdf", type: "file", parent: 3 },
|
|
127
|
-
{ id: 6, name: "=� cover-letter.doc", type: "file", parent: 3 },
|
|
128
|
-
{ id: 7, name: "=� vacation.jpg", type: "file", parent: 2 },
|
|
129
|
-
{ id: 8, name: "=� family.png", type: "file", parent: 2 }
|
|
123
|
+
{ id: 1, name: "=� Documents", type: "folder", parent: null },
|
|
124
|
+
{ id: 2, name: "=� Pictures", type: "folder", parent: null },
|
|
125
|
+
{ id: 3, name: "=� Work", type: "folder", parent: 1 },
|
|
126
|
+
{ id: 4, name: "=� Personal", type: "folder", parent: 1 },
|
|
127
|
+
{ id: 5, name: "=� resume.pdf", type: "file", parent: 3 },
|
|
128
|
+
{ id: 6, name: "=� cover-letter.doc", type: "file", parent: 3 },
|
|
129
|
+
{ id: 7, name: "=� vacation.jpg", type: "file", parent: 2 },
|
|
130
|
+
{ id: 8, name: "=� family.png", type: "file", parent: 2 }
|
|
130
131
|
];
|
|
131
132
|
|
|
132
133
|
const fileTree = new TreeNodes({
|
|
@@ -645,10 +646,10 @@ type NodeVisibleFun = (node: TreeNode<any>) => boolean;
|
|
|
645
646
|
switch (type) {
|
|
646
647
|
case 'country': return '<
|
|
647
648
|
case 'province':
|
|
648
|
-
case 'state': return '<�';
|
|
649
|
+
case 'state': return '<�';
|
|
649
650
|
case 'district':
|
|
650
|
-
case 'city': return '<�';
|
|
651
|
-
default: return '=�';
|
|
651
|
+
case 'city': return '<�';
|
|
652
|
+
default: return '=�';
|
|
652
653
|
}
|
|
653
654
|
}
|
|
654
655
|
|
|
@@ -51,8 +51,9 @@ import TreeNodes, { type TreeNode } from "@ticatec/uniface-element/TreeNodes";
|
|
|
51
51
|
}
|
|
52
52
|
</script>
|
|
53
53
|
|
|
54
|
-
<TreeView
|
|
54
|
+
<TreeView
|
|
55
55
|
nodes={treeNodes.nodes}
|
|
56
|
+
version={treeNodes.version}
|
|
56
57
|
textField="name"
|
|
57
58
|
bind:activeNode
|
|
58
59
|
onchange={handleSelectionChange}
|
|
@@ -75,6 +76,7 @@ import TreeNodes, { type TreeNode } from "@ticatec/uniface-element/TreeNodes";
|
|
|
75
76
|
| `onblur` | `((event: FocusEvent) => void) \| null` | `null` | 树失去焦点时的回调函数 |
|
|
76
77
|
| `onContextMenu` | `OnContextMenu` | `null` | 右键上下文菜单处理程序 |
|
|
77
78
|
| `checkIsDirectory` | `CheckIsDirectory<any>` | 必需 | 判断节点是否为目录的函数 |
|
|
79
|
+
| `version` | `number \| undefined` | `undefined` | 用于触发响应式更新的版本号(从 TreeNodes 对象传入) |
|
|
78
80
|
| `class` | `string` | `""` | CSS 类名 |
|
|
79
81
|
|
|
80
82
|
## 类型定义
|
|
@@ -727,8 +729,146 @@ const treeNodes = new TreeNodes({
|
|
|
727
729
|
});
|
|
728
730
|
|
|
729
731
|
treeNodes.setData(flatData); // 将扁平数据转换为树结构
|
|
732
|
+
|
|
733
|
+
// 动态添加节点
|
|
734
|
+
treeNodes.append(newItem);
|
|
735
|
+
|
|
736
|
+
// 移除节点
|
|
737
|
+
treeNodes.removeItem(oldItem);
|
|
738
|
+
|
|
739
|
+
// 移动节点
|
|
740
|
+
treeNodes.moveTo(itemToMove, newParentId);
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
## 动态更新数据
|
|
744
|
+
|
|
745
|
+
TreeView 的每个节点都提供了便捷的方法来操作树结构:
|
|
746
|
+
|
|
747
|
+
### 节点方法
|
|
748
|
+
|
|
749
|
+
所有从 TreeNodes 获取的节点都包含以下方法:
|
|
750
|
+
|
|
751
|
+
- `node.append(childItem)` - 添加子节点到当前节点
|
|
752
|
+
- `node.remove()` - 删除当前节点(从父节点中移除)
|
|
753
|
+
- `node.replace(newItem)` - 替换当前节点的数据
|
|
754
|
+
- `node.moveTo(newParentId)` - 将当前节点移动到另一个父节点下
|
|
755
|
+
|
|
756
|
+
### 使用示例
|
|
757
|
+
|
|
758
|
+
```svelte
|
|
759
|
+
<script>
|
|
760
|
+
import TreeView from "@ticatec/uniface-element/TreeView";
|
|
761
|
+
import TreeNodes from "@ticatec/uniface-element/TreeNodes";
|
|
762
|
+
|
|
763
|
+
let activeNode = null;
|
|
764
|
+
let zones;
|
|
765
|
+
|
|
766
|
+
const treeNodes = new TreeNodes({
|
|
767
|
+
keyField: 'id',
|
|
768
|
+
textField: 'name',
|
|
769
|
+
parentKeyField: 'parent',
|
|
770
|
+
checkIsRoot: (item) => item.parent === null,
|
|
771
|
+
checkIsDirectory: (node) => node.children != null
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
treeNodes.setData(initialData);
|
|
775
|
+
zones = treeNodes;
|
|
776
|
+
|
|
777
|
+
// 添加子节点到选中的节点
|
|
778
|
+
function addNode() {
|
|
779
|
+
if (!activeNode) return;
|
|
780
|
+
|
|
781
|
+
const newItem = {
|
|
782
|
+
id: Date.now(),
|
|
783
|
+
name: "新节点",
|
|
784
|
+
parent: activeNode.item.id
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// 方式1:直接在节点上调用 append 方法
|
|
788
|
+
activeNode.append(newItem);
|
|
789
|
+
|
|
790
|
+
// 方式2:使用 TreeNodes 的 append 方法(会自动找到父节点)
|
|
791
|
+
// treeNodes.append(newItem);
|
|
792
|
+
|
|
793
|
+
// version 会自动更新,TreeView 会重新渲染
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// 删除选中的节点
|
|
797
|
+
function deleteNode() {
|
|
798
|
+
if (activeNode) {
|
|
799
|
+
// 直接在节点上调用 remove 方法
|
|
800
|
+
activeNode.remove();
|
|
801
|
+
activeNode = null;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// 替换节点的数据
|
|
806
|
+
function renameNode() {
|
|
807
|
+
if (activeNode) {
|
|
808
|
+
const newItem = {
|
|
809
|
+
...activeNode.item,
|
|
810
|
+
name: "重命名后的节点"
|
|
811
|
+
};
|
|
812
|
+
activeNode.replace(newItem);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// 移动节点到另一个父节点
|
|
817
|
+
function moveNode() {
|
|
818
|
+
if (activeNode) {
|
|
819
|
+
const newParentId = prompt("输入新的父节点 ID:");
|
|
820
|
+
if (newParentId) {
|
|
821
|
+
activeNode.moveTo(newParentId);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
</script>
|
|
826
|
+
|
|
827
|
+
<div>
|
|
828
|
+
<button on:click={addNode} disabled={!activeNode}>添加子节点</button>
|
|
829
|
+
<button on:click={deleteNode} disabled={!activeNode}>删除节点</button>
|
|
830
|
+
<button on:click={renameNode} disabled={!activeNode}>重命名</button>
|
|
831
|
+
<button on:click={moveNode} disabled={!activeNode}>移动节点</button>
|
|
832
|
+
|
|
833
|
+
<TreeView
|
|
834
|
+
nodes={treeNodes.nodes}
|
|
835
|
+
version={treeNodes.version}
|
|
836
|
+
textField="name"
|
|
837
|
+
bind:activeNode
|
|
838
|
+
checkIsDirectory={node => node.children != null}
|
|
839
|
+
/>
|
|
840
|
+
</div>
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### TreeNodes 类方法
|
|
844
|
+
|
|
845
|
+
如果你只有一个数据项,可以使用 `TreeNodes` 类的方法:
|
|
846
|
+
|
|
847
|
+
```typescript
|
|
848
|
+
// 添加新节点(会自动根据 parentKeyField 找到父节点)
|
|
849
|
+
treeNodes.append(newItem);
|
|
850
|
+
|
|
851
|
+
// 注意:删除、替换、移动操作建议直接在节点上调用方法
|
|
852
|
+
// 因为这些操作通常针对特定的节点实例
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
### Lazy 节点注意事项
|
|
856
|
+
|
|
857
|
+
对于使用 lazyLoader 的节点(尚未展开加载的节点),直接添加子节点会给出警告:
|
|
858
|
+
|
|
859
|
+
```javascript
|
|
860
|
+
// 如果父节点是 lazy 节点且尚未加载
|
|
861
|
+
if (parentNode.children == null) {
|
|
862
|
+
console.warn('Cannot add child: parent is a lazy-loaded node');
|
|
863
|
+
// 节点会被添加到 nodeMap,但不会添加到 parent.children
|
|
864
|
+
// 这样不会干扰后续的 lazyLoader 行为
|
|
865
|
+
}
|
|
730
866
|
```
|
|
731
867
|
|
|
868
|
+
建议在使用前先展开节点,触发 lazyLoader 加载子节点。
|
|
869
|
+
|
|
870
|
+
**重要提示**:必须将 `treeNodes.version` 传递给 `TreeView` 的 `version` 属性,以确保在数据变化时组件能够正确重新渲染。
|
|
871
|
+
|
|
732
872
|
## 可访问性
|
|
733
873
|
|
|
734
874
|
- 支持使用箭头键和回车键进行键盘导航
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
let loading: boolean = false;
|
|
20
20
|
|
|
21
21
|
const isDirectory = () => {
|
|
22
|
-
console.log('检测目录函数', checkIsDirectory);
|
|
23
22
|
return lazyLoader == null ? checkIsDirectory?.(node) : lazyLoader.isBranch(node);
|
|
24
23
|
}
|
|
25
24
|
|
|
@@ -27,7 +26,12 @@
|
|
|
27
26
|
if (node.children == null && lazyLoader != null) {
|
|
28
27
|
try {
|
|
29
28
|
loading = true;
|
|
30
|
-
|
|
29
|
+
const loadedChildren = await lazyLoader.load(node);
|
|
30
|
+
// 初始化新加载的子节点,确保它们的 expand 属性为 false
|
|
31
|
+
node.children = loadedChildren.map(child => ({
|
|
32
|
+
...child,
|
|
33
|
+
expand: child.expand ?? false
|
|
34
|
+
}));
|
|
31
35
|
} finally {
|
|
32
36
|
loading = false;
|
|
33
37
|
}
|
|
@@ -40,11 +44,18 @@
|
|
|
40
44
|
if (respond) {
|
|
41
45
|
respond = false;
|
|
42
46
|
if (isDirectory()) {
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
// 如果是懒加载模式且没有子节点,先加载子节点
|
|
48
|
+
if (lazyLoader != null && !node.children) {
|
|
45
49
|
await loadChildren();
|
|
50
|
+
// 加载完成后,如果有子节点则展开
|
|
51
|
+
const children = node.children as TreeNode<any>[] | undefined;
|
|
52
|
+
if (children && children.length > 0) {
|
|
53
|
+
node.expand = true;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
// 非懒加载模式或已有子节点,直接切换展开状态
|
|
57
|
+
node.expand = !node.expand;
|
|
46
58
|
}
|
|
47
|
-
node.expand = !node.expand;
|
|
48
59
|
}
|
|
49
60
|
await utils.sleep(0.2);
|
|
50
61
|
respond = true;
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
export let onblur: ((event: FocusEvent) => void) | null = null;
|
|
20
20
|
export let onContextMenu: OnContextMenu = null as unknown as OnContextMenu;
|
|
21
21
|
export let checkIsDirectory: CheckIsDirectory<any>;
|
|
22
|
+
export let version: number | undefined = undefined;
|
|
22
23
|
let className: string = "";
|
|
23
24
|
|
|
24
25
|
const onNodeSelectionChange = async (node: TreeNode<any>): Promise<boolean> => {
|
|
@@ -30,10 +31,32 @@
|
|
|
30
31
|
return accept;
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
// 通过 version 触发响应式更新:当 version 变化时,重新读取 nodes
|
|
35
|
+
// 这样可以确保在节点数据变化时,TreeView 能够正确重新渲染
|
|
36
|
+
$: reactiveNodes = version !== undefined ? nodes : nodes;
|
|
37
|
+
|
|
38
|
+
// 当 activeNode 变化时,如果是 lazy 节点且未加载,自动触发加载
|
|
39
|
+
$: if (activeNode && lazyLoader && activeNode.children == null) {
|
|
40
|
+
const loadLazyNode = async () => {
|
|
41
|
+
if (activeNode.children == null && lazyLoader != null) {
|
|
42
|
+
try {
|
|
43
|
+
const loadedChildren = await lazyLoader.load(activeNode);
|
|
44
|
+
activeNode.children = loadedChildren.map(child => ({
|
|
45
|
+
...child,
|
|
46
|
+
expand: child.expand ?? false
|
|
47
|
+
}));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Failed to load lazy node:', error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
loadLazyNode();
|
|
54
|
+
}
|
|
55
|
+
|
|
33
56
|
</script>
|
|
34
57
|
<div class="uniface-tree-view {className}" {style} on:focus={onfocus} on:blur={onblur} tabindex="0">
|
|
35
58
|
<div>
|
|
36
|
-
{#each
|
|
59
|
+
{#each reactiveNodes as node}
|
|
37
60
|
<TreeNodeView {activeNode} {node} {textField} {lazyLoader} {onNodeSelectionChange} {isVisible} {checkIsDirectory} {onContextMenu}/>
|
|
38
61
|
{/each}
|
|
39
62
|
</div>
|
|
@@ -27,6 +27,7 @@ declare const TreeView: $$__sveltets_2_IsomorphicComponent<{
|
|
|
27
27
|
onblur?: ((event: FocusEvent) => void) | null;
|
|
28
28
|
onContextMenu?: OnContextMenu;
|
|
29
29
|
checkIsDirectory: CheckIsDirectory<any>;
|
|
30
|
+
version?: number | undefined;
|
|
30
31
|
}, {
|
|
31
32
|
[evt: string]: CustomEvent<any>;
|
|
32
33
|
}, {}, {
|
|
@@ -9,15 +9,19 @@ export type NodeVisibleFun = (node: TreeNode<any>) => boolean;
|
|
|
9
9
|
*/
|
|
10
10
|
export type LoadChildrenFun = (item: TreeNode<any>) => Promise<Array<TreeNode<any>>>;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* 按需加载树节点的子节点
|
|
13
13
|
*/
|
|
14
|
-
export interface
|
|
14
|
+
export interface TreeLazyLoader {
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* 检查是否是枝节点(可能包含子节点)
|
|
17
17
|
*/
|
|
18
18
|
isBranch: CheckIsDirectory<any>;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* 加载子节点数据
|
|
21
21
|
*/
|
|
22
22
|
load: LoadChildrenFun;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated 使用 TreeLazyLoader 代替。LazyLoader 已弃用,将在未来版本中移除。
|
|
26
|
+
*/
|
|
27
|
+
export type LazyLoader = TreeLazyLoader;
|