diff --git a/site/.vitepress/code/ReloadChildren.vue b/site/.vitepress/code/ReloadChildren.vue
index 47b2908..2a91d8d 100644
--- a/site/.vitepress/code/ReloadChildren.vue
+++ b/site/.vitepress/code/ReloadChildren.vue
@@ -1,8 +1,9 @@
-
+
+
-
+
@@ -47,6 +48,17 @@ const handleSetChildren = () => {
const handleClearChildren = () => {
tree.value.updateNode('node-1', { children: [] })
}
+const handleUpdateChildren = () => {
+ tree.value.updateNode('node-1', {
+ children: children.map((child) => {
+ return {
+ ...child,
+ title: `${child.title} ${Date.now()}`,
+ checked: true,
+ }
+ })
+ })
+}
diff --git a/site/api/vtree.md b/site/api/vtree.md
index 41d6d34..fe1aafa 100644
--- a/site/api/vtree.md
+++ b/site/api/vtree.md
@@ -97,6 +97,8 @@
| filter | 过滤节点 | `keyword: string`: 过滤关键词
`filterMethod: (keyword: string, node: TreeNode) => boolean`: 过滤方法,默认为 filterMethod Prop ,如果没有传 filterMethod Prop 则为搜索 title 字段的一个内置方法 | `void` |
| showCheckedNodes | 展示已选节点 | `showUnloadCheckedNodes: boolean`: 是否显示未加载的选中节点,默认为 Prop 传入的值 | `void` |
| loadRootNodes | 从远程加载根节点 | 无 | `Promise` |
+| updateNode `4.1.0` | 更新单个节点 | `key: string \| number`: 节点 key
`newNode: object`: 新节点数据,某些字段将被忽略,例如以下划线 "_" 开头的字段,以及 key 字段和 `indeterminate`, `visible`, `isLeaf` 等 | `void` |
+| updateNodes `4.1.0` | 更新多个节点 | `newNodes: object[]`: 新节点数据数组,与 `updateNode` 相同,特定的字段会被忽略,且没有 key 字段的元素将被忽略 | `void` |
| scrollTo | 滚动到指定节点位置 | `key: string \| number`: 节点 key
`verticalPosition: 'top' \| 'center' \| 'bottom' \| number`: 滚动的垂直位置 | `void` |
## VTree Slots
diff --git a/site/en/api/vtree.md b/site/en/api/vtree.md
index ef725a0..0eb7e9b 100644
--- a/site/en/api/vtree.md
+++ b/site/en/api/vtree.md
@@ -97,6 +97,8 @@ Note: Since `2.0.8`, the node info returned in events contains the full node inf
| filter | Filter nodes | `keyword: string`: filter keyword
`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` |
| showCheckedNodes | Show checked nodes | `showUnloadCheckedNodes: boolean`: whether to show checked nodes that are not loaded, default to prop value | `void` |
| loadRootNodes | Load root nodes from remote | None | `Promise` |
+| updateNode `4.1.0` | Update single node | `key: string \| number`: node key
`newNode: object`: new node data, some fields will be ignored, like those start with underscore '_', the key field and `indeterminate`, `visible`, `isLeaf`, etc. | `void` |
+| 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` |
| scrollTo | Scroll to specific node position | `key: string \| number`: node key
`verticalPosition: 'top' \| 'center' \| 'bottom' \| number`: vertical position of scrolling | `void` |
## VTree Slots
diff --git a/site/en/examples/node-manipulation.md b/site/en/examples/node-manipulation.md
index 5e4f92e..216d33c 100644
--- a/site/en/examples/node-manipulation.md
+++ b/site/en/examples/node-manipulation.md
@@ -22,3 +22,23 @@ Enable `draggable` and `droppable`
- Invoke `remove` to remove a node
+
+## Update Node Title {#update-node-title}
+
+Invoke `updateNode` method to update some fields of tree node
+
+Invoke `updateNodes` to update multiple nodes
+
+
+
+## Update Custom Field {#update-custom-field}
+
+Invoke `updateNode` method to update custom fields in tree node
+
+
+
+## Reload Child Nodes {#reload-children}
+
+Invoke `updateNode` and pass a new `children` list to reload child nodes
+
+
diff --git a/site/examples/node-manipulation.md b/site/examples/node-manipulation.md
index a07ed86..98947b0 100644
--- a/site/examples/node-manipulation.md
+++ b/site/examples/node-manipulation.md
@@ -31,6 +31,12 @@
+## 更新自定义字段 {#update-custom-field}
+
+调用树组件的 `updateNode` 方法更新自定义字段
+
+
+
## 重新加载子节点 {#reload-children}
调用 `updateNode` 传入新的 `children` 列表可以重新加载子节点
diff --git a/src/store/tree-store.ts b/src/store/tree-store.ts
index 631615e..15d1bd1 100644
--- a/src/store/tree-store.ts
+++ b/src/store/tree-store.ts
@@ -291,18 +291,15 @@ export default class TreeStore extends TreeEventTarget {
} else {
// 设置的节点不是当前已选中节点,要么当前没有选中节点,要么当前有选中节点
if (value) {
- if (this.currentSelectedKey === null) {
- // 当前没有选中节点
- node.selected = value
- this.currentSelectedKey = node[this.options.keyField]
- } else {
+ if (this.currentSelectedKey !== null) {
// 取消当前已选中,设置新的选中节点
if (this.mapData[this.currentSelectedKey]) {
this.mapData[this.currentSelectedKey].selected = false
}
- node.selected = value
- this.currentSelectedKey = node[this.options.keyField]
}
+ node.selected = value
+ this.currentSelectedKey = node[this.options.keyField]
+ this.unloadSelectedKey = null
}
}
@@ -327,9 +324,7 @@ export default class TreeStore extends TreeEventTarget {
triggerDataChange: boolean = true
): void {
if (value) {
- if (this.currentSelectedKey) {
- this.setSelected(this.currentSelectedKey, false, false, false)
- }
+ this.currentSelectedKey = null
this.unloadSelectedKey = key
} else {
if (this.unloadSelectedKey === key) {
@@ -492,8 +487,13 @@ export default class TreeStore extends TreeEventTarget {
}
}
+ private isChildrenChanged(node: TreeNode, newNode: ITreeNodeOptions): boolean {
+ return ('children' in newNode) && (!!node.children.length || !!newNode.children?.length)
+ }
+
updateNode(key: TreeNodeKeyType, newNode: ITreeNodeOptions, triggerEvent = true, triggerDataChange = true) {
- if (!this.mapData[key]) return
+ const node = this.mapData[key]
+ if (!node) return
const newNodeCopy: ITreeNodeOptions = {}
const notAllowedFields = [
@@ -512,14 +512,15 @@ export default class TreeStore extends TreeEventTarget {
const previousCheckedKeys = this.getCheckedKeys()
const previousSelectedKey = this.getSelectedKey()
+ let triggerSetDataFlag = this.isChildrenChanged(node, newNodeCopy)
- if ('children' in newNodeCopy) {
+ if (('children' in newNodeCopy) && (!!node.children.length || !!newNodeCopy.children?.length)) {
// remove all children
this.removeChildren(key, false, false)
// add new children
if (Array.isArray(newNodeCopy.children)) {
- this.loadChildren(this.mapData[key], newNodeCopy.children, this.mapData[key].expand)
+ this.loadChildren(node, newNodeCopy.children, node.expand)
}
delete newNodeCopy.children
@@ -537,7 +538,7 @@ export default class TreeStore extends TreeEventTarget {
delete newNodeCopy.expand
}
Object.keys(newNodeCopy).forEach((field) => {
- this.mapData[key][field] = newNodeCopy[field]
+ node[field] = newNodeCopy[field]
})
const currentCheckedKeys = this.getCheckedKeys()
@@ -554,6 +555,9 @@ export default class TreeStore extends TreeEventTarget {
}
if (triggerDataChange) {
+ if (triggerSetDataFlag) {
+ this.emit('set-data')
+ }
this.emit('visible-data-change')
}
}
@@ -564,9 +568,15 @@ export default class TreeStore extends TreeEventTarget {
const previousCheckedKeys = this.getCheckedKeys()
const previousSelectedKey = this.getSelectedKey()
+ let triggerSetDataFlag = false
- validNodes.forEach((node) => {
- this.updateNode(node[this.options.keyField], node, false, false)
+ validNodes.forEach((newNode) => {
+ const key = newNode[this.options.keyField]
+ const node = this.mapData[key]
+ if (node) {
+ triggerSetDataFlag = triggerSetDataFlag || this.isChildrenChanged(node, newNode)
+ this.updateNode(key, newNode, false, false)
+ }
})
const currentCheckedKeys = this.getCheckedKeys()
@@ -580,6 +590,10 @@ export default class TreeStore extends TreeEventTarget {
this.triggerSelectedChange(true, false)
}
+ if (triggerSetDataFlag) {
+ this.emit('set-data')
+ }
+
this.emit('visible-data-change')
}
@@ -894,6 +908,7 @@ export default class TreeStore extends TreeEventTarget {
if (!node || !node.children.length) return null
const firstChild = node.children[0]
+ let movingNode = firstChild
// 从 flatData 中移除
const index = this.findIndex(node)
@@ -905,6 +920,11 @@ export default class TreeStore extends TreeEventTarget {
// 从 mapData 中移除
delete this.mapData[this.flatData[i][this.options.keyField]]
deleteCount++
+
+ // 如果是 Selected 的节点,则记录
+ if (this.flatData[i].selected) {
+ movingNode = this.flatData[i]
+ }
} else break
}
this.flatData.splice(index + 1, deleteCount)
@@ -915,7 +935,7 @@ export default class TreeStore extends TreeEventTarget {
node.indeterminate = false
// 更新被移除处父节点状态
- this.updateMovingNodeStatus(firstChild, triggerEvent, triggerDataChange)
+ this.updateMovingNodeStatus(movingNode, triggerEvent, triggerDataChange)
if (triggerDataChange) {
this.emit('visible-data-change')
@@ -935,7 +955,7 @@ export default class TreeStore extends TreeEventTarget {
const currentCheckedKeys = this.getCheckedKeys()
const flattenChildren = this.flattenData(
node.children,
- this.getSelectedKey === null
+ this.getSelectedKey() === null
)
this.insertIntoFlatData(parentIndex + 1, flattenChildren)
// 如果有未加载的选中节点,判断其是否已加载
@@ -943,6 +963,8 @@ export default class TreeStore extends TreeEventTarget {
if (this.unloadSelectedKey !== null) {
this.setUnloadSelectedKey(this.unloadSelectedKey)
}
+
+ this.checkNodeUpward(node, true)
}
private getInsertedNode(
@@ -1168,8 +1190,6 @@ export default class TreeStore extends TreeEventTarget {
if (node.checked && this.options.cascade) {
// 向下勾选,包括自身
this.checkNodeDownward(node, true)
- // 向上勾选父节点直到根节点
- this.checkNodeUpward(node)
}
if (node.selected && overrideSelected) {
@@ -1191,6 +1211,12 @@ export default class TreeStore extends TreeEventTarget {
this.flattenData(node.children, overrideSelected, result)
}
}
+
+ if (this.options.cascade && !!length) {
+ // 向上勾选父节点直到根节点
+ this.checkNodeUpward(nodes[0])
+ }
+
return result
}
@@ -1230,9 +1256,10 @@ export default class TreeStore extends TreeEventTarget {
/**
* 向上勾选/取消勾选父节点,不包括自身
* @param node 需要勾选的节点
+ * @param fromCurrentNode 是否从当前节点开始处理
*/
- private checkNodeUpward(node: TreeNode) {
- let parent = node._parent
+ private checkNodeUpward(node: TreeNode, fromCurrentNode = false) {
+ let parent = fromCurrentNode ? node : node._parent
while (parent) {
this.checkParentNode(parent)
parent = parent._parent
diff --git a/tests/unit/tree.spec.ts b/tests/unit/tree.spec.ts
index 7050458..f342c34 100644
--- a/tests/unit/tree.spec.ts
+++ b/tests/unit/tree.spec.ts
@@ -168,7 +168,7 @@ describe('树展示测试', () => {
).toBe(true)
expect(
treeNodes[1].find('.vtree-tree-node__checkbox_indeterminate').exists()
- ).toBe(true)
+ ).toBe(false)
expect(
treeNodes[2].find('.vtree-tree-node__title_selected').exists()
).toBe(true)