1、流程如下
2、具体分析
该问题的主要难点在于:用户点击全选之后,对已勾选项进行操作,对跨页后的数据对自己是否被勾选情况不明
具体场景如下:
用户点击全选后(全选状态为true),通过单项或者单页取消勾选 ——>此时(全选状态为false),但是分页的时候其他页面的数据应该也是被选中。
解决办法: 当全选状态为true时,将点击取消勾选的项目存入uncheckedArr中,全选状态变为false
因此主要分为以下三种场景:
-
全选状态(isSelectedAll = true)时,切换页面全选
-
全不选状态且取消勾选的数组中有指(isSelectedAll = false && uncheckedArr.length>0)时,切换页面时仍然选中全部(但需要排除包含在uncheckedArr中的值)
-
全不选状态(isSelectedAll = false)时,切换页面全不选中
在以上三种场景的基础上再对表格的单项勾选/取消勾选事件、单页勾选/取消勾选事件分析:
elementUI的table表格组件中设置reserve-selection属性,即可记住已勾选的项。
因此我们只需要关注取消勾选的项,即场景1和场景2
-
全选状态(isSelectedAll = true)时,只有取消勾选操作,取消勾选时将被取消的项存入uncheckedArr中,同时isSelectedAll = false
-
全不选状态且取消勾选的数组中有指(isSelectedAll = false && uncheckedArr.length>0)时:
2.1. 取消勾选:将被取消的项存入uncheckedArr
2.2. 重新勾选:
(1)将重新勾选的项从uncheckedArr移除
(2)当table表格被选中的项数===表格总项数时,isSelectedAll设置为true
(3)当tuncheckedArr中没有项时,isSelectedAll设置为 true
3、相关代码
3.1 封装的table组件代码
// STable.vue
<template>
<div class="s-table">
<el-table
ref="innerTable"
class="table"
style="width: 100%"
:row-key="getRowKeys"
:data="tableData"
:is-selected-all="isSelectedAll"
:total="total"
v-bind="$attrs"
@select="select"
@select-all="selectAll"
v-on="$listeners"
>
<el-table-column
v-if="selectable"
type="selection"
width="80"
:reserve-selection="true"
></el-table-column>
<el-table-column
v-for="(item) in columns"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:width="parseInt(item.width)"
>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'STable',
props: {
// 默认列
columns: {
type: Array,
default: () => []
},
tableData: {
type: Array,
default: () => []
},
// 是否可选
selectable: {
type: Boolean,
default: false
},
// 是否跨页全选
isSelectedAll: {
type: Boolean,
default: false
},
// table表格的总数据条数
total: {
type: Number,
default: 0
}
},
data() {
return {
uncheckedArr: []
}
},
watch: {
// 分页全选-监听数据变化
tableData: {
handler (value) {
this.isSelectedAll && this.chooseAllPages(this.isSelectedAll)
!this.isSelectedAll && this.checkedSelected()
},
deep: true
}
},
methods: {
getRowKeys(row) {
return row.id
},
// 全选所有页面
chooseAllPages(val) {
if (val) {
// 全选
this.$nextTick(() => {
this.tableData.forEach(row => {
this.$refs.innerTable.toggleRowSelection(row, true)
})
})
} else {
// 取消全选
this.uncheckedArr = []
this.$nextTick(() => {
this.$refs.innerTable.clearSelection()
})
}
},
// 切换分页时选择之前选中
checkedSelected() {
if (this.isSelectedAll) {
// 全选状态isSelectedAll=true
this.$nextTick(() => {
this.tableData.forEach(row => {
this.$refs.innerTable.toggleRowSelection(row, true)
})
})
} else {
// 非全选状态下isSelectedAll=false
// uncheckedArr有值,切换页面时仍然选中全部(但需要排除包含在uncheckedArr中的值)
if (this.uncheckedArr.length > 0) {
this.$nextTick(() => {
this.tableData.forEach(row => {
if (this.uncheckedArr.map(v => v.id).indexOf(row.id) < 0) {
this.$refs.innerTable.toggleRowSelection(row, true)
}
})
})
}
}
},
// 全选后取消单个选择事件
select(selection, row) {
this.selectExceptCardIds(selection, row)
this.$emit('select', [selection, row])
},
// 全选后取消单个选择事件
selectAll(selection) {
this.selectAllRows(selection)
this.$emit('selectAll', selection)
},
// 全选后取消单个选择事件
selectExceptCardIds(selection, row) {
if (this.isSelectedAll) {
// 全选状态下,列表行数据都处于被选中状态,手动勾选只能触发取消选中
this.uncheckedArr.push(row)
this.$emit('update:isSelectedAll', false)
} else {
// false, 非全选状态下,列表行数据可能被 取消勾选 或者 重新勾选
if (selection.indexOf(row) > 0) {
// 勾选
selection.map(el => {
this.uncheckedArr.map((v, index) => {
if (el.id === v.id) {
this.uncheckedArr.splice(index, 1)
if (this.uncheckedArr.length === 0) {
this.$emit('update:isSelectedAll', true)
}
}
})
if (selection.length === this.total) {
this.$emit('update:isSelectedAll', true)
}
})
} else {
// 取消勾选
// 若是uncheckedArr.length > 0,取消勾选的数组加入uncheckedArr
if (this.uncheckedArr.length > 0) {
this.uncheckedArr.push(row)
}
}
}
},
// 当前页面全选
selectAllRows(selection) {
if (this.isSelectedAll) {
// 全选状态下,列表行数据都处于被选中状态,手动勾选只能触发取消选中
this.tableData.map(row => {
this.uncheckedArr.push(row)
})
this.$emit('update:isSelectedAll', false)
} else {
// false,部分选中状态下
selection.map(row => {
// 勾选
this.uncheckedArr.map((v, index) => {
if (row.id === v.id) {
this.uncheckedArr.splice(index, 1)
}
if (this.uncheckedArr.length === 0) {
this.$emit('update:isSelectedAll', true)
}
})
if (selection.length === this.total) {
this.$emit('update:isSelectedAll', true)
}
// 取消勾选
if (this.uncheckedArr.length > 0) {
if (this.tableData.map(v => v.id).indexOf(row.id) === -1) {
this.uncheckedArr.push(row)
}
}
})
}
}
}
}
</script>
<style lang="scss">
</style>
3.2 示例代码
// example.vue
<template>
<el-card>
<el-checkbox
v-model="isSelectedAll"
@change="checkboxChange"
>全选所有</el-checkbox>
<s-table
ref="accessControlTable"
:table-data="tableData.slice((currentPage - 1) * pageSize, currentPage * pageSize)"
:columns="columns"
:selectable="true"
:total="tableData.length"
:is-selected-all.sync="isSelectedAll"
>
</s-table>
<el-pagination
class="s-pagination"
:currrent-page="currentPage"
:page-size="pageSize"
:page-sizes="[5,10,20,30]"
:total="tableData.length"
layout="total, prev, pager, next, sizes, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
</el-pagination>
</el-card>
</template>
<script>
import STable from '../components/STable.vue'
export default {
components: {
STable
},
data() {
return {
tableData: [
{
id: 1,
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 2,
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 3,
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 4,
date: '2016-05-05',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 5,
date: '2016-05-06',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 6,
date: '2016-05-07',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 7,
date: '2016-05-08',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 8,
date: '2016-05-09',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 9,
date: '2016-05-10',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 10,
date: '2016-05-11',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
id: 11,
date: '2016-05-12',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}
],
columns: [
{
label: '日期',
prop: 'date',
width: 180
},
{
label: '姓名',
prop: 'name',
width: 180
},
{
label: '地址',
prop: 'address'
}
],
currentPage: 1, // 当前页码
pageSize: 5, // 每页的数据条数
total: 0,
isSelectedAll: false
}
},
methods: {
handleSizeChange(val) {
this.currentPage = 1
this.pageSize = val
},
handleCurrentChange(val) {
this.currentPage = val
},
// 手动点击改变全选/非全选状态触发的事件
checkboxChange(val) {
this.$refs.accessControlTable.chooseAllPages(val)
}
}
}
</script>
<style scoped>
.s-pagination {
text-align: right;
margin-top: 10px;
}
</style>
4、相关属性和方法
属性或者方法名 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
columns | 默认列 | Array | -- | [] |
tableData | 表格数据 | Array | -- | [] |
selectable | 是否可选 | Boolean | true/false | false |
isSelectedAll | 是否跨页全选 | Boolean | true/false | false |
total | 表格数据的总条目数 | Number | -- | 0 |
chooseAllPages | 全选所有页面 | Function | -- | -- |
说明:
isSelectedAll
表示表格数据是否全选,可用户传入对初始状态进行设置,设置为true表示初始默认设置全选;
chooseAllPages
用户手动改变isSelectedAll的值时,需要调用触发chooseAllPages事件;
注意:
要实现跨页全选,传入的tableData数据中必须包含id属性;