Element 分页表格跨页多选状态保持方案(十几行代码解决)

问题背景

在使用 Element-Plus/Element-UI 的分页表格(或者其他表格组件)时,默认的多选功能无法跨页保持选中状态。当切换分页请求新数据后,之前选中的数据会被清空。

之前遇到这个问题搜了挺多网上的文章,看着代码又多又复杂,下面是很简单的实现方法,主要代码就十几行。

先贴效果

核心思路

  1. 全局选中的ID集合 :维护一个全局的全局ID集合(Set)存储所有选中的数据ID。
  2. 数据同步 :在操作选中、取消选中、全选、取消全选时同步全局ID集合
  3. 状态回显 :在分页变化,每次加载新数据后,如果数据(当前页的数据数组)的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 集合来更新状态;翻页或者重新加载数据时,再根据这个集合自动回显勾选状态,保证选中项不会丢失。

相关推荐
HSunR33 分钟前
ruoyi 若依 自定义注解 参数校验
java·前端·数据库
拆房老料2 小时前
ONLYOFFICE也能像Microsoft Word和WPS一样分别设置中西文字体
前端·html·word·开源软件·wps
Solis程序员2 小时前
Stripe 幂等表:把「不确定的重试」变成「查表返回」
前端·安全·bootstrap·php·agent
李纲明2 小时前
WordPress 站点变慢:按 TTFB → 缓存 → 查询 → 前端 分层排查(含配置注意)
前端·缓存·性能优化·wordpress·后端开发
程序人生8882 小时前
PDF 转 Word 版式错乱、表格丢失?DocConverter Web 格式互转引擎的保真实践
前端·人工智能·opencv·机器学习·pdf·word
粥里有勺糖3 小时前
安利一下最近用的桌面“Agent” | T3 Code
前端·github·ai编程
xy34533 小时前
Axure 9.0 动态面板基本操作
前端·ui·html·axure·原型·产品设计
不停喝水3 小时前
【前端转全栈java速通课】 项目实战④7-11节 操作数据库-登录-注册-修改密码-注销-mubatis-plus简化crud-
java·前端·数据库
一水鉴天4 小时前
统一对象拓扑(UOT)设计全链定稿:从三件事分离到三继承 之1 20261010(豆包“查看聊天内容”继续)
前端·css
栖凤4 小时前
Java 的 try-catch 在 Agent 里失效了,我用了 5 个模式才兜住
java·前端·python