Element UI 多选表格【翻页多选】全能版(含翻页多选数据反显、toggleRowSelection失效的原因解析和解决方案)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Element UI 多选表格【翻页多选】全能版(含翻页多选数据反显、toggleRowSelection失效的原因解析和解决方案)

效果预览

实现思路

data中定义 selectedList 数组保存选中的数据

在页面初次渲染、翻页、切换每页数据数量等每次重新获取表格数据渲染表格时,都根据 selectedList 勾选表格中已经选中的行

切换单行勾选状态时,判断是选中还是取消选中,选中则增加选中项,取消选中则移除选中项。

切换全选和取消全选时,根据当前页选中行的数据进行更新,若选中行已在 selectedList 中则不再重复添加,否则添加,对于其他非选中行若已在selectedList 中,则从 selectedList 中移除。

技术难点

1. 不能使用 selection-change 的原因

selection-change 的参数为当前页选中的所有行,在每次重新获取数据渲染表格时(如翻页时),参数为一个空数组,会导致历史选中的数据被清空!

即便使用 :reserve-selection="true" 来保留历史选中记录也无法达到预期效果。

2. toggleRowSelection失效的原因解析和解决方案

toggleRowSelection失效通常为以下两种情况:

(1)表格数据发生变化,表格还没渲染完便执行了toggleRowSelection

解决方案 : 在 $nextTick 中执行 toggleRowSelection

// toggleRowSelection 需在$nextTick中使用!
this.$nextTick(() => {
  this.$refs.multipleTable.toggleRowSelection(row);
});

(2)toggleRowSelection的第一个参数不是表格源数据

即便数据的值完全一样也会失效,因为数据为引用类型,必须地址一样。

解决方案 :toggleRowSelection的第一个参数通过在表格数据中通过 find() 查找得到。

  this.$nextTick(() => {
    this.$refs.multipleTable.toggleRowSelection(
      // 此处必须在 tableData 中查找对应的数据,否则 toggleRowSelection 会失效
      this.tableData.find((row) => {
        return row[this.UID] === item[this.UID];
      }),
      false
    );
  });

完整范例代码

<template>
  <div class="mainBox">
    <h3>已选择:</h3>
    <el-tag
      :key="item[UID]"
      v-for="(item, index) in selectedList"
      closable
      @close="removeItemByIndex(index, item)"
    >
      {{ item.name }}
    </el-tag>

    <el-table
      v-loading="loading"
      ref="multipleTable"
      :data="tableData"
      @select="selectChange"
      @select-all="selectAllChange"
    >
      <el-table-column type="selection" width="55" align="center">
      </el-table-column>
      <el-table-column prop="ID" label="编号" align="center"> </el-table-column>
      <el-table-column prop="name" label="姓名" align="center">
      </el-table-column>
      <el-table-column prop="age" label="年龄" align="center">
      </el-table-column>
    </el-table>
    <el-row type="flex" class="pageBanner" justify="center">
      <el-pagination
        background
        :total="total"
        :page-size="pageSize"
        @size-change="pageSizeChange"
        @current-change="currentPageChange"
        :current-page="currentPage"
        :page-sizes="[1, 2, 3]"
        layout="total, sizes, prev, pager, next, jumper"
      >
      </el-pagination>
    </el-row>
  </div>
