Vue使用el-table实现表格跨页多选

 更新时间:2023年08月09日 09:34:01   作者:前端手术刀  
在我们日常项目开发中,经常会有表格跨页多选的需求,接下来让我们用 el-table示例一步步来实现这个需求,文中有详细的代码讲解,对我们的学习或工作有一定的帮助,需要的朋友可以参考下

前言

在我们日常项目开发中,经常会有 表格跨页多选 的需求,接下来让我们用 el-table 示例一步步来实现这个需求。

动手开发

在线体验

priceless-mcclintock-4cp7x3 - CodeSandbox

常规版本

本部分只写了一些重点代码,心急的彦祖可以直接看 性能进阶版

  1. 首先我们需要初始化一个选中的数组 checkedRows
this.checkedRows = [] 
  1. 在触发选中的时候,我们就需要把当前行数据 push checkedRows ,否则就需要剔除对应行
<el-table ref="multipleTable" @select="handleSelectChange">
handleSelectChange (val, row) {
  const checkedIndex = this.checkedRows.findIndex(_row => _row.id === row.id)
  if (checkedIndex > -1) {
    // 选中剔除
    this.checkedRows.splice(checkedIndex, 1)
  } else {
    // 未选中压入
    this.checkedRows.push(row)
  }
}
  1. 实现换页的时候的回显逻辑
this.data.forEach(row=>{
  const checkedIndex = this.checkedRows.findIndex(_row => _row.id === row.id)
  if(checkedIndex>-1) this.$refs.multipleTable.toggleRowSelection(row,true)
})

效果预览

让我们看下此时的效果

2023-08-08 20.03.52.gif

完整代码

<template>
  <div>
    <el-table
      ref="multipleTable"
      :data="tableData"
      tooltip-effect="dark"
      style="width: 100%"
      @select="handleSelectChange"
      @select-all="handleSelectAllChange"
    >
      <el-table-column
        type="selection"
        width="55"
      />
      <el-table-column
        label="日期"
        width="120"
        prop="date"
      />
      <el-table-column
        prop="name"
        label="姓名"
        width="120"
      />
    </el-table>
    <el-pagination
      background
      :current-page.sync="currentPage"
      layout="prev, pager, next"
      :total="1000"
      @current-change="currentChange"
    />
  </div>
</template>
<script>
export default {
  data () {
    return {
      currentPage: 1,
      checkedRows: [],
      pageSize: 10,
      totalData: Array.from({ length: 1000 }, (_, index) => {
        return {
          date: '2016-05-03',
          id: index,
          name: '王小虎' + index
        }
      })
    }
  },
  computed: {
    tableData () {
      const { currentPage, totalData, pageSize } = this
      return totalData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
    }
  },
  methods: {
    currentChange (page) {
      this.currentPage = page
      this.tableData.forEach(row => {
        const checkedIndex = this.checkedRows.findIndex(_row => _row.id === row.id)
        if (checkedIndex > -1) this.$refs.multipleTable.toggleRowSelection(row, true)
      })
    },
    handleSelectChange (val, row) {
      const checkedIndex = this.checkedRows.findIndex(_row => _row.id === row.id)
      if (checkedIndex > -1) {
        this.checkedRows.splice(checkedIndex, 1)
      } else {
        this.checkedRows.push(row)
      }
    },
    handleSelectAllChange (val) {
      this.tableData.forEach(row => {
        this.handleSelectChange(null, row)
      })
    }
  }
}
</script>

性能进阶版

性能缺陷分析

优秀的彦祖们,应该发现以上代码的性能缺陷了

1. handleSelectChange 需要执行一个 O(n) 复杂度的循环

2. currentChange 的回显逻辑内部, 有一个 O(n^2) 复杂度的循环

想象一下 如果场景中勾选的行数达到了 10000 行, 每页显示 100

那么我们每次点击换页 最坏情况就要执行 10000 * 100 次循环,这是件可怕的事...

重新设计数据结构

其实我们没必要把 checkedRows 设计成一个数组, 我们可以设计成一个 map ,这样读取值就只需要 O(1) 复杂度

1.改造 checkedRows

this.crossPageMap = new Map() 

2.修改选中逻辑( 核心代码 )

handleSelectChange (val, row) {
  // 实现了 O(n) 到 O(1) 的提升
  const checked = this.crossPageMap.has(row.id)
  if (checked) {
    this.crossPageMap.delete(row.id)
  } else {
    this.crossPageMap.set(row.id, row)
  }
}

3.修改换页回显逻辑

currentChange (page) {
  this.currentPage = page
  // 实现了 O(n^2) 到 O(n) 的提升
  this.tableData.forEach(row => {
    const checked = this.crossPageMap.has(row.id)
    if (checked) this.$refs.multipleTable.toggleRowSelection(row, true)
  })
}

完整代码

