diff --git a/docs/examples/select-v2/use-valueKey.vue b/docs/examples/select-v2/use-valueKey.vue index 9f7107e580..8a568df97f 100644 --- a/docs/examples/select-v2/use-valueKey.vue +++ b/docs/examples/select-v2/use-valueKey.vue @@ -3,7 +3,7 @@ v-model="value" :options="options" placeholder="Please select" - value-key="value.name" + value-key="name" /> @@ -11,7 +11,7 @@ import { ref } from 'vue' const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] -const value = ref() +const value = ref({ name: 'Option 1', test: 'test 0' }) const options = Array.from({ length: 1000 }).map((_, idx) => ({ value: { name: `Option ${idx + 1}`, diff --git a/packages/components/select-v2/__tests__/select.test.ts b/packages/components/select-v2/__tests__/select.test.ts index 9af471fa21..f592934cea 100644 --- a/packages/components/select-v2/__tests__/select.test.ts +++ b/packages/components/select-v2/__tests__/select.test.ts @@ -272,22 +272,19 @@ describe('Select', () => { it('default value is Object', async () => { const wrapper = createSelect({ data: () => ({ - valueKey: 'value', - value: { - value: '1', - label: 'option_a', - }, + valueKey: 'id', + value: { id: 1 }, options: [ { - value: '1', + value: { id: 1 }, label: 'option_a', }, { - value: '2', + value: { id: 2 }, label: 'option_b', }, { - value: '3', + value: { id: 3 }, label: 'option_c', }, ], @@ -298,9 +295,7 @@ describe('Select', () => { expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe( vm.options[0].label ) - expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe( - vm.value.label - ) + expect(vm.value).toEqual(vm.options[0].value) }) it('sync set value and options', async () => { @@ -352,22 +347,19 @@ describe('Select', () => { return { options: [ { - id: 'id 1', - value: 'value 1', + value: { id: 1 }, label: 'option 1', }, { - id: 'id 2', - value: 'value 2', + value: { id: 2 }, label: 'option 2', }, { - id: 'id 3', - value: 'value 3', + value: { id: 3 }, label: 'option 3', }, ], - value: '', + value: undefined, valueKey: 'id', } }, @@ -378,12 +370,11 @@ describe('Select', () => { const options = getOptions() options[1].click() await nextTick() - expect(vm.value).toBe(vm.options[1].id) - vm.valueKey = 'value' - await nextTick() + expect(vm.value).toEqual(vm.options[1].value) + options[2].click() await nextTick() - expect(vm.value).toBe(vm.options[2].value) + expect(vm.value).toEqual(vm.options[2].value) }) it('disabled option', async () => { @@ -616,18 +607,15 @@ describe('Select', () => { return { options: [ { - id: 'id 1', - value: 'value 1', + value: { id: 1, value: 'a' }, label: 'option 1', }, { - id: 'id 2', - value: 'value 2', + value: { id: 2, value: 'b' }, label: 'option 2', }, { - id: 'id 3', - value: 'value 3', + value: { id: 3, value: 'c' }, label: 'option 3', }, ], @@ -644,13 +632,24 @@ describe('Select', () => { options[1].click() await nextTick() expect(vm.value.length).toBe(1) - expect(vm.value[0]).toBe(vm.options[1].id) - vm.valueKey = 'value' - await nextTick() + expect(vm.value).toContainEqual(vm.options[1].value) options[2].click() await nextTick() expect(vm.value.length).toBe(2) - expect(vm.value[1]).toBe(vm.options[2].value) + expect(vm.value).toContainEqual(vm.options[2].value) + options[2].click() + await nextTick() + expect(vm.value.length).toBe(1) + expect(vm.value).not.toContainEqual(vm.options[2].value) + + vm.valueKey = 'value' + await nextTick() + expect(vm.value.length).toBe(1) + expect(vm.value).toContainEqual(vm.options[1].value) + options[0].click() + await nextTick() + expect(vm.value.length).toBe(2) + expect(vm.value).toContainEqual(vm.options[0].value) }) }) diff --git a/packages/components/select-v2/src/select-dropdown.tsx b/packages/components/select-v2/src/select-dropdown.tsx index 894fb20e83..bf0629ae8e 100644 --- a/packages/components/select-v2/src/select-dropdown.tsx +++ b/packages/components/select-v2/src/select-dropdown.tsx @@ -1,4 +1,12 @@ -import { computed, defineComponent, inject, ref, unref, watch } from 'vue' +import { + computed, + defineComponent, + inject, + ref, + toRaw, + unref, + watch, +} from 'vue' import { get } from 'lodash-unified' import { isObject, isUndefined } from '@element-plus/utils' import { @@ -69,7 +77,7 @@ export default defineComponent({ return ( arr && arr.some((item) => { - return get(item, valueKey) === get(target, valueKey) + return toRaw(get(item, valueKey)) === get(target, valueKey) }) ) } @@ -83,11 +91,10 @@ export default defineComponent({ } const isItemSelected = (modelValue: any[] | any, target: Option) => { - const { valueKey } = select.props if (select.props.multiple) { - return contains(modelValue, get(target, valueKey)) + return contains(modelValue, target.value) } - return isEqual(modelValue, get(target, valueKey)) + return isEqual(modelValue, target.value) } const isItemDisabled = (modelValue: any[] | any, selected: boolean) => { diff --git a/packages/components/select-v2/src/useSelect.ts b/packages/components/select-v2/src/useSelect.ts index 3693c91bf4..e564adb40a 100644 --- a/packages/components/select-v2/src/useSelect.ts +++ b/packages/components/select-v2/src/useSelect.ts @@ -176,7 +176,7 @@ const useSelect = (props: ExtractPropTypes, emit) => { const valueMap = new Map() filteredOptions.value.forEach((option, index) => { - valueMap.set(getValueKey(option), { option, index }) + valueMap.set(getValueKey(option.value), { option, index }) }) return valueMap }) @@ -409,7 +409,7 @@ const useSelect = (props: ExtractPropTypes, emit) => { if (props.multiple) { let selectedOptions = (props.modelValue as any[]).slice() - const index = getValueIndex(selectedOptions, getValueKey(option)) + const index = getValueIndex(selectedOptions, option.value) if (index > -1) { selectedOptions = [ ...selectedOptions.slice(0, index), @@ -421,7 +421,7 @@ const useSelect = (props: ExtractPropTypes, emit) => { props.multipleLimit <= 0 || selectedOptions.length < props.multipleLimit ) { - selectedOptions = [...selectedOptions, getValueKey(option)] + selectedOptions = [...selectedOptions, option.value] states.cachedOptions.push(option) selectNewOption(option) updateHoveringIndex(idx) @@ -445,7 +445,7 @@ const useSelect = (props: ExtractPropTypes, emit) => { } else { selectedIndex.value = idx states.selectedLabel = option.label - update(getValueKey(option)) + update(option.value) expanded.value = false states.isComposing = false states.isSilentBlur = byClick @@ -457,20 +457,21 @@ const useSelect = (props: ExtractPropTypes, emit) => { } } - const deleteTag = (event: MouseEvent, tag: Option) => { - const { valueKey } = props - const index = (props.modelValue as Array).indexOf(get(tag, valueKey)) + const deleteTag = (event: MouseEvent, option: Option) => { + let selectedOptions = (props.modelValue as any[]).slice() + + const index = getValueIndex(selectedOptions, option.value) if (index > -1 && !selectDisabled.value) { - const value = [ + selectedOptions = [ ...(props.modelValue as Array).slice(0, index), ...(props.modelValue as Array).slice(index + 1), ] states.cachedOptions.splice(index, 1) - update(value) - emit('remove-tag', get(tag, valueKey)) + update(selectedOptions) + emit('remove-tag', option.value) states.softFocus = true - removeNewOption(tag) + removeNewOption(option) return nextTick(focusAndUpdatePopup) } event.stopPropagation() @@ -673,8 +674,12 @@ const useSelect = (props: ExtractPropTypes, emit) => { states.previousValue = props.modelValue.toString() for (const value of props.modelValue) { - if (filteredOptionsValueMap.value.has(value)) { - const { index, option } = filteredOptionsValueMap.value.get(value) + const selectValue = getValueKey(value) + + if (filteredOptionsValueMap.value.has(selectValue)) { + const { index, option } = + filteredOptionsValueMap.value.get(selectValue) + states.cachedOptions.push(option) if (!initHovering) { updateHoveringIndex(index) @@ -691,7 +696,8 @@ const useSelect = (props: ExtractPropTypes, emit) => { states.previousValue = props.modelValue const options = filteredOptions.value const selectedItemIndex = options.findIndex( - (option) => getValueKey(option) === getValueKey(props.modelValue) + (option) => + getValueKey(option.value) === getValueKey(props.modelValue) ) if (~selectedItemIndex) { states.selectedLabel = options[selectedItemIndex].label