_makabaka

表格分页多选默认回显实现

element的表格可以使用 row-keyreserve-selection 来实现多选翻页回显,但是在使用过程中却出现了,翻页之后,翻页保存的数据丢失问题

复现前准备

首先是使用 el-tableel-pagination 搭建好基本的页面,包括获取表格数据事件,翻页事件以及表格的 selection-changerow-keyreserve-selectionref 这些东西。

页面模板

<el-table
  :data="list"
  :row-key="getRowKey"
  @selection-change="onSelectionChange"
  ref="multipleTable"
>
  <el-table-column	type="selection" width="55"	:reserve-selection="true"></el-table-column>
  <el-table-column label="姓名" prop="name"></el-table-column>
  <el-table-column label="sysId" prop="sysId"></el-table-column>
</el-table>
<el-pagination  :current-page.sync="currentPage"  @current-change="getTableData"  :total="200"></el-pagination>

然后是表格(假)数据获取(翻页事件)、selection-change 事件、getRowKey 事件的实现。

获取表格(假)数据

// 获取表格数据
getTableData() {
  const list = []
  for (let i = 0; i < 10; i++) {
	list.push({
	  sysId: this.currentPage + '' + i,
	  name: `用户-${this.currentPage + '' + i}`
	})
  }
  this.list = list
  // 勾选回显
  this.$nextTick(() => {
	this.tableEcho()
  })
}

selection-change事件

根据官方文档,这个事件接收一个 selection 参数,该参数是一个数组,数组中存放着表格中被勾选的行的数据,如果使用了 reserve-selection 的话,那么即使翻页了重新勾选,这个 selection 中也会保存着之前勾选的数据。

举个例子来说,假如我在第一页,勾选了ABC3行数据,然后在第二页勾选了DE2行数据,那么当我们勾选E这一行的时候 selection 参数中是存在5个元素的,就是ABCDE这5个。
根据上面的描述,我们只需要在 selection-change 事件中,将 selection 参数赋值给一个变量就可以拿到分页表格所勾选的全部数据了。

因此这个 selection-change 的逻辑非常简单,只需要做简单的赋值操作

onSelectionChange(selection) {
  this.selection = selection
}

getRowKey方法

这个根据表格中的数据来确定返回的数据,主要是不要存在重复的就可以了,比如说上面的假数据中,sysId是肯定不会重复的,因此 getRowKey 方法中返回表格数据的 sysId

getRowKey(row) {
  return row.sysId
}

tableEcho勾选回显方法

这个方法中调用表格实例的 toggleRowSelection 方法进行勾选回显。

tableEcho() {
  const tableRef = this.$refs.multipleTable
  const sysIds = this.selection.map(i => i.sysId)
  this.list.forEach(i => {
	if (sysIds.includes(i.sysId)) {
	  tableRef.toggleRowSelection(i, true)
	}
  })
}

(状态)变量定义

最后就是在 data 中定义几个状态,比如说表格数据 list ,当前页面 currentPage,当前已选行 selection,然后就可以开始复现了。

复现过程

首先分成两种情况:selection 状态不存在默认值(空数组)和存在默认值(非空数组)。

不存在默认值

selection 为空数组的时候,无论如何勾选,翻页,都可以正常使用。

存在默认值

selection 为非空数组的时候,问题就出现了,假设 selection 中存储的是第一页的第1/2行数据和第3页的第4/5条数据,也就是说我们希望表格的第一页和第三页存在默认勾选的数据,注意是默认勾选。

selection: [
	{ "sysId": "11", "name": "用户-11" },
	{ "sysId": "12", "name": "用户-12" },
	{ "sysId": "33", "name": "用户-33" },
	{ "sysId": "34", "name": "用户-34" },
	{ "sysId": "35", "name": "用户-35" }
]

然后进入到页面,我们可以发现,表格的第一页确实把1/2行的数据勾选起来了,如下图

image.png

但是问题是,当我们翻到第3页的时候,发现4/5却并没有被勾选起来,如下图

image.png

通过Vue开发者工具可以看到 selection 状态变成了只保存了第一页的勾选行,而丢失了第三页的勾选行。

image.png

通过在 selection-change 事件中 debugger 找到了原因,当我们获取到第一页表格数据的时候,进行表格勾选回显,此时 selection 参数中只存在第一页中默认被勾选的两行,然后 selection 被赋值给了 this.selection,进而导致 this.selection 中原本存在的第三页默认勾选的数据被抹掉了,所以当表格翻到第三页的时候,本来该被勾选的行却没被勾选。

