el-table 树形懒加载 全选和取消全选
时间: 2025-07-22 20:15:34 浏览: 5
在使用 Element UI 的 `el-table` 组件时,实现树形结构懒加载情况下的全选和取消全选功能,需要结合 `row-key`、`tree-props` 以及手动管理选中状态。由于 `el-table` 原生的 `@select-all` 和 `@select` 事件在懒加载模式下无法自动处理深层嵌套或未加载的数据,因此必须通过自定义逻辑来控制节点的选中状态。
### 实现思路
1. **启用懒加载与树形结构**
使用 `lazy` 属性开启懒加载,并通过 `load` 方法动态加载子级数据。同时,设置 `tree-props` 指定子节点字段名,通常为 `children`。
2. **维护一个选中状态数组**
在组件内部维护一个 `selectedRows` 数组,用于保存当前所有已选中的节点对象。每当用户点击行或触发全选操作时,更新该数组。
3. **实现全选/反选功能**
全选操作需要递归遍历当前表格渲染的所有节点(包括已展开且加载的子节点),并根据是否禁用选择进行选中或取消选中。此过程需跳过被标记为不可选的节点。
4. **处理父子节点联动**
当父节点被选中时,应同步选中其所有子节点;若子节点全部选中,则父节点自动变为选中状态;若部分选中,则父节点为半选状态。反之亦然,这需要编写递归函数进行状态传播[^4]。
5. **响应表头全选事件**
监听 `@select-all` 事件,在事件处理函数中判断当前是全选还是反选,并调用相应的处理方法,例如 `toggleAllSelection`。
6. **支持分页与异步加载**
如果表格支持分页,每次切换页面时应清空之前的选中状态,避免跨页误选。对于懒加载节点,在加载完成后也应检查是否应自动选中。
### 示例代码
以下是一个基于 Vue 2 或 Vue 3(Element Plus)的简化示例代码:
```vue
<template>
<el-table
ref="table"
:data="tableData"
row-key="id"
lazy
:load="loadNode"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
@select-all="handleSelectAll"
@select="handleSelect"
>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column prop="name" label="名称"></el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [], // 初始数据
selectedRows: [],
allSelected: false,
};
},
methods: {
loadNode(row, treeNode, resolve) {
// 模拟异步加载子节点
setTimeout(() => {
const children = this.fetchChildren(row.id);
this.$set(row, 'children', children);
resolve(children);
}, 500);
},
fetchChildren(id) {
// 模拟从接口获取子节点数据
return [
{ id: `${id}-1`, name: `子节点1-${id}` },
{ id: `${id}-2`, name: `子节点2-${id}` }
];
},
handleSelect(rows, row) {
// 处理单个行勾选变化
this.updateSelectionState(row, rows.includes(row));
},
handleSelectAll(rows) {
// 处理表头全选事件
this.allSelected = rows.length > 0;
this.toggleAllSelection(rows);
},
toggleAllSelection(selected) {
const flatNodes = this.flattenTree(this.tableData);
flatNodes.forEach(node => {
if (!node.disabled) {
node.checked = selected;
}
});
this.selectedRows = selected ? [...flatNodes.filter(n => !n.disabled)] : [];
},
updateSelectionState(node, isSelected) {
node.checked = isSelected;
this.propagateSelectionUp(node);
this.propagateSelectionDown(node, isSelected);
this.updateAllSelectedStatus();
},
propagateSelectionDown(node, isSelected) {
if (node.children && node.children.length) {
node.children.forEach(child => {
if (!child.disabled) {
child.checked = isSelected;
this.propagateSelectionDown(child, isSelected);
}
});
}
},
propagateSelectionUp(node) {
let parent = this.findParentNode(this.tableData, node.id);
while (parent) {
const allChildrenChecked = parent.children.every(c => c.checked || c.disabled);
const someChildrenChecked = parent.children.some(c => c.checked);
parent.checked = allChildrenChecked;
parent.indeterminate = someChildrenChecked && !allChildrenChecked;
parent = this.findParentNode(this.tableData, parent.id);
}
},
findParentNode(data, targetId, parent = null) {
for (const node of data) {
if (node.id === targetId) {
return parent;
}
if (node.children) {
const result = this.findParentNode(node.children, targetId, node);
if (result) return result;
}
}
return null;
},
flattenTree(data) {
let result = [];
data.forEach(node => {
result.push(node);
if (node.children) {
result = result.concat(this.flattenTree(node.children));
}
});
return result;
},
updateAllSelectedStatus() {
const flatNodes = this.flattenTree(this.tableData);
const allChecked = flatNodes.every(n => n.checked || n.disabled);
const someChecked = flatNodes.some(n => n.checked);
this.allSelected = allChecked;
this.$refs.table.clearSelection();
if (someChecked && !allChecked) {
this.$refs.table.toggleAllSelection(); // 触发半选状态
}
}
}
};
</script>
```
### 注意事项
- 在实际项目中,可能需要引入 `lodash` 或类似工具库简化递归查找和状态更新。
- 若使用 `element-plus`(Vue 3 版本),确保 `ref` 和响应式数据使用 `setup()` 或 Composition API 正确绑定。
- 对于大型树结构,建议优化性能,避免频繁递归导致卡顿。
- 分页场景下,应将每页的选中状态独立保存,并提供“全选所有页”的扩展功能。
阅读全文
相关推荐


















