实现效果:(可拉代码下来看:vue-demo: vueDemo)
左侧表格为点击查询调用接口查询出来的数据,右侧表格为左侧表格所有选择的数据,由前端实现分页。
两个el-table勾选数据联动更新
实现逻辑:
el-table表格的selection-change方法(element组件的table表格)结合分页组件,自定义一个用于存储(左侧表格)当前页的已勾选数据;一个用于存储所勾选的所有数据(右侧表格所有数据);一个用于存储(右侧表格)当前页的展示数据。
代码实现:
所有代码:
<template><div><Search v-show="showSearch" label-width="100" :limit-height-param="false" @search="handleQuery" @clear="resetQuery"><div><span class="label">账号名称</span><el-selectv-model="queryParams.companyCodeList"placeholder="请选择账号名称"clearablefilterabledefault-first-optionmultiplesize="small"><el-optionv-for="item in companyCodeOptions":key="item.dictValue":label="`${item.dictLabel} | ${item.dictValue}`":value="item.dictValue"></el-option></el-select></div><div><span class="label">账户类型</span><el-selectv-model="queryParams.accountType"placeholder="请选择账户类型"clearablefilterabledefault-first-optionsize="small"><el-optionv-for="dict in accountTypeOptions":key="dict.dictValue":label="dict.dictLabel":value="dict.dictValue"></el-option></el-select></div><div><span class="label">银行卡号</span><el-inputv-model="queryParams.cardNum"placeholder="请输入银行卡号"clearablesize="small"@keyup.enter.native="handleQuery"/></div></Search><el-row :gutter="20"><el-col :span="12"><el-tablev-loading="tableLoading"ref="multipleTable":data="tableData":stripe="true":row-key="getRowKeys":row-style="{height: '54px'}"@selection-change="handleSelectionChange"><el-table-column type="selection" align="center" width="55" fixed /><el-table-column label="序号" type="index" align="center" width="55" fixed /><el-table-column label="公司名称" prop="companyCode" :formatter="tableFormat" align="center" min-width="120" show-overflow-tooltip /><el-table-column label="bankAccountId" prop="bankAccountId" align="center" min-width="130" show-overflow-tooltip /><el-table-column label="账户类型" prop="accountType" :formatter="tableFormat" align="center" min-width="100" show-overflow-tooltip /><el-table-column label="银行卡号" prop="cardNum" align="center" min-width="220" show-overflow-tooltip /><template v-for="(column, columIndex) in tableColumnOption"><el-table-column:key="columIndex":prop="column.prop":label="column.label":min-width="column.width":fixed="column.fixed || false":align="column.align || 'center'":sortable="column.sortable || false":index="columIndex":show-overflow-tooltip="column.tooltip || true"><!-- v-for="(column, columIndex) in tableColumnOption" --><template slot-scope="scope"><span v-if="column.prop === 'status'"><el-tag v-if="scope.row.status == 0" type="danger">禁用</el-tag><el-tag v-else-if="scope.row.status == 1" type="success">启用</el-tag></span><span v-else-if="column.prop === 'type'"><el-tag v-if="scope.row.type == 1">type1</el-tag><el-tag v-if="scope.row.type == 0" type="success">type0</el-tag></span><span v-else>{{ scope.row[column.prop] }}</span></template></el-table-column></template></el-table><paginationv-show="total > 0":total="total":page.sync="queryParams.pageNum":limit.sync="queryParams.pageSize":page-sizes="[3, 5, 10, 20, 30, 50]":pagerCount="5"@pagination="getList"/></el-col><el-col :span="12"><el-tablev-loading="tableLoading"ref="selectMultipleTable":data="selectTableData":row-style="{height: '54px'}"><el-table-column label="序号" type="index" align="center" width="55" fixed /><el-table-column label="账户名称" prop="companyCode" :formatter="tableFormat" align="center" min-width="120" show-overflow-tooltip /><el-table-column label="bankAccountId" prop="bankAccountId" align="center" min-width="130" show-overflow-tooltip /><el-table-column label="账户类型" prop="accountType" :formatter="tableFormat" align="center" min-width="100" show-overflow-tooltip /><el-table-column label="银行卡号" prop="cardNum" align="center" min-width="220" show-overflow-tooltip /><el-table-columnlabel="操作"align="center"fixed="right"width="100"><template slot-scope="scope"><el-buttonclass="delBtn"size="small"type="text"icon="el-icon-delete"@click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table><paginationv-show="selectTotal > 0":total="selectTotal":page.sync="pageNum":limit.sync="pageSize":page-sizes="[3, 5, 10, 20, 30, 50]":pagerCount="5"@pagination="getSelectList"/></el-col></el-row></div>
</template><script>
import Search from '@/components/Search/index'
import {deepClone,
} from '@/utils'
import {getDepartmentsList
} from '@/api/tableLinkage'
import {geBasicAccountType,geBasicCompany,
} from '@/api/selectList'export default {components: {Search,},data() {return {// 遮罩层// loading: false,tableLoading: false,// 显示搜索条件showSearch: true,// 查询参数queryParams: {pageNum: 1,pageSize: 10,companyCodeList: [],cardNum: null,accountType: null,},companyCodeOptions: [],accountTypeOptions: [],codeBsOptions: [],allDataScopeOptions: [{dictValue: '1',dictLabel: '是'},{dictValue: '0',dictLabel: '否'}],total: 0, // 搜索表格(左侧)总条数selectTotal: 0, // 勾选表格(右侧)总条数pageNum: 1, // 勾选表格(右侧)当前页码pageSize: 10, // 勾选表格(右侧)每页数量tableData: [], // 搜索表格(左侧)数据selectedData: [], // 所勾选的所有数据selectTableData: [], // 勾选表格(右侧)当前页展示的数据multipleSelection: [], // 当前页选中的数据idKey: 'bankAccountId', // 标识列表数据中每一行的唯一键的名称// idKey: 'id', // 标识列表数据中每一行的唯一键的名称tableColumnOption: [{prop: 'preview',label: 'preview',width: '200',},{prop: 'status',label: 'status',width: '200',},{prop: 'jobType',label: 'jobType',width: '200',},{prop: 'type',label: 'type',width: '200',},],columnOption: [// { selection: true },// { type: 'index' },// {prop: 'name', label: '名称', width: 160},// { slot: 'action', label: '操作' }],}},created () {// 若初始就有选中数据,需要对选中数据分页// this.selectedData = [...]// this.handleSelectListPaging()this.geBasicAccountType()this.geBasicCompany()this.getList()},methods: {geBasicAccountType () {geBasicAccountType().then(res => {this.accountTypeOptions = res.data})},geBasicCompany () {geBasicCompany().then(res => {this.companyCodeOptions = res.data})},getList () {this.tableLoading = truegetDepartmentsList(this.queryParams).then(res => {console.log("🚀 ~ file: index.vue:24 ~ getDepartmentsList ~ res:", res)// console.log(res.data)this.tableData = res?.rows || []this.total = res?.total || 0// this.handleSelectListPaging()this.$nextTick(() => {// 设置选中this.setSelectRow()})this.tableLoading = false}).catch(() => { this.tableLoading = false })},/** 搜索按钮操作 */handleQuery() {this.queryParams.pageNum = 1this.getList()},/** 重置按钮操作 */resetQuery() {this.queryParams = this.$options.data().queryParamsthis.handleQuery()},tableFormat(row, { property }, value) {return this.selectDictLabel(this[`${property}Options`], value)},// 对表格数据分页getSelectList() {const start = Math.ceil(((this.pageNum - 1) * this.pageSize).toFixed(0)),end = Math.ceil((start + this.pageSize).toFixed(2))this.selectTableData = this.selectedData.slice(start >= 0 ? start : 0, end)// this.$refs.selectMultipleTable.bodyWrapper.scrollTo(0, 0)},handleSelectListPaging() {// 前端分页选中数据表格(左侧表格)this.selectTotal = this.selectedData.length// 总页数=(总数 - 1)/ 每页数量 + 1// 总页数=(总数 + 每页数量 - 1)/ 每页数量// 总页数 = Math.ceil(总条数 / 每页数量)// 使用Math.ceil()函数,返回大于或者等于指定表达式的最小整数,进1法取整const totalPage = Math.ceil(this.selectTotal / this.pageSize)// 原页码大于现数据的总页数,页码取总页数的值this.pageNum = this.pageNum > totalPage ? totalPage : this.pageNum// 页码最小值为1this.pageNum = this.pageNum < 1 ? 1 : this.pageNum// 对表格数据分页this.getSelectList()},// 设置选择表格行唯一标识getRowKeys(row) {return row[this.idKey]},// 设置选中的方法setSelectRow() {if (!this.selectedData || this.selectedData.length <= 0) {return}// 标识当前行的唯一键的名称let idKey = this.idKey// 获取所有选中数据的idconst selectAllIds = this.selectedData.map(row => row[idKey]) || []console.warn("🚀 ~ file: dataAuthenticationUpdateDetail.vue:558 ~ setSelectRow ~ selectAllIds:", selectAllIds)this.$refs.multipleTable.clearSelection() // 先清空原有选中数据this.tableData.forEach(item => {// 查找当前页中是否包含所有选中数据中的id,存在则设置选中(触发 handleSelectionChange ,进而触发记忆选择核心方法 changePageCoreRecordData)if (selectAllIds.indexOf(item[idKey]) >= 0) {this.$refs.multipleTable.toggleRowSelection(item, true)}})},// 多选框选中数据handleSelectionChange(selection) {this.multipleSelection = selectionthis.$nextTick(() => {this.changePageCoreRecordData()})},// 记忆选择核心方法(操作搜索表格——左侧,联动改变勾选表格——右侧)async changePageCoreRecordData() {// 标识当前行的唯一键的名称let idKey = this.idKey// 如果总记忆中还没有选择的数据,那么就直接取当前页选中的数据,不需要后面一系列计算if (this.selectedData.length <= 0) {this.selectedData = this.multipleSelection// 前端分页this.handleSelectListPaging()return}// 总选择里面的key集合const selectAllIds = this.selectedData.map(row => row[idKey]) || []// 获取当前页选中的idlet selectIds = []this.multipleSelection.forEach(row => {selectIds.push(row[idKey])// 如果总选择里面不包含当前页选中的数据,那么就加入到总选择集合里// (左侧表格勾选数据,加入到右侧表格中)if (selectAllIds.indexOf(row[idKey]) < 0) {this.selectedData.push(row)}})// 得到当前页没有选中的idlet noSelectIds = []this.tableData.map(row => {if (selectIds.indexOf(row[idKey]) < 0) {noSelectIds.push(row[idKey])}})noSelectIds.map(id => {if (selectAllIds.indexOf(id) >= 0) {for (let i = 0; i < this.selectedData.length; i++) {if (this.selectedData[i][idKey] === id) {console.warn('总选择中有未被选中的,那么就删除这条:', id)// 如果总选择中有未被选中的,那么就删除这条// (左侧表格取消勾选数据,从右侧表格中删除这条数据)this.selectedData.splice(i, 1)break}}}})// 前端分页this.handleSelectListPaging()},// 删除handleDelete(row) {// 标识当前行的唯一键的名称let idKey = this.idKey// 过滤所有选中数据,将要删除的这条数据过滤掉this.selectedData = this.selectedData.filter(item => {return item[idKey] !== row[idKey]})// 查找当前页是否存在需要删除的这条数据const findRow = this.tableData.find(item => { return row[idKey] === item[idKey] })if (findRow) {// 当前页存在要删除的这条数据,取消选中状态// (触发 handleSelectionChange ,进而触发记忆选择核心方法 changePageCoreRecordData)this.$refs.multipleTable.toggleRowSelection(findRow)} else {// 当前页不存在需要删除的这条数据,直接调用前端分页方法,对所有选中数据(已剔除需要删除的这条数据)重新进行分页this.handleSelectListPaging()}},// 处理参数handleParams() {const param = deepClone(this.form)const bankAccountIdList = this.selectedData.map(item => item.bankAccountId)const params = {id: param.id,codeBs: param.codeBs,allDataScope: param.allDataScope,bankAccountIdList: bankAccountIdList}return params},}
}
</script><style>
</style>
关键步骤:
当点击搜索查询左侧数据、或切换分页、切换页面大小,调用后端接口(this.getList())的时候,需要调用选中方法(this.setSelectRow()):
// 设置选中的方法setSelectRow() {if (!this.selectedData || this.selectedData.length <= 0) {return}// 标识当前行的唯一键的名称let idKey = this.idKey// 获取所有选中数据的idconst selectAllIds = this.selectedData.map(row => row[idKey]) || []console.warn("🚀 ~ file: dataAuthenticationUpdateDetail.vue:558 ~ setSelectRow ~ selectAllIds:", selectAllIds)this.$refs.multipleTable.clearSelection() // 先清空原有选中数据this.tableData.forEach(item => {// 查找当前页中是否包含所有选中数据中的id,存在则设置选中// (触发 handleSelectionChange ,进而触发记忆选择核心方法 changePageCoreRecordData)if (selectAllIds.indexOf(item[idKey]) >= 0) {this.$refs.multipleTable.toggleRowSelection(item, true)}})},
设置选中方法,先清空左侧表格原有选中数据,然后根据右侧表格所有选中数据的id(自己设定的key)来判断右侧表格当前页是否存在选中数据,存在则调用el-table的toggleRowSelection方法,将数据勾选上。(toggleRowSelection方法会触发el-table的selection-change【即触发handleSelectionChange】进而触发记忆选择核心方法 changePageCoreRecordData)
// 多选框选中数据handleSelectionChange(selection) {this.multipleSelection = selectionthis.$nextTick(() => {this.changePageCoreRecordData()})},
勾选右侧表格数据触发记忆选择核心方法 changePageCoreRecordData:
// 记忆选择核心方法(操作搜索表格——左侧,联动改变勾选表格——右侧)async changePageCoreRecordData() {// 标识当前行的唯一键的名称let idKey = this.idKey// 如果总记忆中还没有选择的数据,那么就直接取当前页选中的数据,不需要后面一系列计算if (this.selectedData.length <= 0) {this.selectedData = this.multipleSelection// 前端分页this.handleSelectListPaging()return}// 总选择里面的key集合const selectAllIds = this.selectedData.map(row => row[idKey]) || []// 获取当前页选中的idlet selectIds = []this.multipleSelection.forEach(row => {selectIds.push(row[idKey])// 如果总选择里面不包含当前页选中的数据,那么就加入到总选择集合里// (左侧表格勾选数据,加入到右侧表格中)if (selectAllIds.indexOf(row[idKey]) < 0) {this.selectedData.push(row)}})// 得到当前页没有选中的idlet noSelectIds = []this.tableData.map(row => {if (selectIds.indexOf(row[idKey]) < 0) {noSelectIds.push(row[idKey])}})noSelectIds.map(id => {if (selectAllIds.indexOf(id) >= 0) {for (let i = 0; i < this.selectedData.length; i++) {if (this.selectedData[i][idKey] === id) {console.warn('总选择中有未被选中的,那么就删除这条:', id)// 如果总选择中有未被选中的,那么就删除这条// (左侧表格取消勾选数据,从右侧表格中删除这条数据)this.selectedData.splice(i, 1)break}}}})// 前端分页this.handleSelectListPaging()},
如果总记忆中还没有选择的数据,那么就直接取当前页选中的数据,不需要后面一系列计算。
如果已经有已选择的数据了,则先获取当前页选中数据的值集,获取当前页没有选中的数据的值集。如果总选择里面不包含当前页选中的数据,那么就将这些数据加入到总选择集合里(左侧表格勾选数据,加入到右侧表格中)。如果总选择中有未被选中的数据,那么就从总选择表格中删除这些数据(左侧表格取消勾选数据,从右侧表格中删除这条数据)。
这里经过上述计算后,得到最新的右侧表格全部数据,这时就需要对右侧表格重新分页:
(涉及到总页面数的计算,看这里Javascript 前端分页——根据页面大小(pageSize)和总行数(total)计算总页面数(totalPage)-CSDN博客)
// 对表格数据分页getSelectList() {const start = Math.ceil(((this.pageNum - 1) * this.pageSize).toFixed(0)),end = Math.ceil((start + this.pageSize).toFixed(2))this.selectTableData = this.selectedData.slice(start >= 0 ? start : 0, end)// this.$refs.selectMultipleTable.bodyWrapper.scrollTo(0, 0)},handleSelectListPaging() {// 前端分页选中数据表格(左侧表格)this.selectTotal = this.selectedData.length// 总页数=(总数 - 1)/ 每页数量 + 1// 总页数=(总数 + 每页数量 - 1)/ 每页数量// 总页数 = Math.ceil(总条数 / 每页数量)// 使用Math.ceil()函数,返回大于或者等于指定表达式的最小整数,进1法取整const totalPage = Math.ceil(this.selectTotal / this.pageSize)// 原页码大于现数据的总页数,页码取总页数的值this.pageNum = this.pageNum > totalPage ? totalPage : this.pageNum// 页码最小值为1this.pageNum = this.pageNum < 1 ? 1 : this.pageNum// 对表格数据分页this.getSelectList()},
前面已经讲了左侧表格勾选,取消勾选时,对右侧表格的联动改变。下面来讲,右侧表格删除一条数据,如何让左侧表格取消勾选。其实也很简单,就是从右侧表格全部数据中,将要删除的数据去掉,若这条数据在左侧表格当前页存在,则触发el-table的toggleRowSelection(row,false)方法,取消勾选。若这条数据左侧表格在当前页不存在,则直接对右侧表格重新分页就好了。因为下次当左侧表格重新调用getList()方法(触发记忆选择核心方法 changePageCoreRecordData)的时候,右侧表格内已经没有要删除的数据了。
// 删除handleDelete(row) {// 标识当前行的唯一键的名称let idKey = this.idKey// 过滤所有选中数据,将要删除的这条数据过滤掉this.selectedData = this.selectedData.filter(item => {return item[idKey] !== row[idKey]})// 查找当前页是否存在需要删除的这条数据const findRow = this.tableData.find(item => { return row[idKey] === item[idKey] })if (findRow) {// 当前页存在要删除的这条数据,取消选中状态// (触发 handleSelectionChange ,进而触发记忆选择核心方法 changePageCoreRecordData)this.$refs.multipleTable.toggleRowSelection(findRow)} else {// 当前页不存在需要删除的这条数据,直接调用前端分页方法,对所有选中数据(已剔除需要删除的这条数据)重新进行分页this.handleSelectListPaging()}},
参考文章:
element的table表格多选结合分页实现已选中的记忆功能和实现表格数据和已选数据动态同步更新_element table分页多选-CSDN博客