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) {