<template>
  <div>
    <el-table
      ref="multipleTable"
      :data="tableData"
      tooltip-effect="dark"
      style="width: 100%;height:500px"
      @select="handleSelectChange"
      @select-all="handleSelectAllChange"
    >
      <el-table-column
        type="selection"
        width="55"
      />
      <el-table-column
        label="日期"
        width="120"
        prop="date"
      />
      <el-table-column
        prop="name"
        label="姓名"
        width="120"
      />
    </el-table>
    <el-pagination
      background
      :current-page.sync="currentPage"
      layout="prev, pager, next"
      :total="1000"
      @current-change="currentChange"
    />
  </div>
</template>
<script>
export default {
  data () {
    return {
      currentPage: 1,
      crossPageMap: new Map(),
      pageSize: 10,
      totalData: Array.from({ length: 1000 }, (_, index) => {
        return {
          date: '2016-05-03',
          id: index,
          name: '王小虎' + index
        }
      })
    }
  },
  computed: {
    tableData () {
      const { currentPage, totalData, pageSize } = this
      return totalData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
    }
  },
  methods: {
    currentChange (page) {
      this.currentPage = page
      this.tableData.forEach(row => {
        const checked = this.crossPageMap.has(row.id)
        if (checked) this.$refs.multipleTable.toggleRowSelection(row, true)
      })
    },
    handleSelectChange (val, row) {
      const checked = this.crossPageMap.has(row.id)
      if (checked) {
        this.crossPageMap.delete(row.id)
      } else {
        this.crossPageMap.set(row.id, row)
      }
    },
    handleSelectAllChange (val) {
      this.tableData.forEach(row => {
        this.handleSelectChange(null, row)
      })
    }
  }
}
</script>

抽象业务逻辑

以上就是完整的业务代码部分,但是为了复用性。

我们考虑可以把其中的逻辑抽象成一个 CrossPage

设计 CrossPage 类

接收以下参数

`data` - 行数据
`key` - 行数据唯一值
`max` - 最大选中行数
`toggleRowSelection` - 切换行数据选中/取消选中的方法

提供以下方法

`onRowSelectChange` - 外部点行数据点击的时候调用此方法
`onDataChange` - 外部数据变化的时候调用此方法
`clear` - 清空所有选中行

构造器大致代码 如下

constructor (options={}) {
    this.crossPageMap = new Map()
    this.key = options.key || 'id'
    this.data = options.data || []
    this.max = options.max || Number.MAX_SAFE_INTEGER
    this.toggleRowSelection = options.toggleRowSelection
    if(typeof this.toggleRowSelection !== 'function') throw new Error('toggleRowSelection is not function')
}

设置私有crossPageMap

彦祖们,问题来了,我们把 crossPageMap 挂载到实例上,那么外部就可以直接访问修改这个变量。

这可能导致我们内部的数据逻辑错乱,所以必须禁止外部访问。

我们可以使用 # 修饰符来实现私有属性,具体参考

类私有域 - JavaScript | MDN (mozilla.org)

完整代码

  • CrossPage.js
/**
 * @description 跨页选择
 * @param {Object} options
 * @param {String} options.key 行数据唯一标识
 * @param {Array} options.data 行数据
 * @param {Number} options.max 最大勾选行数
 * @param {Function} options.toggleRowSelection 设置行数据选中/取消选中的方法,必传
 */
