fix(components): [el-tree-select] fix lazy and multiple select node (#17903)

* fix(components): [el-tree-select] fix lazy and multiple select node

1.Fix the issue where non-leaf nodes cannot be selected when using lazy and multiple together.

2. Since the original code did not handle the selection of non-leaf nodes before expanding
lazy-loaded child nodes, additional processing was added to link the selection of parent and child
nodes.

3. Because the time required for lazyLoad after nodeExpand cannot be predicted, the triggering time
of the node-expand event in the tree has been adjusted to occur within the callback of the expand
function. This change only affects the timing when using lazyLoad.

BREAKING CHANGE :
no

closed closed Fixes #17232

* fix: test typecheck

* fix: onNodeExpand event and update function name
This commit is contained in:
xuan 2024-09-01 20:59:31 +08:00 committed by GitHub
parent 93d28929fe
commit 27bdf1f076
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 101 additions and 8 deletions

View File

@ -725,6 +725,63 @@ describe('TreeSelect.vue', () => {
expect(modelValue.value).toEqual([])
})
test('cached checked node and use lazy multiple', async () => {
const modelValue = ref<number[]>([5])
const cacheData = reactive([{ value: 5, label: 'lazy load node5' }])
let id = 0
const { tree, select } = createComponent({
props: {
modelValue,
multiple: true,
showCheckbox: true,
checkStrictly: false,
lazy: true,
load: (node: object, resolve: (p: any) => any[]) => {
resolve([
{
value: ++id,
label: `lazy load node${id}`,
},
{
value: ++id,
label: `lazy load node${id}`,
isLeaf: true,
},
])
},
cacheData,
},
})
await nextTick()
const node1Checkbox = tree
.findAll('.el-tree-node__content')[0]
.find('.el-checkbox')
await node1Checkbox.trigger('click')
await nextTick()
expect(select.vm.modelValue).toEqual([5, 1])
const node2Checkbox = tree
.findAll('.el-tree-node__content')[1]
.find('.el-checkbox')
await node2Checkbox.trigger('click')
await nextTick()
expect(select.vm.modelValue).toEqual([5, 1, 2])
const node1 = tree.findAll('.el-tree-node__content')[0]
await node1.trigger('click')
await nextTick()
expect(select.vm.modelValue).toEqual([5, 3, 4, 2])
const node2 = tree.findAll('.el-tree-node__content')[1]
await node2.trigger('click')
await nextTick()
expect(select.vm.modelValue).toEqual([5, 6, 4, 2])
})
test('no checkbox and check on click node', async () => {
const { select, tree } = createComponent({
props: {

View File

@ -1,8 +1,8 @@
// @ts-nocheck
import { computed, nextTick, toRefs, watch } from 'vue'
import { isEqual, pick } from 'lodash-unified'
import { isEqual, isNil, pick } from 'lodash-unified'
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { escapeStringRegexp, isFunction } from '@element-plus/utils'
import { escapeStringRegexp, isEmpty, isFunction } from '@element-plus/utils'
import ElTree from '@element-plus/components/tree'
import TreeSelectOption from './tree-select-option'
import {
@ -113,6 +113,13 @@ export const useTree = (
return options
})
const getChildCheckedKeys = () => {
return tree.value?.getCheckedKeys().filter((checkedKey) => {
const node = tree.value?.getNode(checkedKey) as Node
return !isNil(node) && isEmpty(node.childNodes)
})
}
return {
...pick(toRefs(props), Object.keys(ElTree.props)),
...attrs,
@ -208,10 +215,9 @@ export const useTree = (
// only can select leaf node
else {
if (props.multiple) {
emit(
UPDATE_MODEL_EVENT,
cachedKeys.concat((tree.value as TreeInstance).getCheckedKeys(true))
)
const childKeys = getChildCheckedKeys()
emit(UPDATE_MODEL_EVENT, cachedKeys.concat(childKeys))
} else {
// select first leaf node when check parent
const firstLeaf = treeFind(
@ -258,6 +264,35 @@ export const useTree = (
select.value?.focus()
},
onNodeExpand: (data, node, e) => {
attrs.onNodeExpand?.(data, node, e)
nextTick(() => {
if (
!props.checkStrictly &&
props.lazy &&
props.multiple &&
node.checked
) {
const dataMap = {}
const uncachedCheckedKeys = (
tree.value as TreeInstance
).getCheckedKeys()
treeEach(
[tree.value.store.root],
(node) => (dataMap[node.key] = node),
(node) => node.childNodes
)
const cachedKeys = toValidArray(props.modelValue).filter(
(item) => !(item in dataMap) && !uncachedCheckedKeys.includes(item)
)
const childKeys = getChildCheckedKeys()
emit(UPDATE_MODEL_EVENT, cachedKeys.concat(childKeys))
}
})
},
// else
cacheOptions,
}

View File

@ -270,8 +270,9 @@ export default defineComponent({
tree.ctx.emit('node-collapse', props.node.data, props.node, instance)
props.node.collapse()
} else {
props.node.expand()
props.node.expand(() => {
ctx.emit('node-expand', props.node.data, props.node, instance)
})
}
}