问题背景
在使用 Element-Plus/Element-UI
的分页表格(或者其他表格组件)时,默认的多选功能无法跨页保持选中状态。当切换分页请求新数据后,之前选中的数据会被清空。
之前遇到这个问题搜了挺多网上的文章,看着代码又多又复杂,下面是很简单的实现方法,主要代码就十几行
。
先贴效果

核心思路
- 全局选中的ID集合 :维护一个全局的
全局ID集合(Set)
存储所有选中的数据ID
。 - 数据同步 :在操作
选中
、取消选中
、全选
、取消全选
时同步全局ID集合
- 状态回显 :在分页变化,每次加载新数据后,如果数据(当前页的数据数组)的
ID
存在于全局ID集合
就回显选中项,让对应的行数据选中。
关键代码解析
1. 全局状态定义
js
const globalSelectedIds = ref(new Set()); // 全局选中id
const isPageChanging = ref(false); // 标记是否正在切换分页
2. 请求接口数据加载与表格状态回显
js
const getList = async () => {
isPageChanging.value = true;
let res = await mockApi({
page: currentPage.value,
pageSize: pageSize.value,
});
tableData.value = res;
// 数据更新后回显选中状态
nextTick(() => {
tableData.value.forEach((row) => {
tableRef.value?.toggleRowSelection(
row,
globalSelectedIds.value.has(row.id)
);
});
isPageChanging.value = false;
});
};
3. 选择状态变更处理 (核心代码)
js
const handleSelectionChange = (selection) => {
// // 如果是请求数据后回显表格选中状态导致的变化事件,则不处理
if (isPageChanging.value) return;
const currentPageIds = tableData.value.map((row) => row.id);
// 先移除当前页所有ID
currentPageIds.forEach((id) => globalSelectedIds.value.delete(id));
// 再添加当前选中的ID
selection.forEach((row) => globalSelectedIds.value.add(row.id));
};
4. 从全局移除选中项(可选,仅演示点击标签删除选中数据使用)
js
const removeSelected = (id) => {
globalSelectedIds.value.delete(id);
const row = tableData.value.find((row) => row.id === id);
row && tableRef.value?.toggleRowSelection(row, false);
};
完整实现方案代码 (vue3 + element plus)
html
<template>
<div>
<!-- 表格 -->
<el-table
:data="tableData"
@selection-change="handleSelectionChange"
ref="tableRef"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="姓名" />
</el-table>
<el-pagination
class="mt-4"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 10, 20]"
:total="100"
layout="total, sizes, prev, pager, next"
@size-change="handlePageSizeChange"
@current-change="handlePageChange"
/>
<!-- 展示全局选中的数据id -->
<div>
<h4>全局选中数据 ({{ globalSelectedIds.size }}个)</h4>
<div v-if="globalSelectedIds.size > 0">
<el-tag
v-for="id in Array.from(globalSelectedIds)"
:key="id"
closable
@close="removeSelected(id)"
class="mr-2 mb-2"
>
ID: {{ id }}
</el-tag>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from "vue";
const currentPage = ref(1);
const pageSize = ref(5);
const tableRef = ref();
const tableData = ref([]);
const globalSelectedIds = ref(new Set()); // 全局选中id
const isPageChanging = ref(false); // 标记是否正在切换分页
// 模拟从接口获取数据
const mockApi = ({ page, pageSize }) => {
const data = [];
const startIndex = (page - 1) * pageSize;
for (let i = 1; i <= pageSize; i++) {
const id = startIndex + i;
data.push({
id: id,
name: `用户${id}`,
});
}
return new Promise((resolve) => {
setTimeout(() => {
resolve(data);
}, 100);
});
};
// 获取列表数据
const getList = async () => {
isPageChanging.value = true;
let res = await mockApi({
page: currentPage.value,
pageSize: pageSize.value,
});
tableData.value = res;
// 数据更新后回显选中状态
nextTick(() => {
tableData.value.forEach((row) => {
tableRef.value?.toggleRowSelection(
row,
globalSelectedIds.value.has(row.id)
);
});
isPageChanging.value = false;
});
};
// 统一处理表格选择变化
const handleSelectionChange = (selection) => {
// 如果是请求数据后回显表格选中状态导致的变化事件,则不处理
if (isPageChanging.value) return;
const currentPageIds = tableData.value.map((row) => row.id);
// 先移除当前页所有ID
currentPageIds.forEach((id) => globalSelectedIds.value.delete(id));
// 再添加当前选中的ID
selection.forEach((row) => globalSelectedIds.value.add(row.id));
};
// 从全局移除选中项,如果删除的ID在当前页,需要更新表格选中状态
const removeSelected = (id) => {
globalSelectedIds.value.delete(id);
const row = tableData.value.find((row) => row.id === id);
row && tableRef.value?.toggleRowSelection(row, false);
};
// 处理分页变化
const handlePageChange = () => {
getList();
};
// 处理分页数量变化
const handlePageSizeChange = () => {
currentPage.value = 1;
getList();
};
// 请求接口获取数据
getList();
</script>
总结
这个功能的实现其实挺简单的,核心就是维护一个全局的选中 ID 集合。每次用户勾选表格时,对比当前页的数据和全局 ID 集合来更新状态;翻页或者重新加载数据时,再根据这个集合自动回显勾选状态,保证选中项不会丢失。