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

