mirror of
https://github.com/element-plus/element-plus.git
synced 2024-11-27 02:01:15 +08:00
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:
parent
fd711d5c0f
commit
33ca7ee4f0
@ -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 | — | — |
|
||||
|
@ -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',
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
|
28
packages/components/tree-select/src/cache-options.ts
Normal file
28
packages/components/tree-select/src/cache-options.ts
Normal 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
|
||||
},
|
||||
})
|
@ -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),
|
||||
})
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
},
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user