fix(components): [table] prevent child node selection loss during table data modification (#20322)

* fix(components): [table] child selection loss during data modification

* refactor: replace logical AND (&&) with explicit if statement

* test: use unique id
This commit is contained in:
comp-squirrel
2025-04-09 17:14:55 +08:00
committed by GitHub
parent ee13188cca
commit 1ee7fa7e7d
3 changed files with 102 additions and 3 deletions

View File

@@ -338,6 +338,92 @@ describe('table column', () => {
expect(expectIndexs).toEqual(actualIndexs)
expect(wrapper.vm.selected.length).toBe(wrapper.vm.testData.length)
})
// #20292
it('Maintain child selection states during data updates', async () => {
const wrapper = mount({
components: {
ElTable,
ElTableColumn,
},
template: `
<el-table ref="tableRef" :data="testData" row-key="id" default-expand-all>
<el-table-column type="selection" />
<el-table-column prop="desc" />
</el-table>
`,
data() {
return {
testData: [
{ id: 0, desc: 'record 1' },
{ id: 1, desc: 'record 2' },
{
id: 2,
desc: 'record 3',
children: [
{ id: 3, desc: 'record 3-1' },
{ id: 4, desc: 'record 3-2' },
],
},
],
}
},
methods: {
addRow() {
this.testData.push({ id: 5, desc: 'record 4' })
},
removeRow() {
this.testData.pop()
},
removeChildRow() {
this.testData[2].children.pop()
},
},
})
await doubleWait()
const getSelection = wrapper.vm.$refs.tableRef.getSelectionRows
const domArr = wrapper.findAll('.el-table__row .el-checkbox')
let selectionKeys = []
domArr.forEach(
(checkbox, index) => index < 3 && checkbox.trigger('click')
)
await doubleWait()
selectionKeys = getSelection().map((item) => item.id)
expect(selectionKeys).toEqual([0, 1, 2, 3, 4])
// add
wrapper.vm.addRow()
await doubleWait()
selectionKeys = getSelection().map((item) => item.id)
expect(selectionKeys).toEqual([0, 1, 2, 3, 4])
// remove
wrapper.vm.removeRow()
await doubleWait()
selectionKeys = getSelection().map((item) => item.id)
expect(selectionKeys).toEqual([0, 1, 2, 3, 4])
// remove child
wrapper.vm.removeChildRow()
await doubleWait()
selectionKeys = getSelection().map((item) => item.id)
expect(selectionKeys).toEqual([0, 1, 2, 3])
// remove with child
wrapper.vm.removeRow()
await doubleWait()
selectionKeys = getSelection().map((item) => item.id)
expect(selectionKeys).toEqual([0, 1])
})
})
describe('= index', () => {

View File

@@ -193,7 +193,8 @@ function useWatcher<T>() {
let deleted
if (rowKey.value) {
deleted = []
const dataMap = getKeysMap(data.value, rowKey.value)
const childrenKey = instance?.store?.states?.childrenColumnName.value
const dataMap = getKeysMap(data.value, rowKey.value, true, childrenKey)
for (const key in selectedMap.value) {
if (hasOwn(selectedMap.value, key) && !dataMap[key]) {
deleted.push(selectedMap.value[key].row)

View File

@@ -196,12 +196,24 @@ export const getRowIdentity = <T>(
export const getKeysMap = function <T>(
array: T[],
rowKey: string
rowKey: string,
flatten = false,
childrenKey = 'children'
): Record<string, { row: T; index: number }> {
const data = array || []
const arrayMap = {}
;(array || []).forEach((row, index) => {
data.forEach((row, index) => {
arrayMap[getRowIdentity(row, rowKey)] = { row, index }
if (flatten) {
const children = row[childrenKey]
if (isArray(children)) {
Object.assign(arrayMap, getKeysMap(children, rowKey, true, childrenKey))
}
}
})
return arrayMap
}