</template>
<script>
export default {
  data() {
    return {
      // 唯一标识符
      UID: "ID",
      loading: false,
      total: 0,
      pageSize: 3,
      currentPage: 1,
      selectedList: [
        {
          ID: 1,
          name: "王小虎",
          age: 10,
        },
      ],
      tableData: [],
    };
  },

  mounted() {
    // 页面初始化时,首次加载数据
    this.getData(this.currentPage, this.pageSize);
  },

  methods: {
    // 单行前的勾选状态切换
    selectChange(selectedRows, row) {
      // true为选中, 0或false为取消选中
      let selected = selectedRows.length && selectedRows.indexOf(row) !== -1;
      if (selected) {
        this.addItem(row);
      } else {
        this.removeItem(row);
      }
    },
    // 全选/取消全选
    selectAllChange(selectedRows) {
      let selectedMarkList = this.selectedList.map((item) => item[this.UID]);
      // 当前页选中行的标记列表
      let pageSelectedMarkList = Array.isArray(selectedRows)
        ? selectedRows.map((item) => item[this.UID])
        : [];

      this.tableData.forEach((row) => {
        if (pageSelectedMarkList.includes(row[this.UID])) {
          if (!selectedMarkList.includes(row[this.UID])) {
            this.addItem(row);
          }
        } else if (selectedMarkList.includes(row[this.UID])) {
          this.removeItem(row);
        }
      });
    },
    // 切换每页显示条数
    pageSizeChange(newPageSize) {
      this.pageSize = newPageSize;
      this.getData(this.currentPage, this.pageSize);
    },
    // 切换页码--翻页
    currentPageChange(newPage) {
      this.currentPage = newPage;
      this.getData(this.currentPage, this.pageSize);
    },
    // 更新勾选标记
    updateMark() {
      let selectedMarkList = this.selectedList.map((item) => item[this.UID]);

      this.tableData.forEach((row) => {
        if (selectedMarkList.includes(row[this.UID])) {
          // toggleRowSelection 需在$nextTick中使用!
          this.$nextTick(() => {
            this.$refs.multipleTable.toggleRowSelection(row);
          });
        }
      });
    },

    // 模拟访问接口获取数据
    getData(page, pageSize) {
      this.loading = true;
      setTimeout(() => {
        let data = [
          {
            ID: 1,
            name: "王小虎",
            age: 10,
          },
          {
            ID: 2,
            name: "张三",
            age: 20,
          },
          {
            ID: 3,
            name: "李四",
            age: 30,
          },
          {
            ID: 4,
            name: "何香",
            age: 18,
          },
          {
            ID: 5,
            name: "刘刀",
            age: 27,
          },
          {
            ID: 6,
            name: "关胜",
            age: 33,
          },
          {
            ID: 7,
            name: "齐巧",
            age: 55,
          },
          {
            ID: 8,
            name: "卢一方",
            age: 45,
          },
          {
            ID: 9,
            name: "王兴海",
            age: 66,
          },
          {
            ID: 10,
            name: "全德",
            age: 100,
          },
        ];

        this.total = data.length;
        let startIndex = pageSize * (page - 1);
        let endIndex = pageSize * page;
        this.tableData = data.slice(startIndex, endIndex);
        this.updateMark();
        this.loading = false;
      }, 1000);
    },
    // 新增选中项
    addItem(item) {
      this.selectedList.push(item);
    },
    // 移除选中项
    removeItem(item) {
      for (let [index, itemTemp] of this.selectedList.entries()) {
        if (itemTemp[this.UID] === item[this.UID]) {
          this.removeItemByIndex(index);
          break;
        }
      }
    },
    // 根据下标移除选中项
    removeItemByIndex(index, item) {
      this.selectedList.splice(index, 1);

      // 若有item,则是点击标签上的关闭按钮,移除选中项
      if (item) {
        this.$nextTick(() => {
          this.$refs.multipleTable.toggleRowSelection(
            // 此处必须在 tableData 中查找对应的数据,否则 toggleRowSelection 会失效
            this.tableData.find((row) => {
              return row[this.UID] === item[this.UID];
            }),
            false
          );
        });
      }
    },
  },
};
</script>
<style  scoped>
.mainBox {
  margin: 30px;
}
.el-tag {
  margin: 10px;
}
.pageBanner {
  margin: 10px;
}
</style>

化为己用

若你的唯一标识符不是 ID,则修改 data() 中的UID 的值即可!

      // 唯一标识符
      UID: "ID",

目录
相关文章
|
3月前
|
API UED 容器
深入探索 Element UI:自定义滚动条与弹出层管理的技巧
在这篇博客中,我们将深入探讨 Element UI 中的自定义滚动条及弹出层管理技巧。文章详细介绍了 el-scrollbar 组件的使用和参数设置,以及 PopupManager 如何有效管理弹出层的 z-index。我们还将探讨如何实现灵活的全屏组件,利用 vue-popper 创建自定义弹出层,最后介绍 ClickOutside 指令的用法。这些高级技巧将帮助你提升 Element UI 应用程序的用户体验与交互灵活性。
371 1
深入探索 Element UI:自定义滚动条与弹出层管理的技巧
|
3月前
|
JavaScript 索引
Vue开发中Element UI/Plus使用指南:常见问题(如Missing required prop: “value“)及中文全局组件配置解决方案
Vue开发中Element UI/Plus使用指南:常见问题(如Missing required prop: “value“)及中文全局组件配置解决方案
240 0
|
5月前
|
开发者 C# Android开发
明白吗?Xamarin与Native的终极对决:究竟哪种开发方式更适合您的项目需求,让我们一探究竟!
【8月更文挑战第31天】随着移动应用开发的普及,开发者面临多种技术选择。本文对比了跨平台解决方案Xamarin与原生开发方式的优势与劣势。Xamarin使用C#进行跨平台开发,代码复用率高,可大幅降低开发成本;但因基于抽象层,可能影响性能。原生开发则充分利用平台特性,提供最佳用户体验,但需维护多套代码库,增加工作量。开发者应根据项目需求、团队技能和预算综合考量,选择最适合的开发方式。
139 0
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
103 2
|
3月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
89 0
|
3月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
69 0
|
3月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
75 0
|
3月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
101 0
|
20天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
20天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

推荐镜像

更多