思考

1. 多加一个默认值状态(废弃)

因为 selction 状态被 selection 参数给冲掉了,所以我就想着多定义一个 defaultSelection 状态,里面存放需要进行默认回显的数据,然后我们的 tableEcho 方法就要改成下面这样:

tableEcho() {
  const tableRef = this.$refs.multipleTable
  //============= 这里变啦 👇
  const sysIds = this.defaultSelection.map(i => i.sysId)
  this.list.forEach(i => {
	if (sysIds.includes(i.sysId)) {
	  tableRef.toggleRowSelection(i, true)
	}
  })
}

后来发现不行,因为没有去改变 defaultSelection 的逻辑,就会导致假如我把某一行数据取消勾选了,但是翻页之后还是会被重新勾选回来。

2. 不使用selection-change事件

问题出现的本质原因是, 我把被勾选项这个东西交给了 el-table 组件自己维护,如果我不使用 selection-change 事件,而是使用 select 事件,自己来对 selection 状态进行维护,自己来控制 selection 状态中元素的增删,这不就可以解决了吗?

/**
 * 表格复选框勾选时事件
 * @param selection 当前勾选的数据
 * @param row 当前被勾选的行数据
 */
onSelect(selection, row) {
  const {sysId} = row
  const index = this.selection.findIndex(i => i.sysId === sysId)
  // 如果已经包含该项 说明本次是取消勾选
  if (index > -1) {
	this.selection.splice(index, 1)
  } else {
	// 否则,将已勾选的push进去
	this.selection.push(row)
  }
}

然后删掉 el-tableselection-change 事件,添加上 select 事件。

<el-table
  :data="list"
  :row-key="getRowKey"
  style="width: 100%"
  @select="onSelect"
  @select-all="onSelectAll"
  ref="multipleTable"
>

因为存在全选逻辑,所以全选这里也要加上事件 onSelectAll,全选事件只有一个参数 selection,表示当前表格所有勾选的数据,每次变化的时候,它并不会告诉我们本次操作是全选还是全不选。

但是我们可以通过对比 this.selection 状态和 selection 参数的长度来判断出本次操作是对当前页的全选还是全不选。

因为在执行 select-all 之前,必然有着 this.selection.length === selection.length 的关系,当进行全选操作时,就会有 selection.length > this.selection.length,反之,则有 selection.length < this.selection.length

什么?你说假设某一页默认被全部勾选了呢?嘿嘿,el-table 还是挺靠谱的,假如某一页默认全勾选,那么它就只有全不选这个操作。所以上面的那个判断关系,还是靠谱的。

onSelectAll(selection) {
  /**
   * this.selection.length > selection.length
   * 表示全不选操作
   * 那么就要把存在于this.selection中
   * 且不存在于selection中的元素从this.selection中删除
   * */
  if (this.selection.length > selection.length) {
	const sysIds = selection.map(i => i.sysId)
	this.selection = this.selection.filter(item => sysIds.includes(item.sysId))
  }
  /**
   * this.selection.length < selection.length
   * 表示全选操作
   * 把存在于selection且不存在于this.selection的元素
   * 添加到this.selection中
   */
  else if (this.selection.length < selection.length) {
	const sysIds = this.selection.map(i => i.sysId)
	const unExist = selection.filter(i => !sysIds.includes(i.sysId))
	this.selection.push(...unExist)
  }
}

// ===ChatGPT 代码简化 ==感觉不对=有空分析一下=简化后好像没有对全选全不选的判断===
onSelectAll(selection) {
  const sysIds = selection.map(i => i.sysId)
  const exist = this.selection.filter(item => sysIds.includes(item.sysId))
  const unExist = selection.filter(i => !sysIds.includes(i.sysId))
  this.selection = exist.concat(unExist)
}

测试了一下,嘎嘎棒。

结论

假如表格不存在默认勾选这种需求,那么使用下面的这个方法进行回显是最方便的。

row-key + reserve-selection + @selection-change + ref 

假如表格存在默认勾选回显,那么则应该使用下面这种方法来实现需求

row-key + reserve-selection + @select + @select-all + ref 

posted on 2023-07-11 13:57  __makabaka  阅读(282)  评论(0编辑  收藏  举报

导航