diff --git a/site/.vitepress/code/ReloadChildren.vue b/site/.vitepress/code/ReloadChildren.vue
new file mode 100644
index 0000000..2a91d8d
--- /dev/null
+++ b/site/.vitepress/code/ReloadChildren.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/.vitepress/code/UpdateCustomField.vue b/site/.vitepress/code/UpdateCustomField.vue
new file mode 100644
index 0000000..34a7a3d
--- /dev/null
+++ b/site/.vitepress/code/UpdateCustomField.vue
@@ -0,0 +1,66 @@
+
+
+
+
+ {{ node.title }}
+
+ Count: {{ node.count }}
+
+
+
+
+
+
+
+
diff --git a/site/.vitepress/code/UpdateNodeTitle.vue b/site/.vitepress/code/UpdateNodeTitle.vue
new file mode 100644
index 0000000..9eb4e82
--- /dev/null
+++ b/site/.vitepress/code/UpdateNodeTitle.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
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 36a1e41..98947b0 100644
--- a/site/examples/node-manipulation.md
+++ b/site/examples/node-manipulation.md
@@ -22,3 +22,23 @@
- 调用树组件的 `remove` 方法,可移除节点
+
+## 更新节点名称 {#update-node-title}
+
+调用树组件的 `updateNode` 方法可更新节点部分字段
+
+调用 `updateNodes` 可批量更新
+
+
+
+## 更新自定义字段 {#update-custom-field}
+
+调用树组件的 `updateNode` 方法更新自定义字段
+
+
+
+## 重新加载子节点 {#reload-children}
+
+调用 `updateNode` 传入新的 `children` 列表可以重新加载子节点
+
+
diff --git a/src/components/Tree.vue b/src/components/Tree.vue
index e8ab382..4f70e63 100644
--- a/src/components/Tree.vue
+++ b/src/components/Tree.vue
@@ -405,6 +405,8 @@ const {
filter,
showCheckedNodes,
loadRootNodes,
+ updateNode,
+ updateNodes,
} = usePublicTreeAPI(nonReactive, props, {
resetSpaceHeights,
updateExpandedKeys,
@@ -705,6 +707,8 @@ defineExpose({
filter,
showCheckedNodes,
loadRootNodes,
+ updateNode,
+ updateNodes,
scrollTo,
})
diff --git a/src/constants/index.ts b/src/constants/index.ts
index b316bdf..6a5ae03 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -39,6 +39,8 @@ export const TREE_API_METHODS = [
'filter',
'showCheckedNodes',
'loadRootNodes',
+ 'updateNode',
+ 'updateNodes',
'scrollTo'
] as const
diff --git a/src/hooks/usePublicTreeAPI.ts b/src/hooks/usePublicTreeAPI.ts
index 7c553a2..d22737f 100644
--- a/src/hooks/usePublicTreeAPI.ts
+++ b/src/hooks/usePublicTreeAPI.ts
@@ -235,6 +235,18 @@ export const usePublicTreeAPI = (
isRootLoading.value = false
})
}
+ /**
+ * 更新单个节点
+ */
+ function updateNode(key: TreeNodeKeyType, newNode: ITreeNodeOptions) {
+ return nonReactive.store.updateNode(key, newNode)
+ }
+ /**
+ * 更新多个节点
+ */
+ function updateNodes(newNodes: ITreeNodeOptions[]) {
+ return nonReactive.store.updateNodes(newNodes)
+ }
return {
unloadCheckedNodes,
@@ -269,5 +281,7 @@ export const usePublicTreeAPI = (
filter,
showCheckedNodes,
loadRootNodes,
+ updateNode,
+ updateNodes,
}
}
diff --git a/src/store/tree-store.ts b/src/store/tree-store.ts
index 79e5d60..15d1bd1 100644
--- a/src/store/tree-store.ts
+++ b/src/store/tree-store.ts
@@ -232,8 +232,8 @@ export default class TreeStore extends TreeEventTarget {
* @param triggerDataChange 是否触发视图刷新
*/
private triggerCheckedChange(
- triggerEvent: boolean = true,
- triggerDataChange: boolean = true
+ triggerEvent = true,
+ triggerDataChange = true,
) {
if (triggerEvent) {
this.emit('checked-change', this.getCheckedNodes(), this.getCheckedKeys())
@@ -244,6 +244,24 @@ export default class TreeStore extends TreeEventTarget {
}
}
+ /**
+ * 触发 selected-change 的快捷方法
+ * @param triggerEvent 是否触发事件
+ * @param triggerDataChange 是否触发视图刷新
+ */
+ private triggerSelectedChange(
+ triggerEvent = true,
+ triggerDataChange = true,
+ ) {
+ if (triggerEvent) {
+ this.emit('selected-change', this.getSelectedNode(), this.getSelectedKey())
+ }
+
+ if (triggerDataChange) {
+ this.emit('render-data-change')
+ }
+ }
+
/**
* 设置单选选中
* @param key 选中节点 key
@@ -273,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
}
}
@@ -294,17 +309,9 @@ export default class TreeStore extends TreeEventTarget {
} else {
this.emit('unselect', node)
}
-
- this.emit(
- 'selected-change',
- this.getSelectedNode(),
- this.getSelectedKey()
- )
}
- if (triggerDataChange) {
- this.emit('render-data-change')
- }
+ this.triggerSelectedChange(triggerEvent, triggerDataChange)
}
/**
@@ -317,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) {
@@ -327,17 +332,7 @@ export default class TreeStore extends TreeEventTarget {
}
}
- if (triggerEvent) {
- this.emit(
- 'selected-change',
- this.getSelectedNode(),
- this.getSelectedKey()
- )
- }
-
- if (triggerDataChange) {
- this.emit('render-data-change')
- }
+ this.triggerSelectedChange(triggerEvent, triggerDataChange)
}
/**
@@ -359,17 +354,7 @@ export default class TreeStore extends TreeEventTarget {
} else if (this.unloadSelectedKey !== null) {
this.unloadSelectedKey = null
- if (triggerEvent) {
- this.emit(
- 'selected-change',
- this.getSelectedNode(),
- this.getSelectedKey()
- )
- }
-
- if (triggerDataChange) {
- this.emit('render-data-change')
- }
+ this.triggerSelectedChange(triggerEvent, triggerDataChange)
}
}
@@ -407,23 +392,7 @@ export default class TreeStore extends TreeEventTarget {
})
.then(children => {
if (Array.isArray(children)) {
- const parentIndex: number = this.findIndex(node)
- if (parentIndex === -1) return
- node._loaded = true
- node.expand = value
- node.setChildren(children)
- // 如果单选选中的值为空,则允许后续数据覆盖单选 value
- const currentCheckedKeys = this.getCheckedKeys()
- const flattenChildren = this.flattenData(
- node.children,
- this.getSelectedKey === null
- )
- this.insertIntoFlatData(parentIndex + 1, flattenChildren)
- // 如果有未加载的选中节点,判断其是否已加载
- this.setUnloadCheckedKeys(currentCheckedKeys)
- if (this.unloadSelectedKey !== null) {
- this.setUnloadSelectedKey(this.unloadSelectedKey)
- }
+ this.loadChildren(node, children, value)
this.emit('set-data')
}
})
@@ -518,6 +487,116 @@ 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) {
+ const node = this.mapData[key]
+ if (!node) return
+
+ const newNodeCopy: ITreeNodeOptions = {}
+ const notAllowedFields = [
+ this.options.keyField,
+ 'indeterminate',
+ 'visible',
+ 'isLeaf',
+ ]
+
+ // Exclude key field and fields starting with '_'
+ Object.keys(newNode).forEach((field) => {
+ if (!field.startsWith('_') && !notAllowedFields.includes(field)) {
+ newNodeCopy[field] = newNode[field]
+ }
+ })
+
+ const previousCheckedKeys = this.getCheckedKeys()
+ const previousSelectedKey = this.getSelectedKey()
+ let triggerSetDataFlag = this.isChildrenChanged(node, 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(node, newNodeCopy.children, node.expand)
+ }
+
+ delete newNodeCopy.children
+ }
+ if ('checked' in newNodeCopy) {
+ this.setChecked(key, newNodeCopy.checked, false, false)
+ delete newNodeCopy.checked
+ }
+ if ('selected' in newNodeCopy) {
+ this.setSelected(key, newNodeCopy.selected, false, false)
+ delete newNodeCopy.selected
+ }
+ if ('expand' in newNodeCopy) {
+ this.setExpand(key, newNodeCopy.expand, false, false, false)
+ delete newNodeCopy.expand
+ }
+ Object.keys(newNodeCopy).forEach((field) => {
+ node[field] = newNodeCopy[field]
+ })
+
+ const currentCheckedKeys = this.getCheckedKeys()
+ const currentSelectedKey = this.getSelectedKey()
+
+ if (triggerEvent) {
+ if (JSON.stringify(currentCheckedKeys.sort()) !== JSON.stringify(previousCheckedKeys.sort())) {
+ this.triggerCheckedChange(true, false)
+ }
+
+ if (currentSelectedKey !== previousSelectedKey) {
+ this.triggerSelectedChange(true, false)
+ }
+ }
+
+ if (triggerDataChange) {
+ if (triggerSetDataFlag) {
+ this.emit('set-data')
+ }
+ this.emit('visible-data-change')
+ }
+ }
+
+ updateNodes(newNodes: ITreeNodeOptions[]) {
+ const validNodes = newNodes.filter((node) => node[this.options.keyField] != null)
+ if (!validNodes.length) return
+
+ const previousCheckedKeys = this.getCheckedKeys()
+ const previousSelectedKey = this.getSelectedKey()
+ let triggerSetDataFlag = 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()
+ const currentSelectedKey = this.getSelectedKey()
+
+ if (JSON.stringify(currentCheckedKeys.sort()) !== JSON.stringify(previousCheckedKeys.sort())) {
+ this.triggerCheckedChange(true, false)
+ }
+
+ if (currentSelectedKey !== previousSelectedKey) {
+ this.triggerSelectedChange(true, false)
+ }
+
+ if (triggerSetDataFlag) {
+ this.emit('set-data')
+ }
+
+ this.emit('visible-data-change')
+ }
+
//#endregion Set api
//#region Get api
@@ -618,12 +697,14 @@ export default class TreeStore extends TreeEventTarget {
insertBefore(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
- referenceKey: TreeNodeKeyType
+ referenceKey: TreeNodeKeyType,
+ triggerEvent = true,
+ triggerDataChange = true,
): TreeNode | null {
const node = this.getInsertedNode(insertedNode, referenceKey)
if (!node) return null
- this.remove(node[this.options.keyField], false)
+ this.remove(node[this.options.keyField], false, false)
const referenceNode = this.mapData[referenceKey]
const parentNode = referenceNode._parent
@@ -636,19 +717,25 @@ export default class TreeStore extends TreeEventTarget {
const dataIndex =
(parentNode && -1) || this.findIndex(referenceKey, this.data)
- this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex)
- this.emit('visible-data-change')
+ this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex, triggerEvent, triggerDataChange)
+
+ if (triggerDataChange) {
+ this.emit('visible-data-change')
+ }
+
return node
}
insertAfter(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
- referenceKey: TreeNodeKeyType
+ referenceKey: TreeNodeKeyType,
+ triggerEvent = true,
+ triggerDataChange = true,
): TreeNode | null {
const node = this.getInsertedNode(insertedNode, referenceKey)
if (!node) return null
- this.remove(node[this.options.keyField], false)
+ this.remove(node[this.options.keyField], false, false)
const referenceNode = this.mapData[referenceKey]
const parentNode = referenceNode._parent
@@ -674,57 +761,77 @@ export default class TreeStore extends TreeEventTarget {
const dataIndex =
(parentNode && -1) || this.findIndex(referenceKey, this.data) + 1
- this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex)
- this.emit('visible-data-change')
+ this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex, triggerEvent, triggerDataChange)
+
+ if (triggerDataChange) {
+ this.emit('visible-data-change')
+ }
+
return node
}
append(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
- parentKey: TreeNodeKeyType
+ parentKey: TreeNodeKeyType,
+ triggerEvent = true,
+ triggerDataChange = true,
): TreeNode | null {
const parentNode = this.mapData[parentKey]
if (!parentNode.isLeaf) {
const childrenLength = parentNode.children.length
return this.insertAfter(
insertedNode,
- parentNode.children[childrenLength - 1][this.options.keyField]
+ parentNode.children[childrenLength - 1][this.options.keyField],
+ triggerEvent,
+ triggerDataChange,
)
}
const node = this.getInsertedNode(insertedNode, parentKey, true)
if (!node) return null
- this.remove(node[this.options.keyField], false)
+ this.remove(node[this.options.keyField], false, false)
const flatIndex = this.findIndex(parentKey) + 1
- this.insertIntoStore(node, parentNode, 0, flatIndex)
- this.emit('visible-data-change')
+ this.insertIntoStore(node, parentNode, 0, flatIndex, undefined, triggerEvent, triggerDataChange)
+
+ if (triggerDataChange) {
+ this.emit('visible-data-change')
+ }
+
return node
}
prepend(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
- parentKey: TreeNodeKeyType
+ parentKey: TreeNodeKeyType,
+ triggerEvent = true,
+ triggerDataChange = true,
): TreeNode | null {
const parentNode = this.mapData[parentKey]
if (!parentNode.isLeaf) {
return this.insertBefore(
insertedNode,
- parentNode.children[0][this.options.keyField]
+ parentNode.children[0][this.options.keyField],
+ triggerEvent,
+ triggerDataChange,
)
}
const node = this.getInsertedNode(insertedNode, parentKey, true)
if (!node) return null
- this.remove(node[this.options.keyField], false)
+ this.remove(node[this.options.keyField], false, false)
const flatIndex = this.findIndex(parentKey) + 1
- this.insertIntoStore(node, parentNode, 0, flatIndex)
- this.emit('visible-data-change')
+ this.insertIntoStore(node, parentNode, 0, flatIndex, undefined, triggerEvent, triggerDataChange)
+
+ if (triggerDataChange) {
+ this.emit('visible-data-change')
+ }
+
return node
}
@@ -734,7 +841,8 @@ export default class TreeStore extends TreeEventTarget {
*/
remove(
removedKey: TreeNodeKeyType,
- triggerDataChange: boolean = true
+ triggerEvent: boolean = true,
+ triggerDataChange: boolean = true,
): TreeNode | null {
const node = this.mapData[removedKey]
if (!node) return null
@@ -781,8 +889,53 @@ export default class TreeStore extends TreeEventTarget {
node._parent.indeterminate = false
}
// 更新被移除处父节点状态
- this.updateMovingNodeStatus(node)
+ this.updateMovingNodeStatus(node, triggerEvent, triggerDataChange)
+ }
+
+ if (triggerDataChange) {
+ this.emit('visible-data-change')
+ }
+
+ return node
+ }
+
+ private removeChildren(
+ parentKey: TreeNodeKeyType,
+ triggerEvent: boolean = true,
+ triggerDataChange: boolean = true,
+ ) {
+ const node = this.mapData[parentKey]
+ if (!node || !node.children.length) return null
+
+ const firstChild = node.children[0]
+ let movingNode = firstChild
+
+ // 从 flatData 中移除
+ const index = this.findIndex(node)
+ if (index === -1) return null
+ let deleteCount = 0
+ const length = this.flatData.length
+ for (let i = index + 1; i < length; i++) {
+ if (this.flatData[i]._level > node._level) {
+ // 从 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)
+
+ // 从父节点 children 中移除
+ node.children.splice(0, node.children.length)
+ node.isLeaf = true
+ node.indeterminate = false
+
+ // 更新被移除处父节点状态
+ this.updateMovingNodeStatus(movingNode, triggerEvent, triggerDataChange)
if (triggerDataChange) {
this.emit('visible-data-change')
@@ -791,6 +944,29 @@ export default class TreeStore extends TreeEventTarget {
return node
}
+ private loadChildren(node: TreeNode, children: any[], expand: boolean) {
+ const parentIndex: number = this.findIndex(node)
+ if (parentIndex === -1) return
+ node._loaded = true
+ node.expand = expand
+ node.setChildren(children)
+ node.isLeaf = !node.children.length
+ // 如果单选选中的值为空,则允许后续数据覆盖单选 value
+ const currentCheckedKeys = this.getCheckedKeys()
+ const flattenChildren = this.flattenData(
+ node.children,
+ this.getSelectedKey() === null
+ )
+ this.insertIntoFlatData(parentIndex + 1, flattenChildren)
+ // 如果有未加载的选中节点,判断其是否已加载
+ this.setUnloadCheckedKeys(currentCheckedKeys)
+ if (this.unloadSelectedKey !== null) {
+ this.setUnloadSelectedKey(this.unloadSelectedKey)
+ }
+
+ this.checkNodeUpward(node, true)
+ }
+
private getInsertedNode(
insertedNode: TreeNodeKeyType | ITreeNodeOptions,
referenceKey: TreeNodeKeyType,
@@ -833,7 +1009,9 @@ export default class TreeStore extends TreeEventTarget {
parentNode: TreeNode | null,
childIndex: number,
flatIndex: number,
- dataIndex?: number
+ dataIndex?: number,
+ triggerEvent = true,
+ triggerDataChange = true,
): void {
if (flatIndex === -1) return
@@ -873,16 +1051,16 @@ export default class TreeStore extends TreeEventTarget {
this.insertIntoFlatData(flatIndex, nodes)
// 更新插入节点父节点状态
- this.updateMovingNodeStatus(node)
+ this.updateMovingNodeStatus(node, triggerEvent, triggerDataChange)
}
- private updateMovingNodeStatus(movingNode: TreeNode): void {
+ private updateMovingNodeStatus(movingNode: TreeNode, triggerEvent = true, triggerDataChange = true): void {
// 处理多选
this.checkNodeUpward(movingNode)
- this.triggerCheckedChange()
+ this.triggerCheckedChange(triggerEvent, triggerDataChange)
// 处理单选
if (movingNode.selected) {
- this.setSelected(movingNode[this.options.keyField], true)
+ this.setSelected(movingNode[this.options.keyField], true, triggerEvent, triggerDataChange)
}
}
@@ -1012,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) {
@@ -1035,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
}
@@ -1074,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)