fix(components): [TreeSelect] incorrect label when child not rendered (#10716)

* feat: add `treeEach` utility function

* fix(components): [TreeSelect] incorrect label when child not rendered

* docs(components): [TreeSelect] remove tips for resolved issues

* fix(components): [TreeSelect] add `cacheData` props for lazy label

* docs(components): [TreeSelect] add `cacheData` document and examples

* docs(components): [TreeSelect] add version identification for new props

* refactor(components): [TreeSelect] replace any type

* docs(components): [TreeSelect] update version tag
This commit is contained in:
yujinpan 2022-11-30 23:43:57 +08:00 committed by GitHub
parent fd711d5c0f
commit 33ca7ee4f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 11 deletions

View File

@ -18,14 +18,6 @@ tree-select/basic
:::
:::tip
Since `render-after-expand` defaults to `true`,
the selected label name may not be displayed when echoing,
you can set it to `false` to display the correct name.
:::
## Select any level
When using the `check-strictly=true` attribute, any node can be checked,
@ -107,3 +99,9 @@ and please go to the original component to view the documentation.
| --------------------------------------- | ----------------------------- | ----------------------------------- | ---------------------------------- |
| [tree](./tree.md#attributes) | [tree](./tree.md#method) | [tree](./tree.md#events) | [tree](./tree.md#slots) |
| [select](./select.md#select-attributes) | [select](./select.md#methods) | [select](./select.md#select-events) | [select](./select.md#select-slots) |
### Own Attributes
| Name | Description | Type | Accepted Values | Default |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----- | --------------- | ------- |
| cacheData<VersionTag version="2.2.26" /> | The cached data of the lazy node, the structure is the same as the data, used to get the label of the unloaded data | array | — | — |

View File

@ -1,11 +1,23 @@
<template>
<el-tree-select v-model="value" lazy :load="load" :props="props" />
<el-divider />
<VersionTag version="2.2.26" /> show lazy load label:
<el-tree-select
v-model="value2"
lazy
:load="load"
:props="props"
:cache-data="cacheData"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const value = ref()
const value2 = ref(5)
const cacheData = [{ value: 5, label: 'lazy load node5' }]
const props = {
label: 'label',

View File

@ -433,4 +433,45 @@ describe('TreeSelect.vue', () => {
tree.findAll('.el-tree-node__children')[0].attributes('style')
).not.toContain('display: none;')
})
test('show correct label when child options are not rendered', async () => {
const modelValue = ref<number>()
const { select } = createComponent({
props: {
modelValue,
renderAfterExpand: true,
},
})
await nextTick()
expect(select.vm.selectedLabel).toBe('')
modelValue.value = 111
await nextTick()
expect(select.vm.selectedLabel).toBe('三级 1-1')
})
test('show correct label when lazy load', async () => {
const modelValue = ref<number>(1)
const { select } = createComponent({
props: {
data: [],
modelValue,
lazy: true,
load: (node: object, resolve: (p: any) => any[]) => {
resolve([{ value: 2, label: '2-label', isLeaf: true }])
},
cacheData: [{ value: 3, label: '3-label' }],
},
})
// no load & no cache will be default value
await nextTick()
expect(select.vm.selectedLabel).toBe(1)
// no load & has cache will be correct label
modelValue.value = 3
await nextTick()
expect(select.vm.selectedLabel).toBe('3-label')
})
})

View File

@ -0,0 +1,28 @@
import { defineComponent, inject } from 'vue'
import { selectKey } from '@element-plus/components/select'
import type { SelectContext } from '@element-plus/components/select'
import type { PropType } from 'vue'
// same as el-option instance,
// these are required for `cachedOptions`
export type CacheOption = {
value: string | number | boolean | object
currentLabel: string | number
isDisabled: boolean
}
export default defineComponent({
props: {
data: {
type: Array as PropType<CacheOption[]>,
default: () => [],
},
},
setup(props) {
const select = inject(selectKey) as NonNullable<SelectContext>
props.data.forEach((item) => select.cachedOptions.set(item.value, item))
return () => undefined
},
})

View File

@ -6,6 +6,7 @@ import ElSelect from '@element-plus/components/select'
import ElTree from '@element-plus/components/tree'
import { useSelect } from './select'
import { useTree } from './tree'
import CacheOptions from './cache-options'
export default defineComponent({
name: 'ElTreeSelect',
@ -14,6 +15,10 @@ export default defineComponent({
props: {
...ElSelect.props,
...ElTree.props,
cacheData: {
type: Array,
default: () => [],
},
},
setup(props, context) {
const { slots, expose } = context
@ -24,7 +29,11 @@ export default defineComponent({
const key = computed(() => props.nodeKey || props.valueKey || 'value')
const selectProps = useSelect(props, context, { select, tree, key })
const treeProps = useTree(props, context, { select, tree, key })
const { cacheOptions, ...treeProps } = useTree(props, context, {
select,
tree,
key,
})
// expose ElTree/ElSelect methods
const methods = reactive({})
@ -71,7 +80,8 @@ export default defineComponent({
}),
{
...slots,
default: () =>
default: () => [
h(CacheOptions, { data: cacheOptions.value }),
h(
ElTree,
reactive({
@ -79,6 +89,7 @@ export default defineComponent({
ref: (ref) => (tree.value = ref),
})
),
],
}
)
},

View File

@ -5,7 +5,14 @@ import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { isFunction } from '@element-plus/utils'
import ElTree from '@element-plus/components/tree'
import TreeSelectOption from './tree-select-option'
import { isValidArray, isValidValue, toValidArray, treeFind } from './utils'
import {
isValidArray,
isValidValue,
toValidArray,
treeEach,
treeFind,
} from './utils'
import type { CacheOption } from './cache-options'
import type { Ref } from 'vue'
import type ElSelect from '@element-plus/components/select'
import type Node from '@element-plus/components/tree/src/model/node'
@ -80,6 +87,27 @@ export const useTree = (
})
.filter((item) => isValidValue(item))
const cacheOptions = computed(() => {
if (!props.renderAfterExpand && !props.lazy) return []
const options: CacheOption[] = []
treeEach(
props.data.concat(props.cacheData),
(node) => {
const value = getNodeValByProp('value', node)
options.push({
value,
currentLabel: getNodeValByProp('label', node),
isDisabled: getNodeValByProp('disabled', node),
})
},
(data) => getNodeValByProp('children', data)
)
return options
})
return {
...pick(toRefs(props), Object.keys(ElTree.props)),
...attrs,
@ -190,5 +218,8 @@ export const useTree = (
}
}
},
// else
cacheOptions,
}
}

View File

@ -59,3 +59,20 @@ export function treeFind<T extends TreeNodeData, R>(
}
}
}
export function treeEach<T extends TreeNodeData>(
treeData: T[],
callback: TreeCallback<T, void>,
getChildren: (data: T) => T[],
parent?: T
) {
for (let i = 0; i < treeData.length; i++) {
const data = treeData[i]
callback(data, i, treeData, parent)
const children = getChildren(data)
if (isValidArray(children)) {
treeEach(children, callback, getChildren, data)
}
}
}