From 5cee7a1464978bc3e68b961047c833325537bfed Mon Sep 17 00:00:00 2001 From: xiaohe0601 Date: Fri, 20 Jun 2025 10:01:20 +0800 Subject: [PATCH] fix(components): [table] fix children can't be unchecked (#20995) * fix(components): [table] fix children can't be unchecked * test(components): [table] reserve selection --- .../table/__tests__/table-column.test.ts | 120 +++++++++++++++++- packages/components/table/src/store/expand.ts | 10 +- .../components/table/src/store/watcher.ts | 6 +- packages/components/table/src/util.ts | 20 ++- 4 files changed, 149 insertions(+), 7 deletions(-) diff --git a/packages/components/table/__tests__/table-column.test.ts b/packages/components/table/__tests__/table-column.test.ts index 3a426af548..a5449f8edf 100644 --- a/packages/components/table/__tests__/table-column.test.ts +++ b/packages/components/table/__tests__/table-column.test.ts @@ -3,7 +3,12 @@ import { describe, expect, it, vi } from 'vitest' import triggerEvent from '@element-plus/test-utils/trigger-event' import ElTable from '../src/table.vue' import ElTableColumn from '../src/table-column' -import { doubleWait, getTestData, mount } from './table-test-common' +import { + doubleWait, + getMutliRowTestData, + getTestData, + mount, +} from './table-test-common' vi.mock('lodash-unified', async () => { return { @@ -424,6 +429,119 @@ describe('table column', () => { selectionKeys = getSelection().map((item) => item.id) expect(selectionKeys).toEqual([0, 1]) }) + + it('reserve selection', async () => { + const wrapper = mount({ + components: { + ElTable, + ElTableColumn, + }, + template: ` + + + + + + + + `, + data() { + return { testData: [] } + }, + created() { + this.updateTestData(1) + }, + methods: { + getTestData(page) { + switch (page) { + case 1: { + const data = getTestData() + data[1].children = [ + { + id: 21, + name: "A Bug's Life copy 1", + release: '1998-11-25-1', + director: 'John Lasseter', + runtime: 95, + }, + { + id: 22, + name: "A Bug's Life copy 2", + release: '1998-11-25-2', + director: 'John Lasseter', + runtime: 95, + }, + ] + return data + } + case 2: { + return getMutliRowTestData() + } + } + return [] + }, + updateTestData(page) { + this.testData = this.getTestData(page) + }, + }, + }) + + await doubleWait() + + wrapper.findAll('.el-checkbox')[1].trigger('click') + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(1) + expect(wrapper.findAll('.el-checkbox')[1].classes()).include( + 'is-checked' + ) + + wrapper.vm.updateTestData(2) + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(0) + + wrapper.vm.updateTestData(1) + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(1) + expect(wrapper.findAll('.el-checkbox')[1].classes()).include( + 'is-checked' + ) + + wrapper.findAll('.el-checkbox')[1].trigger('click') + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(0) + + // test children + + wrapper.findAll('.el-checkbox')[3].trigger('click') + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(1) + expect(wrapper.findAll('.el-checkbox')[3].classes()).include( + 'is-checked' + ) + + wrapper.vm.updateTestData(2) + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(0) + + wrapper.vm.updateTestData(1) + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(1) + expect(wrapper.findAll('.el-checkbox')[3].classes()).include( + 'is-checked' + ) + + // #20987 + + wrapper.findAll('.el-checkbox')[3].trigger('click') + await doubleWait() + expect(wrapper.findAll('.el-checkbox.is-checked')).toHaveLength(0) + + wrapper.unmount() + }) }) describe('= index', () => { diff --git a/packages/components/table/src/store/expand.ts b/packages/components/table/src/store/expand.ts index 977b1d66a2..6e1a42b3e0 100644 --- a/packages/components/table/src/store/expand.ts +++ b/packages/components/table/src/store/expand.ts @@ -32,7 +32,15 @@ function useExpand(watcherData: WatcherPropsData) { } const toggleRowExpansion = (row: T, expanded?: boolean) => { - const changed = toggleRowStatus(expandRows.value, row, expanded) + const changed = toggleRowStatus( + expandRows.value, + row, + expanded, + undefined, + undefined, + undefined, + watcherData.rowKey.value + ) if (changed) { instance.emit('expand-change', row, expandRows.value.slice()) } diff --git a/packages/components/table/src/store/watcher.ts b/packages/components/table/src/store/watcher.ts index 97b8c934c4..aec03d0cbc 100644 --- a/packages/components/table/src/store/watcher.ts +++ b/packages/components/table/src/store/watcher.ts @@ -237,7 +237,8 @@ function useWatcher() { selected, treeProps, ignoreSelectable ? undefined : selectable.value, - data.value.indexOf(row) + data.value.indexOf(row), + rowKey.value ) if (changed) { const newSelection = (selection.value || []).slice() @@ -275,7 +276,8 @@ function useWatcher() { value, treeProps, selectable.value, - rowIndex + rowIndex, + rowKey ) ) { selectionChanged = true diff --git a/packages/components/table/src/util.ts b/packages/components/table/src/util.ts index ab028b2f78..8dddba7a11 100644 --- a/packages/components/table/src/util.ts +++ b/packages/components/table/src/util.ts @@ -292,11 +292,24 @@ export function toggleRowStatus( newVal?: boolean, tableTreeProps?: TreeProps, selectable?: (row: T, index?: number) => boolean, - rowIndex?: number + rowIndex?: number, + rowKey?: string ): boolean { let _rowIndex = rowIndex ?? 0 let changed = false - const index = statusArr.indexOf(row) + + const getIndex = () => { + if (!rowKey) { + return statusArr.indexOf(row) + } + + const id = getRowIdentity(row, rowKey) + + return statusArr.findIndex((item) => getRowIdentity(item, rowKey) === id) + } + + const index = getIndex() + const included = index !== -1 const isRowSelectable = selectable?.call(null, row, _rowIndex) @@ -344,7 +357,8 @@ export function toggleRowStatus( newVal ?? !included, tableTreeProps, selectable, - _rowIndex + 1 + _rowIndex + 1, + rowKey ) _rowIndex += getChildrenCount(item) + 1 if (childChanged) {