在前端开发过程中时常会遇到表格相关的显示与处理。组件库通常都会提供表格组件,对于展示、简单操作这些常用功能通常也够用;但如果需要更多的定制或进行比较复杂的操作,组件库自带的组件可能会捉襟见肘。vxe-table是一个基于Vue.js的功能较为完善的表格组件库,常用功能易于上手,也有很多高级功能。
最近做项目时使用了vxe-table库,期间遇到一个需求(感觉也算是比较常见的场景)
从多页表格中选择一些项,把选中的项返回给后端
然而在实现的时候发现一个问题:由于每次翻页时是重新从后端请求并更新表格展示的数据,所以一旦翻页,这一页已经选中的复选框就丢失了。
最直接的办法当然是不翻页,或者在每次翻页前必须先提交;但显然只有在特定的业务场景中才能这样使用。对于通用情况,不难想到如下思路:
最终的实现方案是使用一个 Set
保存所有选中的项目id,下面详细介绍代码实现。(之前还想了一个有点奇怪的方案,没有采用,写在最后了。)
<vxe-table
ref="paperTable"
row-id="id"
@checkbox-change="togglePaperSelect"
@checkbox-all="toggleAllPaperSelect"
>
<vxe-table-column type="checkbox" width="60"></vxe-table-column>
<vxe-table-column field="title" title="标题"></vxe-table-column>
<!-- other columns -->
</vxe-table>
<vxe-pager
@page-change="handlePageChange"
>
</vxe-pager>
<Button @click="submitAddPaper">确认</Button>
以上是要用到的一些主要配置(有部分省略)
Vue 提供的 ref
属性便于在script中访问表格,省去 getElement 等操作
row-id
给每行设置一个自定义的id,对应行数据中的一个属性,并且需要保证唯一。最好设置这个选项,之后保存和设置的时候更方便。如果不设置他会自动生成一个id,不过自动id每次会改变,无法作为选择和保存的依据。
checkbox-change
checkbox-all
两个事件绑定,顾名思义就是点击每一行中的复选框/表头的全选复选框时触发的事件
由于要进行高频的添加、删除、查询操作,所以比起顺序存储的 Array
,使用哈希表的 Set
明显更加适合。Set 的三个基本操作:
let s = new Set();
s.add(‘a‘); // 添加
s.has(‘a‘); // 删除
s.delete(‘a‘); // 查询
只需要在每次复选框状态发生变动时将对应的项目加入/移出 Set 即可。注意表头的全选框和每行中的复选框对应两个不同事件,不要忘记单独设置。
事件返回的 payload 中可以说是应有尽有,这里只取了两个需要用到的:行 id 和复选框的新状态(true/false)
row-id
配置后 rowid
属性才有意义,因为自动生成的id会改变。如果实在没有适合当id的属性,或者需要取整个对象,可以使用 payload 里的 row
字段checkbox-all
返回的 records 是一个 Array<row>
,每个 row 对象实际上就是表格中的一行数据toggleAllPaperSelect()
函数。分开写只是感觉更直观一点// 在data中定义:selectedPapers: new Set()
togglePaperSelect({ rowid, checked }) {
if (checked) {
this.selectedPapers.add(rowid);
} else {
this.selectedPapers.delete(rowid);
}
},
toggleAllPaperSelect({records, checked}) {
if (checked) {
records.forEach(item => this.selectedPapers.add(item.id))
} else {
records.forEach(item => this.selectedPapers.delete(item.id))
}
},
虽然 vxe-table 提供了 checkRowKeys
配置设置默认选中的行,但是只在表格第一次渲染的时候生效,因此只能手动设置选中的复选框。
不知道为什么 vxe-table 没有提供用 id 设置复选框的 api,只能传入整个 row 作为参数。所以只能先通过 id 获取到 row,再设置这一行的复选框状态为 true。
setCheckboxRow()
getRowById()
都是 table 提供的 api,所以需要先拿到表格的引用handlePageChange({ currentPage, pageSize }) {
// request new page
this.queryPaper().then(() => {
let table = this.$refs.paperTable;
this.paperResult.forEach((paper) => {
if (this.selectedPapers.has(paper.id)) {
table.setCheckboxRow(table.getRowById(paper.id), true);
}
});
});
},
完成上述设置后就可以实现翻页时保存复选框状态的需求了。
因为监听了所有复选框改变的事件,所以 Set 中的内容就是最终所有被选中的内容,因此只需要在提交的时候将 Set 转换为 Array 即可。
let ids = Array.from(this.selectedPapers);
到此为止就实现了需求的所有功能。
其实最初的思路是十分简单粗暴地保存一个列表的列表,即把每一页中选取的 id 分别保存,提交时再合并。虽然最终没有采用,但在写这一版代码的过程中也学习了一些新方法,将代码贴上来记录一下。同时希望分析的过程也能提供一些参考。
这样的思路存在一些优点,比如:
但最终没有使用它是因为它有更多问题:
// 每次翻页时保存当前页中选中的项目,之后加载新页中选中的项目
handlePageChange({ currentPage, pageSize }) {
// save selected ids in current page
let table = this.$refs.paperTable;
let ids = table.getCheckboxRecords().map((item) => item.id);
this.selectedPapers[this.paperPageParam.pageNum] = ids;
// request new page
this.queryPaper().then(() => {
// load saved ids in new page
let currents = this.selectedPapers[currentPage];
if (currents) {
currents.forEach((rowid) => {
table.setCheckboxRow(table.getRowById(rowid), true);
});
}
this.selectedPapers[currentPage] = [];
});
},
// 提交时合并为一个列表
getAllSelectedPapers() {
let table = this.$refs.paperTable;
let ids = table.getCheckboxRecords().map((item) => item.id);
this.selectedPapers[this.paperPageParam.pageNum] = ids;
let definedLists = this.selectedPapers.filter((item) => item);
return [].concat(...definedLists);
},
以上就是我个人在项目过程中遇到的问题以及解决办法,由于个人能力所限,用到的方法可能不是最好的,也可能存在各种问题,欢迎大家讨论与指正。
参考资料:vxe-table 官方文档
原文:https://www.cnblogs.com/skuld-yi/p/14504233.html