# 实现elementUI组件库中el-table表格全选功能
本文利用elementUI组件库中的el-table组件实现全选功能,经过从网上学习,总结了一套完美的方案。详细方案请您继续阅读本文。
# 背景
对于后台管理系统或者一些需要导出信息的表格功能,常常就会出现批量导出功能。此篇文章产生的背景就是项目需要实现一个批量提现申请的功能,element表格表头的全选是本页全选,告知产品后并建议按照滴滴开发票中有 本页全选 及 全选所有 两个按钮同时出现的形式来实现。建议归建议,实现还是有点难度,记得N年前做过这个功能,就是当年没好好总结。这次实现了可得好好把代码保留下来。
# 开干
这种功能是很常见的功能,在网上肯定一堆。时间紧,任务重,只得直接网上搜索了,网上一个链接一个链接尝试,试的怀疑人生,几乎没有一个ok的,阅读他们代码,有的是需要将表格数据完全加载出来的(不可取),找到最后,皇天不负有心人,经测试终于有一个差不多可行的,就是有些bug。
# 主要思路
element组件库的table组件 设置row-key属性 行数据的 Key,用来优化 Table 的渲染;
table-column设置reserve-selection为true 为 true 则会在数据更新之后保留之前选中的数据(需指定
row-key
)TIP
经过以上两个步骤,表格跨页就可以实现数据保留了,但是当前表头的勾选还仅仅是本页全选,且用户会误以为全选所有
新增一个checkbox,用来表示全选所有
当checkbox为true时,表示全选所有,但是此时用户会取消掉部分选中数据,因此需要将取消的数据保存起来
用户去掉部分数据后,全选所有checkbox的样式还是全选打勾的状态,因此需要引入一个indeterminate来控制checkbox的样式
当checkbox为true时,让后端排除掉取消勾选的数据,全做处理。当checkbox为false时,仅做勾选数据的处理即可
当checkbox为true时,切换分页的时候得把数据样式全部选中
将checkbox全选所有及表头的勾选 新增 文字说明,并修改样式
# 后端配合
后端字段要求
页面中搜索所有条件字段(除了分页,查询所有)
是否全选
选中列表
排除列表
数据计算方式
2.1 先根据条件查询出来①总数
2.2 判断是②否是全选
2.3 是全选的话 查看 ③排除列表 ①减去③ 就是 最终选中的数据
2.4 不是全选的话 查看 ④选中列表 ④就是 最终选中的数据
# 示例
代码中有模拟分页接口
,对于后端无数据又需要测试分页功能时,可使用。
<template>
<div class="el-table-select-all">
<div class="table-box">
<el-checkbox class="check-all" :indeterminate="indeterminate " v-model="allCheck" @change="allCheckEvent">全选所有</el-checkbox>
<el-table ref="recordTable" :data="tableData" height="600" :loading="loading" @row-click="clickRow" :row-key="rowKey" @select-all="selectAll" @selection-change="handleSelectionChange" @select="selectOne">
<el-table-column label="" align="center" width="60px">
<template slot-scope="scope">
{{ (scope.$index + 1 )+ (params.page_no - 1) * params.page_size }}
</template>
</el-table-column>
<el-table-column type="selection" width="65px" :reserve-selection="true"></el-table-column>
<el-table-column label="姓名" prop="name"> </el-table-column>
<el-table-column label="性别" prop="gender"> </el-table-column>
<el-table-column label="手机号" prop="tel" dateFormat="datetime"> </el-table-column>
<el-table-column label="地址" prop="address" dateFormat="datetime"> </el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<div>
<el-button size="mini" type="text" @click.native.stop="haddleDetail(scope.row)">详细信息</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-box">
<div class="tips" v-if="total > 0">已选择 {{selectNum}} 条</div>
<el-pagination class="pagination" v-if="total > 0" @size-change="handlePageSizeChange" @current-change="handlePageCurrentChange" :current-page="params.page_no" :page-sizes="[10, 20, 50, 100]" :page-size="params.page_size" layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination>
</div>
</div>
</template>
<script>
export default {
name: "el-table-select-all",
data() {
return {
params: {
page_no: 1,
page_size: 10
},
total: 0,
tableData: [], //表格数据
loading: false, //是否在加载数据
allCheck: false, // 是否全选
checkList: [], //已选择的数据
falseList: [], //存储取消的数据
indeterminate: false, //是否全选的样式
clickRowObj: null, //点击的行数据
isClickRow: false, //是否是 点击行
rowKey: "id" //关键唯一标识
}
},
watch: {
//表格数据变化
tableData: {
handler(value) {
if (this.allCheck) {
let that = this
let len = that.falseList.length
let lenCheck = that.checkList.length
if (that.checkList.length === 0) {
value.forEach(row => {
that.$refs.recordTable.toggleRowSelection(row, true)
})
} else {
value.forEach(row => {
for (let i = 0; i < lenCheck; i++) {
if (row[this.rowKey] === that.checkList[i][this.rowKey]) {
that.$refs.recordTable.toggleRowSelection(row, false)
break
} else {
that.$refs.recordTable.toggleRowSelection(row, true)
}
}
if (len !== 0) {
for (let j = 0; j < len; j++) {
if (row[this.rowKey] === that.falseList[j][this.rowKey]) {
that.$refs.recordTable.toggleRowSelection(row, false)
break
}
}
}
})
}
}
},
deep: true
},
//排除的选项
falseList: {
handler(value) {
if (value.length === this.total) {
this.allCheck = false
this.indeterminate = false
}
if (value.length === 0) {
this.allCheck = true
this.indeterminate = false
}
},
deep: true
},
//选中的选项 allCheck 为false时 此选项才数据才是真实的
checkList: {
handler(value) {
if (!this.allCheck) {
//如果选中数量等于总数 排除项设置为 空数组 自动触发 全选设置为 true
if (value.length === this.total) {
this.falseList = []
}
//如果选中数量为 空
if (value.length === 0) {
this.indeterminate = false
}
//如果选中数量大于0 小于总数 设置样式为横杠
if (value.length < this.total && value.length > 0) {
this.indeterminate = true
}
}
},
deep: true
}
},
computed: {
//计算已选择的条数
selectNum() {
if (this.allCheck) {
return this.total - this.falseList.length
} else {
return this.checkList.length
}
}
},
created() {
//搜索数据
this.handleSearch()
},
methods: {
//点击全选 checkbox 按钮
allCheckEvent() {
if (this.allCheck) {
//全选的时候把 排除项 设置为 空数组
this.falseList = []
this.tableData.forEach(row => {
this.$refs.recordTable.toggleRowSelection(row, true)
})
} else {
//非全选的时候把 checkList 置位 空数组
this.checkList = []
this.$refs.recordTable.clearSelection()
}
},
//点击行
clickRow(row) {
this.isClickRow = true
this.clickRowObj = row
this.$refs.recordTable.toggleRowSelection(row)
},
//点击表格里的 本页全选
selectAll(rows) {
if (this.allCheck) {
let that = this
that.indeterminate = true
let lenNow = that.tableData.length
// 判断勾选全选本页是选中还是取消
if (rows.indexOf(that.tableData[0]) !== -1) {
// 选中
for (let i = 0; i < lenNow; i++) {
for (let n = 0; n < that.falseList.length; n++) {
if (that.falseList[n][this.rowKey] === that.tableData[i][this.rowKey]) {
that.falseList.splice(n, 1)
}
}
}
} else {
// 取消
for (let j = 0; j < lenNow; j++) {
if (that.falseList.length !== 0) {
if (that.falseList.indexOf(that.tableData[j]) === -1) {
that.falseList.push(that.tableData[j])
}
} else {
that.falseList.push(that.tableData[j])
}
}
}
}
},
//判断选中状态
judgeFunc(rows, row) {
// 多选框样式改变
this.indeterminate = true
// 判断勾选数据行是选中还是取消
let selected = rows.length && rows.indexOf(row) !== -1
let lenFalse = this.falseList.length
if (selected) {
// 选中
if (lenFalse !== 0) {
for (let i = 0; i < lenFalse; i++) {
if (this.falseList[i][this.rowKey] === row[this.rowKey]) {
this.falseList.splice(i, 1)
break
}
}
}
} else {
// 取消
this.falseList.push(row)
}
},
//当选择项发生变化时会触发该事件
handleSelectionChange(rows) {
if (this.isClickRow) {
if (this.allCheck) {
this.judgeFunc(rows, this.clickRowObj)
}
this.isClickRow = false
this.clickRowObj = null
}
this.checkList = rows
},
//当用户手动勾选数据行的 Checkbox 时触发的事件
selectOne(rows, row) {
if (this.allCheck) {
this.judgeFunc(rows, row)
}
},
/**
* @description: 搜索数据
* @param {type}
* @return {type}
* @author: syx
*/
handleSearch() {
//当有搜索条件时 更换搜索条件会触发 这个方法,所以这时需要清空之前的选项 并把 allCheck 置为false
this.allCheck = false
this.$refs.recordTable&&this.$refs.recordTable.clearSelection()
this.params.page_no = 1
this.fetchData()
},
//模拟接口
getListApi(params) {
return new Promise((resolve) => {
const data = []
const total = 22
for (let i = 0; i < params.page_size; i++) {
if ((params.page_no - 1) * params.page_size + i < total) {
const id = (params.page_no - 1) * params.page_size + i
data.push({
id,
name: "小" + ["明", "红", "布", "娟", "宁", "婷", "洲", "滨", "青", "黄", "华", "东", "西", "南", "北", "橙", "绿", "蓝", "紫", "涵", "罗", "沈"][id],
tel: "131452066" + (i + "").padStart(2, "0"),
gender: (id % 2 === 0) ? "男" : "女",
address: "福建省" + ["漳州市", "厦门市", "泉州市", "宁德市", "福州市", "三明市", "莆田市", "龙岩市", "南平市"][Math.floor(Math.random() * 9)]
})
} else {
continue
}
}
resolve(
{
data,
total: total,
}
)
})
},
/**
* @description: 触发搜索
* @param {type}
* @return:
* @author: syx
*/
fetchData() {
//请求接口加载表格数据
this.loading = true
this.getListApi(this.params).then(data => {
this.tableData = data.data
this.total = data.total
this.loading = false
}).catch(err => {
this.loading = false
})
},
/** 分页大小发生改变 */
handlePageSizeChange(size) {
this.params.page_size = size
this.handleSearch()
},
/** 分页页数发生改变 */
handlePageCurrentChange(page) {
this.params.page_no = page
this.fetchData()
},
haddleDetail(row) {
alert("注意方法中使用 .native.stop 修饰符 不然会触发点击行选中方法")
}
}
}
</script>
<style lang="less" scoped>
.el-table-select-all {
width: 100%;
.table-box {
position: relative;
/deep/ .check-all {
position: absolute;
top: 12px;
left: 6px;
z-index: 9;
display: flex;
justify-content: flex-start;
align-items: center;
.el-checkbox__label {
width: 34px;
display: inline-block;
font-size: 12px;
line-height: 14px;
white-space: pre-wrap;
color: #606266;
}
}
/deep/ .el-table {
.el-table__header-wrapper {
.el-table__header {
.el-table-column--selection {
.cell {
overflow: visible;
.el-checkbox {
position: relative;
&::after {
content: "本页全选";
position: absolute;
top: 50%;
left: 100%;
transform: translateY(-50%);
white-space: pre-wrap;
width: 34px;
line-height: 14px;
font-size: 12px;
padding-left: 8px;
color: #606266;
}
}
}
}
}
}
}
}
.pagination-box {
position: relative;
.tips {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
font-size: 14px;
color: #409eff;
}
.pagination {
display: flex;
justify-content: flex-end;
align-items: center;
}
}
}
</style>
# 总结
有bug可以搜索微信公众号 【爆米花小布】进行反馈。此篇文章旨在需要用到此功能时,复制代码即可,提高工作效率。也希望干后端的同志遇到全选功能时了解需要 一个是否全选字段,一个选中集合,一个排除集合。