Vue使用el-table實(shí)現(xiàn)表格跨頁(yè)多選
前言
在我們?nèi)粘m?xiàng)目開(kāi)發(fā)中,經(jīng)常會(huì)有 表格跨頁(yè)多選
的需求,接下來(lái)讓我們用 el-table
示例一步步來(lái)實(shí)現(xiàn)這個(gè)需求。
動(dòng)手開(kāi)發(fā)
在線體驗(yàn)
priceless-mcclintock-4cp7x3 - CodeSandbox
常規(guī)版本
本部分只寫(xiě)了一些重點(diǎn)代碼,心急的彥祖可以直接看 性能進(jìn)階版
- 首先我們需要初始化一個(gè)選中的數(shù)組
checkedRows
this.checkedRows = []
- 在觸發(fā)選中的時(shí)候,我們就需要把當(dāng)前行數(shù)據(jù)
push
到checkedRows
,否則就需要剔除對(duì)應(yīng)行
<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) } }
- 實(shí)現(xiàn)換頁(yè)的時(shí)候的回顯邏輯
this.data.forEach(row=>{ const checkedIndex = this.checkedRows.findIndex(_row => _row.id === row.id) if(checkedIndex>-1) this.$refs.multipleTable.toggleRowSelection(row,true) })
效果預(yù)覽
讓我們看下此時(shí)的效果
完整代碼
<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>
性能進(jìn)階版
性能缺陷分析
優(yōu)秀的彥祖?zhèn)?應(yīng)該發(fā)現(xiàn)以上代碼的性能缺陷了
1. handleSelectChange
需要執(zhí)行一個(gè) O(n)
復(fù)雜度的循環(huán)
2. currentChange
的回顯邏輯內(nèi)部, 有一個(gè) O(n^2)
復(fù)雜度的循環(huán)
想象一下 如果場(chǎng)景中勾選的行數(shù)達(dá)到了 10000
行, 每頁(yè)顯示 100
條
那么我們每次點(diǎn)擊換頁(yè) 最壞情況就要執(zhí)行 10000 * 100
次循環(huán),這是件可怕的事...
重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)
其實(shí)我們沒(méi)必要把 checkedRows
設(shè)計(jì)成一個(gè)數(shù)組, 我們可以設(shè)計(jì)成一個(gè) map
,這樣讀取值就只需要 O(1)
復(fù)雜度
1.改造 checkedRows
this.crossPageMap = new Map()
2.修改選中邏輯( 核心代碼
)
handleSelectChange (val, row) { // 實(shí)現(xiàn)了 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.修改換頁(yè)回顯邏輯
currentChange (page) { this.currentPage = page // 實(shí)現(xiàn)了 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>
抽象業(yè)務(wù)邏輯
以上就是完整的業(yè)務(wù)代碼部分,但是為了復(fù)用性。
我們考慮可以把其中的邏輯抽象成一個(gè) CrossPage
類
設(shè)計(jì) CrossPage 類
接收以下參數(shù)
`data` - 行數(shù)據(jù) `key` - 行數(shù)據(jù)唯一值 `max` - 最大選中行數(shù) `toggleRowSelection` - 切換行數(shù)據(jù)選中/取消選中的方法
提供以下方法
`onRowSelectChange` - 外部點(diǎn)行數(shù)據(jù)點(diǎn)擊的時(shí)候調(diào)用此方法 `onDataChange` - 外部數(shù)據(jù)變化的時(shí)候調(diào)用此方法 `clear` - 清空所有選中行
構(gòu)造器大致代碼 如下
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') }
設(shè)置私有crossPageMap
彥祖?zhèn)?問(wèn)題來(lái)了,我們把 crossPageMap
掛載到實(shí)例上,那么外部就可以直接訪問(wèn)修改這個(gè)變量。
這可能導(dǎo)致我們內(nèi)部的數(shù)據(jù)邏輯錯(cuò)亂,所以必須禁止外部訪問(wèn)。
我們可以使用 #
修飾符來(lái)實(shí)現(xiàn)私有屬性,具體參考
類私有域 - JavaScript | MDN (mozilla.org)
完整代碼
- CrossPage.js
/** * @description 跨頁(yè)選擇 * @param {Object} options * @param {String} options.key 行數(shù)據(jù)唯一標(biāo)識(shí) * @param {Array} options.data 行數(shù)據(jù) * @param {Number} options.max 最大勾選行數(shù) * @param {Function} options.toggleRowSelection 設(shè)置行數(shù)據(jù)選中/取消選中的方法,必傳 */ 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 // 調(diào)用實(shí)例 onDataChange 方法 this.crossPageIns.onDataChange(this.tableData) }, handleSelectChange (val, row) { // 調(diào)用實(shí)例 onRowSelectChange 方法 this.crossPageIns.onRowSelectChange(row) }, handleSelectAllChange (val) { this.tableData.forEach(row => { this.crossPageIns.onRowSelectChange(row) }) } } } </script>
寫(xiě)在最后
未來(lái)想做的還有很多
- 利用
requestIdleCallback
提升單頁(yè)大量數(shù)據(jù)的toggleRowSelection
渲染效率 - 提供默認(rèn)選中項(xiàng)的配置
- ...
以上就是Vue使用el-table實(shí)現(xiàn)表格跨頁(yè)多選的詳細(xì)內(nèi)容,更多關(guān)于Vue el-table表格跨頁(yè)多選的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue關(guān)閉當(dāng)前彈窗頁(yè)面的兩種方式
這篇文章主要給大家介紹了關(guān)于Vue關(guān)閉當(dāng)前彈窗頁(yè)面的兩種方式,這是最近項(xiàng)目中遇到的一個(gè)需求,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07vue實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)增刪改查的示例代碼
其實(shí)很多公司都會(huì)有類似于用戶權(quán)限樹(shù)的增刪改查功能,本文主要介紹了vue實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)增刪改查,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09詳解如何在Vue中快速實(shí)現(xiàn)數(shù)據(jù)可視化大屏展示
在現(xiàn)代數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用程序中,數(shù)據(jù)可視化大屏已經(jīng)成為了非常重要的一環(huán),通過(guò)對(duì)海量數(shù)據(jù)進(jìn)行可視化展示,可以幫助用戶更好地理解和分析數(shù)據(jù),從而做出更加明智的決策,在Vue中進(jìn)行數(shù)據(jù)可視化大屏展示也變得越來(lái)越流行,本文將介紹如何在Vue中快速實(shí)現(xiàn)數(shù)據(jù)可視化大屏展示2023-10-10基于Vue.js實(shí)現(xiàn)數(shù)字拼圖游戲
為了進(jìn)一步讓大家了解Vue.js的神奇魅力,了解Vue.js的一種以數(shù)據(jù)為驅(qū)動(dòng)的理念,本文主要利用Vue實(shí)現(xiàn)了一個(gè)數(shù)字拼圖游戲,其原理并不是很復(fù)雜,下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)。2016-08-08vue3.0公共組件自動(dòng)導(dǎo)入的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于vue3.0公共組件自動(dòng)導(dǎo)入的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04vue 動(dòng)態(tài)表單開(kāi)發(fā)方法案例詳解
這篇文章主要介紹了vue 動(dòng)態(tài)表單開(kāi)發(fā)方法,結(jié)合具體案例形式詳細(xì)分析了vue.js動(dòng)態(tài)表單相關(guān)原理、開(kāi)發(fā)步驟與實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-12-12vue使用原生js實(shí)現(xiàn)滾動(dòng)頁(yè)面跟蹤導(dǎo)航高亮的示例代碼
這篇文章主要介紹了vue使用原生js實(shí)現(xiàn)滾動(dòng)頁(yè)面跟蹤導(dǎo)航高亮的示例代碼,滾動(dòng)頁(yè)面指定區(qū)域?qū)Ш礁吡?。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10vue配置electron使用electron-builder進(jìn)行打包的操作方法
這篇文章主要介紹了vue配置electron使用electron-builder進(jìn)行打包的操作方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08