diff --git a/packages/components/tree-select/__tests__/tree-select.test.tsx b/packages/components/tree-select/__tests__/tree-select.test.tsx index ae4bbc72b1..3ee47a663b 100644 --- a/packages/components/tree-select/__tests__/tree-select.test.tsx +++ b/packages/components/tree-select/__tests__/tree-select.test.tsx @@ -499,6 +499,54 @@ describe('TreeSelect.vue', () => { expect(select.vm.modelValue).toBe(undefined) }) + test('emit change when toggling node via label click', async () => { + const handleChange = vi.fn() + const { tree } = createComponent({ + props: { + showCheckbox: true, + checkOnClickNode: true, + onChange: handleChange, + }, + }) + + await nextTick() + const target = tree.findAll('.el-tree-node__content').slice(-1)[0] + + await target.trigger('click') + await nextTick() + expect(handleChange).toHaveBeenLastCalledWith(111) + + await target.trigger('click') + await nextTick() + expect(handleChange).toHaveBeenLastCalledWith(undefined) + expect(handleChange).toHaveBeenCalledTimes(2) + }) + + test('emit change once when toggling node via checkbox click', async () => { + const handleChange = vi.fn() + const { tree } = createComponent({ + props: { + showCheckbox: true, + checkOnClickNode: true, + onChange: handleChange, + }, + }) + + await nextTick() + const checkbox = tree + .findAll('.el-tree-node__content .el-checkbox__original') + .slice(-1)[0] + + await checkbox.trigger('click') + await nextTick() + expect(handleChange).toHaveBeenLastCalledWith(111) + + await checkbox.trigger('click') + await nextTick() + expect(handleChange).toHaveBeenLastCalledWith(undefined) + expect(handleChange).toHaveBeenCalledTimes(2) + }) + test('expand selected node`s parent in first time', async () => { const value = ref(111) const { tree } = createComponent({ diff --git a/packages/components/tree-select/src/tree-select.vue b/packages/components/tree-select/src/tree-select.vue index 9e499fb1a4..cdad9d1839 100644 --- a/packages/components/tree-select/src/tree-select.vue +++ b/packages/components/tree-select/src/tree-select.vue @@ -26,19 +26,27 @@ export default defineComponent({ }, }, setup(props, context) { - const { slots, expose } = context + const { slots, expose, emit, attrs } = context + const childAttrs = { + ...attrs, + onChange: undefined, + } const select = ref() const tree = ref() const key = computed(() => props.nodeKey || props.valueKey || 'value') - const selectProps = useSelect(props, context, { select, tree, key }) - const { cacheOptions, ...treeProps } = useTree(props, context, { - select, - tree, - key, - }) + const selectProps = useSelect(props, { attrs, emit }, { select, tree, key }) + const { cacheOptions, ...treeProps } = useTree( + props, + { attrs: childAttrs, slots, emit }, + { + select, + tree, + key, + } + ) // expose ElTree/ElSelect methods const methods = reactive({}) diff --git a/packages/components/tree-select/src/tree.ts b/packages/components/tree-select/src/tree.ts index 4696c2bcd6..0856dc113a 100644 --- a/packages/components/tree-select/src/tree.ts +++ b/packages/components/tree-select/src/tree.ts @@ -1,7 +1,7 @@ // @ts-nocheck import { computed, nextTick, toRefs, watch } from 'vue' import { isEqual, isNil, pick } from 'lodash-unified' -import { UPDATE_MODEL_EVENT } from '@element-plus/constants' +import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants' import { escapeStringRegexp, isEmpty, isFunction } from '@element-plus/utils' import ElTree from '@element-plus/components/tree' import TreeSelectOption from './tree-select-option' @@ -121,6 +121,17 @@ export const useTree = ( }) } + const emitChange = (val: any | any[]) => { + if (!isEqual(props.modelValue, val)) { + emit(CHANGE_EVENT, val) + } + } + + function update(val) { + emit(UPDATE_MODEL_EVENT, val) + emitChange(val) + } + return { ...pick(toRefs(props), Object.keys(ElTree.props)), ...attrs, @@ -202,9 +213,8 @@ export const useTree = ( const checkedKeys = cachedKeys.concat(uncachedCheckedKeys) if (props.checkStrictly) { - emit( - UPDATE_MODEL_EVENT, - // Checking for changes may come from `check-on-node-click` + // Checking for changes may come from `check-on-node-click` + update( props.multiple ? checkedKeys : checkedKeys.includes(dataValue) @@ -213,40 +223,36 @@ export const useTree = ( ) } // only can select leaf node - else { - if (props.multiple) { - const childKeys = getChildCheckedKeys() + else if (props.multiple) { + const childKeys = getChildCheckedKeys() + update(cachedKeys.concat(childKeys)) + } else { + // select first leaf node when check parent + const firstLeaf = treeFind( + [data], + (data) => + !isValidArray(getNodeValByProp('children', data)) && + !getNodeValByProp('disabled', data), + (data) => getNodeValByProp('children', data) + ) + const firstLeafKey = firstLeaf + ? getNodeValByProp('value', firstLeaf) + : undefined - emit(UPDATE_MODEL_EVENT, cachedKeys.concat(childKeys)) - } else { - // select first leaf node when check parent - const firstLeaf = treeFind( + // unselect when any child checked + const hasCheckedChild = + isValidValue(props.modelValue) && + !!treeFind( [data], - (data) => - !isValidArray(getNodeValByProp('children', data)) && - !getNodeValByProp('disabled', data), + (data) => getNodeValByProp('value', data) === props.modelValue, (data) => getNodeValByProp('children', data) ) - const firstLeafKey = firstLeaf - ? getNodeValByProp('value', firstLeaf) - : undefined - // unselect when any child checked - const hasCheckedChild = - isValidValue(props.modelValue) && - !!treeFind( - [data], - (data) => getNodeValByProp('value', data) === props.modelValue, - (data) => getNodeValByProp('children', data) - ) - - emit( - UPDATE_MODEL_EVENT, - firstLeafKey === props.modelValue || hasCheckedChild - ? undefined - : firstLeafKey - ) - } + update( + firstLeafKey === props.modelValue || hasCheckedChild + ? undefined + : firstLeafKey + ) } nextTick(() => { @@ -289,7 +295,7 @@ export const useTree = ( ) const childKeys = getChildCheckedKeys() - emit(UPDATE_MODEL_EVENT, cachedKeys.concat(childKeys)) + update(cachedKeys.concat(childKeys)) } }) },