export const CrossPage = class {
  #crossPageMap = new Map();
  constructor (options={}) {
    this.key = options.key || 'id'
    this.data = options.data || []
    this.max = options.max || Number.MAX_SAFE_INTEGER
    this.toggleRowSelection = options.toggleRowSelection
    if(typeof this.toggleRowSelection !== 'function') throw new Error('toggleRowSelection is not function')
  }
  get keys(){
    return Array.from(this.#crossPageMap.keys())
  }
  get values(){
    return Array.from(this.#crossPageMap.values())
  }
  get size(){
    return this.#crossPageMap.size
  }
  clear(){
    this.#crossPageMap.clear()
    this.updateViews()
  }
  onRowSelectChange (row) {
    if(typeof row !== 'object') return console.error('row is not object')
    const {key,toggleRowSelection} = this
    const checked = this.#crossPageMap.has(row[key])
    if(checked) this.#crossPageMap.delete(row[key])
    else {
      this.#crossPageMap.set(row[key],row)
      if(this.size>this.max){
        this.#crossPageMap.delete(row[key])
        toggleRowSelection(row,false)
      }
    }
  }
  onDataChange(list){
    this.data = list
    this.updateViews()
  }
  updateViews(){
    const {data,toggleRowSelection,key} = this
    data.forEach(row=>{
      toggleRowSelection(row,this.#crossPageMap.has(row[key]))
    })
  }
}
  • crossPage.vue
<template>
  <div>
    <el-table
      ref="multipleTable"
      :data="tableData"
      tooltip-effect="dark"
      style="width: 100%"
      @select="handleSelectChange"
      @select-all="handleSelectAllChange"
    >
      <el-table-column
        type="selection"
        width="55"
      />
      <el-table-column
        label="日期"
        width="120"
        prop="date"
      />
      <el-table-column
        prop="name"
        label="姓名"
        width="120"
      />
    </el-table>
    <el-button @click="clear">
      清空
    </el-button>
    <el-button @click="keys">
      获取 keys
    </el-button>
    <el-button @click="values">
      获取 values
    </el-button>
    <el-pagination
      background
      :current-page.sync="currentPage"
      layout="prev, pager, next"
      :total="1000"
      @current-change="currentChange"
    />
  </div>
</template>
<script>
import { CrossPage } from './CrossPage'
export default {
  data () {
    return {
      currentPage: 1,
      pageSize: 10,
      totalData: Array.from({ length: 1000 }, (_, index) => {
        return {
          date: '2016-05-03',
          id: index,
          name: '王小虎' + index
        }
      }),
      multipleSelection: []
    }
  },
  computed: {
    tableData () {
      const { currentPage, totalData, pageSize } = this
      return totalData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
    }
  },
  mounted () {
    this.crossPageIns = new CrossPage({
      key: 'id',
      max: 2,
      data: this.tableData,
      toggleRowSelection: this.$refs.multipleTable.toggleRowSelection
    })
  },
  methods: {
    clear () {
      this.crossPageIns.clear()
    },
    keys () {
      console.log('keys:', this.crossPageIns.keys)
    },
    values () {
      console.log('values:', this.crossPageIns.values)
    },
    currentChange (page) {
      this.currentPage = page
      // 调用实例 onDataChange 方法
      this.crossPageIns.onDataChange(this.tableData)
    },
    handleSelectChange (val, row) {
      // 调用实例 onRowSelectChange 方法
      this.crossPageIns.onRowSelectChange(row)
    },
    handleSelectAllChange (val) {
      this.tableData.forEach(row => {
        this.crossPageIns.onRowSelectChange(row)
      })
    }
  }
}
</script>

写在最后

未来想做的还有很多

  • 利用 requestIdleCallback 提升单页大量数据的 toggleRowSelection 渲染效率
  • 提供默认选中项的配置
  • ...

以上就是Vue使用el-table实现表格跨页多选的详细内容,更多关于Vue el-table表格跨页多选的资料请关注脚本之家其它相关文章!

相关文章

  • Vue中created与mounted的区别浅析

    Vue中created与mounted的区别浅析

    在使用vue框架的过程中,我们经常需要给一些数据做一些初始化处理,这时候我们常用的就是在created与mounted选项中作出处理,这篇文章主要给大家介绍了关于Vue中created与mounted区别的相关资料,其中部分知识点可能是我们日常工作会见到或用到的,需要的朋友可以参考下
    2022-06-06
  • Vue关闭当前弹窗页面的两种方式

    Vue关闭当前弹窗页面的两种方式

    这篇文章主要给大家介绍了关于Vue关闭当前弹窗页面的两种方式,这是最近项目中遇到的一个需求,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • vue实现树形结构增删改查的示例代码

    vue实现树形结构增删改查的示例代码

    其实很多公司都会有类似于用户权限树的增删改查功能,本文主要介绍了vue实现树形结构增删改查,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 详解如何在Vue中快速实现数据可视化大屏展示

    详解如何在Vue中快速实现数据可视化大屏展示

    在现代数据驱动的应用程序中,数据可视化大屏已经成为了非常重要的一环,通过对海量数据进行可视化展示,可以帮助用户更好地理解和分析数据,从而做出更加明智的决策,在Vue中进行数据可视化大屏展示也变得越来越流行,本文将介绍如何在Vue中快速实现数据可视化大屏展示
    2023-10-10
  • 基于Vue.js实现数字拼图游戏

    基于Vue.js实现数字拼图游戏

    为了进一步让大家了解Vue.js的神奇魅力,了解Vue.js的一种以数据为驱动的理念,本文主要利用Vue实现了一个数字拼图游戏,其原理并不是很复杂,下面跟着小编一起来学习学习。
    2016-08-08
  • 详解Vue computed计算属性是什么

    详解Vue computed计算属性是什么

    在vue中,有时候你需要对data中的数据进行处理,或者对抓取的数据进行处理之后再挂载呈现到标签中,这时候你就需要计算属性了,当然看到这里你可能还是不了解那下面我举几个实例并附代码解释
    2023-03-03
  • vue3.0公共组件自动导入的方法实例

    vue3.0公共组件自动导入的方法实例

    这篇文章主要给大家介绍了关于vue3.0公共组件自动导入的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • vue 动态表单开发方法案例详解

    vue 动态表单开发方法案例详解

    这篇文章主要介绍了vue 动态表单开发方法,结合具体案例形式详细分析了vue.js动态表单相关原理、开发步骤与实现技巧,需要的朋友可以参考下
    2019-12-12
  • vue使用原生js实现滚动页面跟踪导航高亮的示例代码

    vue使用原生js实现滚动页面跟踪导航高亮的示例代码

    这篇文章主要介绍了vue使用原生js实现滚动页面跟踪导航高亮的示例代码,滚动页面指定区域导航高亮。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • vue配置electron使用electron-builder进行打包的操作方法

    vue配置electron使用electron-builder进行打包的操作方法

    这篇文章主要介绍了vue配置electron使用electron-builder进行打包的操作方法,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-08-08

最新评论