解决 el-table 过滤数据和重置、分页后数据勾选回显问题

571 阅读4分钟

前言

在前端开发中,数据表格的多选管理是一项常见的需求,例如在数据筛选、批量操作等场景下,如何正确管理选中项显得尤为重要。

在网络上搜索 el-table 勾选回显的解决方案时,千篇一律的都是分页回显,并没有数据过滤及重置操作后的数据勾选回显实现。

本文将基于 Vue 3 和 Element Plus,详细讲解如何实现一个支持查询、重置、分页,并且能够正确管理选中项的表格

预览

动画.gif

1. 功能实现

  • 多选与单选管理:实现选中的数据维护,确保在分页或查询数据后,选中状态依然保持不变。
  • 勾选回显:确保在切换分页或查询数据后,已选中的项依然能够正确回显。

2. 组件模板结构

<template>
  <el-button @click="search">查询</el-button>
  <el-button @click="reset">重置</el-button>
  <div style="display: flex">
    <div style="width: 50%;height: 500px">
      <el-table :data="tableData" ref="tableRef" height="100%" @select="nodeSelect" @select-all="selectAll">
        <el-table-column type="selection" width="55"/>
        <el-table-column property="id" label="ID"/>
      </el-table>
      <el-pagination v-model:current-page="page" v-model:page-size="pageSize" @current-change="pageChange"
                     :total="total"></el-pagination>
    </div>
    <el-table :data="selectedList" height="500px" style="width: 50%">
      <el-table-column type="selection" width="55"/>
      <el-table-column property="id" label="ID"/>
      <el-table-column label="操作">
        <template #default="{row, $index}">
          <el-button type="danger" link @click="remove(row,$index)">移除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

3. 核心逻辑实现

3.1 数据初始化

const tableData = ref([]);
const selectedList = ref([]);
const page = ref(1);
const tableRef = ref(null);
const pageSize = ref(10);
const total = ref(30);

let saveId = new Set(); // 记录选中的 ID
let staticData = [];
let basicData = Array.from({length: 30}, (_, i) => ({ id: i }));

数据初始化时,tableData 存储表格数据,selectedList 存储已选中的数据,saveId 用于跟踪选中的 ID,避免分页和查询数据时丢失选中项。

3.2 模拟查询与重置功能

function search() {
  staticData = [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}, {"id": 7}, {"id": 8}, {"id": 29}, {"id": 10}, {"id": 11}, {"id": 12}, {"id": 13}, {"id": 25}];
  total.value = staticData.length;
  tableData.value = staticData;
  pageChange();
  setCheck();
}

function reset() {
  staticData = basicData;
  total.value = staticData.length;
  tableData.value = staticData;
  pageChange();
  setCheck();
}

查询功能会筛选部分数据,重置功能则恢复所有数据。

3.3 模拟分页

function pageChange() {
  tableData.value = [];
  let count = page.value * pageSize.value;
  let num = (page.value * pageSize.value) - pageSize.value;
  for (let i = num; i < count; i++) {
    if (staticData[i]) tableData.value.push(staticData[i]);
  }
  setCheck();
}

3.4 选中状态管理

function nodeSelect(selection, node) {
  node.checked = !node.checked;
  if (node.checked) {
    saveId.add(node.id);
    selectedList.value.push(node);
  } else {
    saveId.delete(node.id);
    selectedList.value = selectedList.value.filter(item => item.id !== node.id);
  }
}

3.5 全选功能

function selectAll(selection) {
  let setData = new Set();
  if (!selection.length) {
    tableData.value.forEach(item => {
      item.checked = false;
      saveId.delete(item.id);
      setData.add(item.id);
    })
    let arr = []
    selectedList.value.forEach(item => {
      if (!setData.has(item.id)) {
        item.checked = true
        arr.push(item);
      }
    })
    selectedList.value = arr
    return
  }

  setData = new Set(selectedList.value.map(item => item.id));

  selection.forEach(item => {
    item.checked = true;
    saveId.add(item.id);
    if (saveId.has(item.id) && !setData.has(item.id)) {
      selectedList.value.push(item);
    }
  })
}

3.6 维护勾选回显

function setCheck() {
  nextTick(() => {
    tableData.value.forEach(item => {
      if (saveId.has(item.id)) {
        item.checked = true;
        tableRef.value.toggleRowSelection(item, true);
      }
    });
  });
}

setCheck() 的作用是在查询、重置或分页切换后,确保之前选中的数据仍然保持选中状态。

3.7 移除选中项

function remove(row, i) {
  selectedList.value.splice(i, 1);
  saveId.delete(row.id);
  for (let item of tableData.value) {
    if (item.id === row.id) {
      saveId.delete(row.id);
      tableRef.value.toggleRowSelection(item, false);
      break;
    }
  }
}

4. 源码

<template>
  <el-button @click="search">查询</el-button>
  <el-button @click="reset">重置</el-button>
  <div style="display: flex">
    <div style="width: 50%;height: 500px">
      <el-table :data="tableData" ref="tableRef" height="100%" @select="nodeSelect" @select-all="selectAll">
        <el-table-column type="selection" width="55"/>
        <el-table-column property="id" label="ID"/>
      </el-table>
      <el-pagination v-model:current-page="page" v-model:page-size="pageSize" @current-change="pageChange"
                     :total="total"></el-pagination>
    </div>
    <el-table :data="selectedList" height="500px" style="width: 50%">
      <el-table-column type="selection" width="55"/>
      <el-table-column property="id" label="ID"/>
      <el-table-column label="操作">
        <template #default="{row, $index}">
          <el-button type="danger" link @click="remove(row,$index)">移除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script setup>
import {nextTick, ref} from "vue";

const tableData = ref([])      // 表格数据
const selectedList = ref([]);  // 选中列表数据
const page = ref(1);
const tableRef = ref(null);
const pageSize = ref(10);
const total = ref(30);

let saveId = new Set(); // 保存选中 ID 的集合
let staticData = [] // 静态数据,用于存储查询或重置后的数据
let basicData = [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}, {"id": 7}, {"id": 8}, {"id": 9}, {"id": 10}, {"id": 11}, {"id": 12}, {"id": 13}, {"id": 14}, {"id": 15}, {"id": 16}, {"id": 17}, {"id": 18}, {"id": 19}, {"id": 20}, {"id": 21}, {"id": 22}, {"id": 23}, {"id": 24}, {"id": 25}, {"id": 26}, {"id": 27}, {"id": 28}, {"id": 29}]

reset()

/**
 * 切换节点的选中状态
 *
 * @param selection 选中状态管理工具
 * @param node 节点对象
 */
function nodeSelect(selection, node) {
  node.checked = !node.checked;

  if (node.checked) {
    saveId.add(node.id);
    selectedList.value.push(node);
  } else {
    let arr = [];
    saveId.delete(node.id);
    selectedList.value.forEach(item => {
      if (item.id !== node.id) {
        saveId.add(item.id);
        arr.push(item);
        item.checked = true;
      } else {
        item.checked = false;
      }
    });
    selectedList.value = arr;
  }
}

/**
 * 全选函数
 */
function selectAll(selection) {
  let setData = new Set();
  if (!selection.length) {
    tableData.value.forEach(item => {
      item.checked = false;
      saveId.delete(item.id);
      setData.add(item.id);
    })
    let arr = []
    selectedList.value.forEach(item => {
      if (!setData.has(item.id)) {
        item.checked = true
        arr.push(item);
      }
    })
    selectedList.value = arr
    return
  }

  setData = new Set(selectedList.value.map(item => item.id));

  selection.forEach(item => {
    item.checked = true;
    saveId.add(item.id);
    if (saveId.has(item.id) && !setData.has(item.id)) {
      selectedList.value.push(item);
    }
  })
}

/**
 * 模拟查询
 **/
function search() {
  staticData = [
    {"id": 0},
    {"id": 1},
    {"id": 2},
    {"id": 3},
    {"id": 4},
    {"id": 5},
    {"id": 6},
    {"id": 7},
    {"id": 8},
    {"id": 29},
    {"id": 10},
    {"id": 11},
    {"id": 12},
    {"id": 13},
    {"id": 25}
  ]
  total.value = staticData.length;
  tableData.value = staticData

  pageChange()
  setCheck()
}

/**
 * 模拟重置数据,重新渲染表格
 **/
function reset() {
  staticData = basicData
  total.value = staticData.length;
  tableData.value = staticData

  pageChange()
  setCheck()
}

/**
 * 模拟分页
 **/
function pageChange() {
  tableData.value = [];
  let count = page.value * pageSize.value;
  let num = (page.value * pageSize.value) - pageSize.value;
  for (let i = num; i < count; i++) {
    if (staticData[i]) tableData.value.push(staticData[i])
  }

  setCheck()
}

/**
 * 设置选中项
 *
 * 将符合条件的数据设置为选中状态。
 */
function setCheck() {
  nextTick(() => {
    tableData.value.forEach(item => {
      if (item && saveId.has(item.id)) {
        item.checked = true;
        tableRef.value.toggleRowSelection(item, true);
      }
    })
  })
}

/**
 * 从选中列表中移除指定行
 * @param row 要移除的行对象
 * @param i 要移除的行在选中列表中的索引
 */
function remove(row, i) {
  selectedList.value.splice(i, 1);
  saveId.delete(row.id);
  for (let item of tableData.value) {
    if (item.id === row.id) {
      saveId.delete(row.id);
      tableRef.value.toggleRowSelection(item, false);
      break;
    }
  }
}
</script>
<style scoped>

</style>

5. 总结

本文介绍了如何实现一个支持分页、查询、重置、全选、单选回显勾选功能。核心难点在于如何正确管理选中项,使其在查询后仍然保持状态。希望本文对你有所帮助!