Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 77c7a2c

Browse files
feat: optimize loadChildren and update doc
1 parent 8e51d24 commit 77c7a2c

File tree

8 files changed

+160
-25
lines changed

8 files changed

+160
-25
lines changed

‎site/.vitepress/code/ReloadChildren.vue

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<template>
2-
<button @click="handleSetChildren">Set node-1 children</button>
32
<button @click="handleClearChildren">Clear node-1 children</button>
3+
<button @click="handleSetChildren">Set node-1 children</button>
4+
<button @click="handleUpdateChildren">Update node-1 children</button>
45
<div :style="{ height: '300px' }">
5-
<VTree ref="tree" />
6+
<VTree ref="tree" checkableselectable/>
67
</div>
78
</template>
89

@@ -47,6 +48,17 @@ const handleSetChildren = () => {
4748
const handleClearChildren = () => {
4849
tree.value.updateNode('node-1', { children: [] })
4950
}
51+
const handleUpdateChildren = () => {
52+
tree.value.updateNode('node-1', {
53+
children: children.map((child) => {
54+
return {
55+
...child,
56+
title: `${child.title} ${Date.now()}`,
57+
checked: true,
58+
}
59+
})
60+
})
61+
}
5062
</script>
5163

5264
<style scoped>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<template>
2+
<button @click="handleUpdateCount">Update node-1 count</button>
3+
<VTree ref="tree">
4+
<template #node="{ node }">
5+
<span>{{ node.title }}</span>
6+
<span v-if="typeof node.count === 'number'">
7+
Count: {{ node.count }}
8+
</span>
9+
</template>
10+
</VTree>
11+
</template>
12+
13+
<script setup lang="ts">
14+
import { onMounted, ref } from 'vue'
15+
import VTree from '@wsfe/vue-tree'
16+
17+
const tree = ref()
18+
19+
const data = [
20+
{
21+
title: 'node-1',
22+
id: 'node-1',
23+
count: 0,
24+
children: [
25+
{
26+
title: 'node-1-1',
27+
id: 'node-1-1',
28+
},
29+
{
30+
title: 'node-1-2',
31+
id: 'node-1-2',
32+
},
33+
],
34+
},
35+
{
36+
title: 'node-2',
37+
id: 'node-2',
38+
children: [
39+
{
40+
title: 'node-2-1',
41+
id: 'node-2-1',
42+
},
43+
],
44+
},
45+
]
46+
47+
onMounted(() => {
48+
tree.value.setData(data)
49+
})
50+
51+
const handleUpdateCount = () => {
52+
const key = 'node-1'
53+
const currentCount = tree.value.getNode(key).count
54+
tree.value.updateNode(key, { count: currentCount + 1 })
55+
}
56+
</script>
57+
58+
<style scoped>
59+
button {
60+
border: 1px solid lightgray;
61+
border-radius: 8px;
62+
padding-left: 10px;
63+
padding-right: 10px;
64+
margin-right: 20px;
65+
}
66+
</style>

‎site/api/vtree.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@
9797
| filter | 过滤节点 | `keyword: string`: 过滤关键词<br/>`filterMethod: (keyword: string, node: TreeNode) => boolean`: 过滤方法,默认为 filterMethod Prop ,如果没有传 filterMethod Prop 则为搜索 title 字段的一个内置方法 | `void` |
9898
| showCheckedNodes | 展示已选节点 | `showUnloadCheckedNodes: boolean`: 是否显示未加载的选中节点,默认为 Prop 传入的值 | `void` |
9999
| loadRootNodes | 从远程加载根节点 || `Promise<void>` |
100+
| updateNode `4.1.0` | 更新单个节点 | `key: string \| number`: 节点 key<br/>`newNode: object`: 新节点数据,某些字段将被忽略,例如以下划线 "_" 开头的字段,以及 key 字段和 `indeterminate`, `visible`, `isLeaf`| `void` |
101+
| updateNodes `4.1.0` | 更新多个节点 | `newNodes: object[]`: 新节点数据数组,与 `updateNode` 相同,特定的字段会被忽略,且没有 key 字段的元素将被忽略 | `void` |
100102
| scrollTo | 滚动到指定节点位置 | `key: string \| number`: 节点 key<br/>`verticalPosition: 'top' \| 'center' \| 'bottom' \| number`: 滚动的垂直位置 | `void` |
101103

102104
## VTree Slots

‎site/en/api/vtree.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ Note: Since `2.0.8`, the node info returned in events contains the full node inf
9797
| filter | Filter nodes | `keyword: string`: filter keyword<br/>`filterMethod: (keyword: string, node: TreeNode) => boolean`: filter method, default to filterMethod prop. if filterMethod prop is not present, it's an internal method that searches node title | `void` |
9898
| showCheckedNodes | Show checked nodes | `showUnloadCheckedNodes: boolean`: whether to show checked nodes that are not loaded, default to prop value | `void` |
9999
| loadRootNodes | Load root nodes from remote | None | `Promise<void>` |
100+
| updateNode `4.1.0` | Update single node | `key: string \| number`: node key<br/>`newNode: object`: new node data, some fields will be ignored, like those start with underscore '_', the key field and `indeterminate`, `visible`, `isLeaf`, etc. | `void` |
101+
| updateNodes `4.1.0` | Update multiple nodes | `newNodes: object[]`: new nodes array, some specific fields will be ignored like `updateNode`, and the elements without key field also will be ignored | `void` |
100102
| scrollTo | Scroll to specific node position | `key: string \| number`: node key<br/>`verticalPosition: 'top' \| 'center' \| 'bottom' \| number`: vertical position of scrolling | `void` |
101103

102104
## VTree Slots

‎site/en/examples/node-manipulation.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,23 @@ Enable `draggable` and `droppable`
2222
- Invoke `remove` to remove a node
2323

2424
<CodeDemo component="NodeCreationAndRemoval" />
25+
26+
## Update Node Title {#update-node-title}
27+
28+
Invoke `updateNode` method to update some fields of tree node
29+
30+
Invoke `updateNodes` to update multiple nodes
31+
32+
<CodeDemo component="UpdateNodeTitle" />
33+
34+
## Update Custom Field {#update-custom-field}
35+
36+
Invoke `updateNode` method to update custom fields in tree node
37+
38+
<CodeDemo component="UpdateCustomField" />
39+
40+
## Reload Child Nodes {#reload-children}
41+
42+
Invoke `updateNode` and pass a new `children` list to reload child nodes
43+
44+
<CodeDemo component="ReloadChildren" />

‎site/examples/node-manipulation.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131

3232
<CodeDemo component="UpdateNodeTitle" />
3333

34+
## 更新自定义字段 {#update-custom-field}
35+
36+
调用树组件的 `updateNode` 方法更新自定义字段
37+
38+
<CodeDemo component="UpdateCustomField" />
39+
3440
## 重新加载子节点 {#reload-children}
3541

3642
调用 `updateNode` 传入新的 `children` 列表可以重新加载子节点

‎src/store/tree-store.ts

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -291,18 +291,15 @@ export default class TreeStore extends TreeEventTarget {
291291
} else {
292292
// 设置的节点不是当前已选中节点,要么当前没有选中节点,要么当前有选中节点
293293
if (value) {
294-
if (this.currentSelectedKey === null) {
295-
// 当前没有选中节点
296-
node.selected = value
297-
this.currentSelectedKey = node[this.options.keyField]
298-
} else {
294+
if (this.currentSelectedKey !== null) {
299295
// 取消当前已选中,设置新的选中节点
300296
if (this.mapData[this.currentSelectedKey]) {
301297
this.mapData[this.currentSelectedKey].selected = false
302298
}
303-
node.selected = value
304-
this.currentSelectedKey = node[this.options.keyField]
305299
}
300+
node.selected = value
301+
this.currentSelectedKey = node[this.options.keyField]
302+
this.unloadSelectedKey = null
306303
}
307304
}
308305

@@ -327,9 +324,7 @@ export default class TreeStore extends TreeEventTarget {
327324
triggerDataChange: boolean = true
328325
): void {
329326
if (value) {
330-
if (this.currentSelectedKey) {
331-
this.setSelected(this.currentSelectedKey, false, false, false)
332-
}
327+
this.currentSelectedKey = null
333328
this.unloadSelectedKey = key
334329
} else {
335330
if (this.unloadSelectedKey === key) {
@@ -492,8 +487,13 @@ export default class TreeStore extends TreeEventTarget {
492487
}
493488
}
494489

490+
private isChildrenChanged(node: TreeNode, newNode: ITreeNodeOptions): boolean {
491+
return ('children' in newNode) && (!!node.children.length || !!newNode.children?.length)
492+
}
493+
495494
updateNode(key: TreeNodeKeyType, newNode: ITreeNodeOptions, triggerEvent = true, triggerDataChange = true) {
496-
if (!this.mapData[key]) return
495+
const node = this.mapData[key]
496+
if (!node) return
497497

498498
const newNodeCopy: ITreeNodeOptions = {}
499499
const notAllowedFields = [
@@ -512,14 +512,15 @@ export default class TreeStore extends TreeEventTarget {
512512

513513
const previousCheckedKeys = this.getCheckedKeys()
514514
const previousSelectedKey = this.getSelectedKey()
515+
let triggerSetDataFlag = this.isChildrenChanged(node, newNodeCopy)
515516

516-
if ('children' in newNodeCopy) {
517+
if (('children' in newNodeCopy)&&(!!node.children.length||!!newNodeCopy.children?.length)) {
517518
// remove all children
518519
this.removeChildren(key, false, false)
519520

520521
// add new children
521522
if (Array.isArray(newNodeCopy.children)) {
522-
this.loadChildren(this.mapData[key], newNodeCopy.children, this.mapData[key].expand)
523+
this.loadChildren(node, newNodeCopy.children, node.expand)
523524
}
524525

525526
delete newNodeCopy.children
@@ -537,7 +538,7 @@ export default class TreeStore extends TreeEventTarget {
537538
delete newNodeCopy.expand
538539
}
539540
Object.keys(newNodeCopy).forEach((field) => {
540-
this.mapData[key][field] = newNodeCopy[field]
541+
node[field] = newNodeCopy[field]
541542
})
542543

543544
const currentCheckedKeys = this.getCheckedKeys()
@@ -554,6 +555,9 @@ export default class TreeStore extends TreeEventTarget {
554555
}
555556

556557
if (triggerDataChange) {
558+
if (triggerSetDataFlag) {
559+
this.emit('set-data')
560+
}
557561
this.emit('visible-data-change')
558562
}
559563
}
@@ -564,9 +568,15 @@ export default class TreeStore extends TreeEventTarget {
564568

565569
const previousCheckedKeys = this.getCheckedKeys()
566570
const previousSelectedKey = this.getSelectedKey()
571+
let triggerSetDataFlag = false
567572

568-
validNodes.forEach((node) => {
569-
this.updateNode(node[this.options.keyField], node, false, false)
573+
validNodes.forEach((newNode) => {
574+
const key = newNode[this.options.keyField]
575+
const node = this.mapData[key]
576+
if (node) {
577+
triggerSetDataFlag = triggerSetDataFlag || this.isChildrenChanged(node, newNode)
578+
this.updateNode(key, newNode, false, false)
579+
}
570580
})
571581

572582
const currentCheckedKeys = this.getCheckedKeys()
@@ -580,6 +590,10 @@ export default class TreeStore extends TreeEventTarget {
580590
this.triggerSelectedChange(true, false)
581591
}
582592

593+
if (triggerSetDataFlag) {
594+
this.emit('set-data')
595+
}
596+
583597
this.emit('visible-data-change')
584598
}
585599

@@ -894,6 +908,7 @@ export default class TreeStore extends TreeEventTarget {
894908
if (!node || !node.children.length) return null
895909

896910
const firstChild = node.children[0]
911+
let movingNode = firstChild
897912

898913
// 从 flatData 中移除
899914
const index = this.findIndex(node)
@@ -905,6 +920,11 @@ export default class TreeStore extends TreeEventTarget {
905920
// 从 mapData 中移除
906921
delete this.mapData[this.flatData[i][this.options.keyField]]
907922
deleteCount++
923+
924+
// 如果是 Selected 的节点,则记录
925+
if (this.flatData[i].selected) {
926+
movingNode = this.flatData[i]
927+
}
908928
} else break
909929
}
910930
this.flatData.splice(index + 1, deleteCount)
@@ -915,7 +935,7 @@ export default class TreeStore extends TreeEventTarget {
915935
node.indeterminate = false
916936

917937
// 更新被移除处父节点状态
918-
this.updateMovingNodeStatus(firstChild, triggerEvent, triggerDataChange)
938+
this.updateMovingNodeStatus(movingNode, triggerEvent, triggerDataChange)
919939

920940
if (triggerDataChange) {
921941
this.emit('visible-data-change')
@@ -935,14 +955,16 @@ export default class TreeStore extends TreeEventTarget {
935955
const currentCheckedKeys = this.getCheckedKeys()
936956
const flattenChildren = this.flattenData(
937957
node.children,
938-
this.getSelectedKey === null
958+
this.getSelectedKey() === null
939959
)
940960
this.insertIntoFlatData(parentIndex + 1, flattenChildren)
941961
// 如果有未加载的选中节点,判断其是否已加载
942962
this.setUnloadCheckedKeys(currentCheckedKeys)
943963
if (this.unloadSelectedKey !== null) {
944964
this.setUnloadSelectedKey(this.unloadSelectedKey)
945965
}
966+
967+
this.checkNodeUpward(node, true)
946968
}
947969

948970
private getInsertedNode(
@@ -1168,8 +1190,6 @@ export default class TreeStore extends TreeEventTarget {
11681190
if (node.checked && this.options.cascade) {
11691191
// 向下勾选,包括自身
11701192
this.checkNodeDownward(node, true)
1171-
// 向上勾选父节点直到根节点
1172-
this.checkNodeUpward(node)
11731193
}
11741194

11751195
if (node.selected && overrideSelected) {
@@ -1191,6 +1211,12 @@ export default class TreeStore extends TreeEventTarget {
11911211
this.flattenData(node.children, overrideSelected, result)
11921212
}
11931213
}
1214+
1215+
if (this.options.cascade && !!length) {
1216+
// 向上勾选父节点直到根节点
1217+
this.checkNodeUpward(nodes[0])
1218+
}
1219+
11941220
return result
11951221
}
11961222

@@ -1230,9 +1256,10 @@ export default class TreeStore extends TreeEventTarget {
12301256
/**
12311257
* 向上勾选/取消勾选父节点,不包括自身
12321258
* @param node 需要勾选的节点
1259+
* @param fromCurrentNode 是否从当前节点开始处理
12331260
*/
1234-
private checkNodeUpward(node: TreeNode) {
1235-
let parent = node._parent
1261+
private checkNodeUpward(node: TreeNode,fromCurrentNode=false) {
1262+
let parent = fromCurrentNode ? node : node._parent
12361263
while (parent) {
12371264
this.checkParentNode(parent)
12381265
parent = parent._parent

‎tests/unit/tree.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ describe('树展示测试', () => {
168168
).toBe(true)
169169
expect(
170170
treeNodes[1].find('.vtree-tree-node__checkbox_indeterminate').exists()
171-
).toBe(true)
171+
).toBe(false)
172172
expect(
173173
treeNodes[2].find('.vtree-tree-node__title_selected').exists()
174174
).toBe(true)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /