【已解决】el-table 前端分页多选、跨页全选等

由于element-ui的自定义header一直不生效(当type未selection时slot / rende-header 都不生效),所以还是使用element-ui原生API去做。

示例

在这里插入图片描述

代码

el-table row-key 属性是必要的。

<template>
  <div>
    <el-table
      ref="table"
      :data="tableDataFilter"
      style="width: 100%"
      row-key="id"
      height="500"
      @select="handleSelect"
      @select-all="handleSelectAll">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column type="index" width="50" label="序号" />
      <el-table-column prop="name" label="姓名" width="120" />
      <el-table-column prop="age" label="年龄" width="80" sortable />
      <el-table-column prop="email" label="邮箱" min-width="180" />
      <el-table-column prop="city" label="城市" width="100" />
      <el-table-column prop="occupation" label="职业" width="150" />
      <el-table-column prop="joinDate" label="加入日期" width="120" />
    </el-table>

    <el-pagination
    :current-page.sync="currentPage"
    :page-sizes="[10, 20, 50]"
    :page-size.sync="pageSize"
    layout="total, sizes, prev, pager, next, jumper"
    :total="total"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
    >
    </el-pagination>
  </div>
</template>

<script>
export default {
  data() {
    return {
    		// 存放完整数据
      tableData: [],
      multipleSelection: [],
      currentPage: 1,
      pageSize: 10,

      /*
        选择Set 是因为在这个场景使用起来比较方便,也能优化些许性能。
        如果需要使用watch来监听selectedIds 的变化,可以改用为数组。
        如改用数组 select等方法中的判断就需要相对繁琐。
        或是对Set 进行包装。
      */ 
      selectedIds: new Set(),
      isSelectAll: false
    };
  },

  computed: {
    total() {
      return this.tableData.length
    },

    // 前端分页
    tableDataFilter() {
      const s = (this.currentPage - 1) * this.pageSize,
      e = s + this.pageSize
      return this.tableData.slice(s, e)
    }
  },

  // 默认全选;如果有需要;此代码可以放在接口调用数据赋值后
  async mounted() {
    await this.$nextTick()
    this.handleSelectAll()
  },

  methods: {
    // 当勾选状态改变做后续的逻辑处理
    change() {
      // 已选中列表
      const selectList = this.tableData.filter(x => this.selectedIds.has(x.id))
      // do something...
    },

    // 勾选/取消单行
    handleSelect(selection, row) {
      if (this.selectedIds.has(row.id)) {
        this.selectedIds.delete(row.id)
      } else {
        this.selectedIds.add(row.id)
      }

      this.checkStatus()
      // 因为 Set 数据结构 不能够被Vue所监听到
      // 所以在每次修改选中状态之后手动调用一次callback
      this.change()
    },
    checkStatus() {
      this.isSelectAll = this.selectedIds.size === this.total
    },
    // 全选事件
    handleSelectAll() {
      // 当前是全选状态 -> 全部取消
      if (this.isSelectAll) {
        this.selectedIds.clear()
        this.$refs.table?.clearSelection?.()
      } else {
        this.tableData.forEach(x => {
          this.selectedIds.add(x.id)
          // ?. 在每次调用接口会清楚选中状态;防止在created中调用接口时table dom还未挂载。
          this.$refs.table?.toggleRowSelection?.(x, true)
        })
      }
      this.checkStatus()
      this.change()
    },
    handleSizeChange() {
      this.setSelection()
    },
    handleCurrentChange() {
      this.setSelection()
    },
    // 分页切换 -> 处理当前页面勾选状态
    async setSelection() {
      await this.$nextTick()
      const tableRef = this.$refs.table
      if (!tableRef) return
      this.tableData.forEach(x => {
        tableRef.toggleRowSelection(x, this.selectedIds.has(x.id))
      })
    },
  },
};
</script>
<think> 我们参考之前的对话历史和引用的内容,来回答用户关于el-table分页全选功能的问题。 根据引用[1][3][4],我们知道实现全选需要以下步骤: 1. 设置el-table的row-key属性,确保每一行有唯一标识(如id)。 2. 在el-table-column的selection列中设置reserve-selection为true,以保留中的状态。 3. 处理selection-change事件,用于收集中的数据。 4. 实现全选功能时,需要维护一个数组(比如unSelectedIds)来记录手动反的数据。当点击全选按钮时,实际上是中除了unSelectedIds以外的所有数据。 但是,由于数据量可能很大,不能一次性加载所有数据,所以不能直接通过前端数据全选。因此,我们需要通过后台配合,记录中的状态,或者通过记录反的数据来实现。 具体实现思路如下: - 当用户点击“全选”按钮时,我们设置一个全选状态(例如selectAllFlag=true),并清空当前中的数组(selectedList),同时将之前手动反的数据(unSelectedIds)置空。然后,我们向后端请求当前的数据,并设置当前中状态(注意:当前的数据中,如果有在unSelectedIds中的,则不中)。 - 当用户切换分页时,根据selectAllFlag和unSelectedIds来设置当前中状态:如果selectAllFlag为true,则当前的数据中不在unSelectedIds的项都应该被中。 - 当用户手动反某一行时,如果当前处于全选状态(selectAllFlag=true),则将该行的id添加到unSelectedIds中,并将该行从中状态移除;如果不在全选状态,则按照普通的单处理。 - 当用户手动中某一行时,如果当前处于全选状态,实际上该行已经被中,所以这里不需要额外处理;如果不是全选状态,则添加到中数组。 此外,引用[1]提到,为了避免频繁请求接口导致的性能问题,我们可以添加loading和disabled效果。 下面我们结合代码示例来具体说明。 步骤1:设置el-table ```vue <el-table ref="multipleTable" :data="tableData" style="width: 100%" @selection-change="handleSelectionChange" row-key="id" > <el-table-column type="selection" width="55" :reserve-selection="true" /> <!-- 其他列 --> </el-table> ``` 步骤2:定义相关数据 ```javascript data() { return { tableData: [], // 当前数据 pageNum: 1, // 当前码 pageSize: 10, // 每条数 total: 0, // 总条数 selectAllFlag: false, // 是否全选全选所有) unSelectedIds: [], // 在全选状态下,手动取消择的id集合 selectedList: [], // 当前中的行(包括中的) loading: false // 加载状态 } } ``` 步骤3:全选按钮 ```vue <el-button @click="toggleSelectAll">全选</el-button> ``` 步骤4:全选方法 ```javascript methods: { toggleSelectAll() { if (this.selectAllFlag) { // 如果已经是全选状态,则取消全选 this.selectAllFlag = false; this.unSelectedIds = []; this.selectedList = []; // 取消表格所有中 this.$refs.multipleTable.clearSelection(); } else { // 设置全选状态 this.selectAllFlag = true; this.unSelectedIds = []; // 清空反列表 // 这里需要将当前的数据中,手动取消中的先过滤掉(初始时unSelectedIds为空,所以当前会全部中) this.setCurrentPageSelected(); // 注意:这里我们只处理了当前,其他的数据在切换分页时会通过reserve-selection和下面的方法自动处理 } }, // 设置当前中状态 setCurrentPageSelected() { if (this.selectAllFlag) { // 全选状态下,当前的数据中,不在unSelectedIds中的行都要中 this.$nextTick(() => { this.tableData.forEach(row => { if (!this.unSelectedIds.includes(row.id)) { this.$refs.multipleTable.toggleRowSelection(row, true); } else { this.$refs.multipleTable.toggleRowSelection(row, false); } }); }); } }, // 处理择变化 handleSelectionChange(selection) { // 注意:selection是当前的所有中行(不包括之前中行,因为reserve-selection虽然保留了中的行,但是在selection-change事件中,当前切换时,事件触发只包含当前中行) // 因此,我们需要重新计算中的列表。 // 在全选状态下,我们需要更新selectedList:selectedList = 所有数据(除了unSelectedIds中的) // 但是,我们并不知道所有数据,所以这里我们只记录当前的变化,并且更新unSelectedIds。 if (this.selectAllFlag) { // 在全选状态下,当前的selection变化都是由于手动取消中或手动中(之前取消的又回来)造成的。 // 当前的所有行id const currentPageIds = this.tableData.map(row => row.id); // 当前应该被中的行id(即不在unSelectedIds中的) const shouldSelectedIds = currentPageIds.filter(id => !this.unSelectedIds.includes(id)); // 实际中的行id const selectedIds = selection.map(row => row.id); // 找出当前中应该中但未被中的(即用户取消中的) const unSelectedInPage = shouldSelectedIds.filter(id => !selectedIds.includes(id)); // 找出当前中不应该中却被中的(即用户重新中的,之前是取消的,现在又中了) const reSelectedInPage = selectedIds.filter(id => this.unSelectedIds.includes(id)); // 更新unSelectedIds:将当前新取消的id添加进去,将当前重新中的id从unSelectedIds中剔除 this.unSelectedIds = [ ...this.unSelectedIds.filter(id => !reSelectedInPage.includes(id) && !currentPageIds.includes(id)), ...unSelectedInPage ]; // 由于全选状态,所以selectedList理论上应该是所有数据(除了unSelectedIds中的),但我们不知道所有数据,所以这里selectedList我们不用来存储所有中的数据,而是用来存储当前中的?实际上,我们不需要维护一个完整的selectedList,因为中的状态由selectAllFlag和unSelectedIds就可以确定:中的是除去unSelectedIds的所有数据。 // 但是,如果我们需要在界面上展示中的数量,或者需要提交中的id,我们可以通过selectAllFlag和unSelectedIds以及总条数来计算: // 中的数量 = 总条数 - unSelectedIds.length // 这里我们可以重新设置selectedList为当前selection(因为中的状态由reserve-selection维护,但事件只触发当前,所以我们不能直接使用selection作为全部中数据) // 实际上,我们并不需要selectedList来存储所有中的数据,因为数据量太大。所以我们通常只记录取消中的id,然后提交给后端时,告诉后端:全选,但排除这些id。 } else { // 非全选状态,直接使用selection作为中的行 this.selectedList = selection; } }, // 加载数据的方法(分页改变时调用) fetchData() { this.loading = true; // 调用接口获取数据 // 假设api.getData返回一个Promise api.getData({ pageNum: this.pageNum, pageSize: this.pageSize }).then(res => { this.tableData = res.list; this.total = res.total; this.loading = false; // 数据加载完成后,设置当前中状态 this.setCurrentPageSelected(); }).catch(() => { this.loading = false; }); } } ``` 注意:上面的实现中,我们在全选状态下,通过unSelectedIds来记录取消中的id。这样,当用户点击全选后,我们实际上知道用户排除了哪些数据。在提交时,我们可以这样处理: - 如果是全选状态,那么需要提交的参数是:{ selectAll: true, excludeIds: this.unSelectedIds } - 如果不是全选状态,那么提交中的id数组:{ selectAll: false, selectedIds: this.selectedList.map(row => row.id) } 另外,当分页改变时,我们需要重新设置当前中状态(调用setCurrentPageSelected)。因此,在fetchData方法中,数据获取后需要调用this.setCurrentPageSelected()。 关于性能:引用[1]提到,全选会触发接口查询数据,所以我们在分页切换时也会请求接口,但这是正常的分页请求。而全选按钮的点击并不会请求接口,只是改变了状态,然后设置当前中状态。所以性能问题主要在于分页请求数据的速度。我们只需要在分页请求时添加loading效果,避免用户频繁操作。 引用[2]提到了使用滚动加载,但用户问题主要是分页全选,所以我们这里以分页为例。 总结:通过以上步骤,我们实现了全选功能,同时保留了el-table原有的单和整全选功能。 下面我们根据要求生成相关问题: </think> ### el-table实现分页全选功能的完整方法 实现el-table分页全选功能,需要解决数据保持中状态的问题。以下是完整实现步骤(基于Vue 2/3 + Element UI): #### 1. 基础表格配置 ```vue <template> <el-table ref="multipleTable" :data="tableData" row-key="id" // 关键:设置行唯一标识 @selection-change="handleSelectionChange" @select="handleSelect" > <el-table-column type="selection" width="55" :reserve-selection="true" // 关键:保留中 /> <!-- 其他列 --> </el-table> <el-pagination @current-change="handlePageChange" :total="total" /> <el-button @click="toggleSelectAll">全选</el-button> </template> ``` #### 2. 核心数据与方法实现 ```javascript export default { data() { return { tableData: [], // 当前数据 total: 0, // 总数据量 pageSize: 10, // 每条数 currentPage: 1, // 当前码 selectedList: [], // 所有中数据 selectAllFlag: false, // 全选状态标识 unSelectedIds: [], // 排除的ID集合 loading: false // 加载状态 } }, methods: { // 加载数据 async loadData() { this.loading = true; const res = await api.getData({ page: this.currentPage, size: this.pageSize }); this.tableData = res.list; this.total = res.total; this.loading = false; this.setPageSelection(); // 设置当前中状态 }, // 设置当前中状态 setPageSelection() { this.$nextTick(() => { this.tableData.forEach(row => { if(this.selectAllFlag) { // 全选状态下排除指定ID const isSelected = !this.unSelectedIds.includes(row.id); this.$refs.multipleTable.toggleRowSelection(row, isSelected); } else { // 非全选状态根据selectedList设置 const isSelected = this.selectedList.some(item => item.id === row.id); this.$refs.multipleTable.toggleRowSelection(row, isSelected); } }); }); }, // 全选切换 toggleSelectAll() { if(this.selectAllFlag) { // 取消全选 this.selectAllFlag = false; this.unSelectedIds = []; this.selectedList = []; } else { // 启用全选 this.selectAllFlag = true; this.unSelectedIds = []; this.selectedList = []; } this.setPageSelection(); }, // 处理单操作 handleSelect(selection, row) { if(this.selectAllFlag) { // 全选状态下的手动反 if(!selection.includes(row)) { this.unSelectedIds.push(row.id); } else { // 移出排除列表 this.unSelectedIds = this.unSelectedIds.filter(id => id !== row.id); } } else { // 普通中状态 this.selectedList = selection; } }, // 分页切换 handlePageChange(page) { this.currentPage = page; this.loadData(); }, // 获取最终择结果 getSelectedIds() { if(this.selectAllFlag) { return { allSelected: true, excludeIds: this.unSelectedIds }; } else { return { allSelected: false, selectedIds: this.selectedList.map(item => item.id) }; } } } } ``` #### 3. 关键点说明 1. **`row-key`和`reserve-selection`** 必须同时设置这两个属性,确保时能跟踪每行中状态 2. **双模式数据存储** - `selectAllFlag=true`:记录排除项(`unSelectedIds`) - `selectAllFlag=false`:记录中项(`selectedList`) 3. **性能优化** - 添加`loading`状态防止重复请求 - 使用`$nextTick`确保DOM更新后操作 - 批量操作时避免渲染整个列表 4. **数据提交** 通过`getSelectedIds()`方法获取两种模式下的中数据,后端根据`allSelected`标志处理 #### 4. 注意事项 1. 后端API需支持排除ID查询(当`selectAllFlag=true`时) 2. 大数据量场景建议添加防抖操作 3. 使用`el-table-infinite-scroll`可实现滚动加载替代分页[^2]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值