element-plus中el-table分页勾选记住勾选内容并限制数量

文章介绍了如何在升级到Vue3和Element-Plus的项目中处理列表分页时保持跨页选择的状态,包括使用row-key、reserve-selection以及自定义方法来管理选中项,同时实现数量限制和全选/取消全选功能。

需求背景,定制需求,列表分页+数量限制提交

前提条件:由于历史项目升级到vue3+element-plus ,故此写法还是偏于vue2的声明写法,没有用到组合式

有过研究的小伙伴们,肯定发现了,element 和element-plus中对于跨页选择保持勾选这个操作,源码进行更改,如果用element的话,用row-key + reserve-selection 即可达到切换分页时,记住之前分页的数据内容,但是element-plus可不行。

下面在线模拟一波

 然后同样的后端分页使用element,切换分页时也可以达到记忆之前勾选内容,但是element-plus按照这种方式并不能实现。

先来看一波element-plus实现的样例:

 这边我设置勾选上限25个,后端分页,切换分页记住之前选择,并达到上限之后,复选框禁用,搭配上全选功能也实现限制。

前提准备:

1.使用row-key定唯一字段、reserve-selection在type=selection勾选框栏(开启跨页记录) 使用selectable属性来控制是否可以勾选

2.尝试@select和@select-change 两个方法,发现select-change不记录跨页数据,故而采用@select,勾选单个勾选框触发的事件,可以知道当前点击行的row信息

3. @select-all 方法全选该页所有项,参数是目前选中的数据,开启跨页记录(含跨页数据)

实现思路:

1.单个选择时,判断选中的行数据是否存在选中数据数组中(currentSelection),不存在就加入存在就剔除

2.全部选择时:分两种情况 @select-all = handlerSelectAll(val)

a: val 为空 取消当前页勾选,并且其他页都没有数据   ===> 直接维护currentSelection 置空即可

b: val有数据 分两种情况

      b1: 当前页的全选

       比较当前页tableData和val差集(diff),如果tableData的数据都在val中,说明是全选,将差集添加到currentSelection即可

        b2: 其他页有数据勾选,当前页取消全选

        如果tableData的数据在val中不存在,则为取消全选,从currentSelection 剔除这些数据即可

    上述分析中再加入上限设置,全选时判断,增加差集过程中,currentSelection + diff 和 上限(maxLength) 作对比,超过限制就截断,不足就直接添加

分析完毕:下面是展示代码

<el-table
   v-show="!tableLoading && tableData.length"
   max-height="330"
   ref="multipleTable"
   :data="tableData"
   class="userCameraTable"
   row-class-name="rowClassName"
   header-row-class-name="headerRowClassName"
   border
   row-key="deviceId"
   @select="handlerSelect"
   @select-all="handlerSelectAll"
 >
   <el-table-column
     label
     width="50"
     type="selection"
     v-if="!isShowDetail"
     :selectable="handlerSelectable"
     :reserve-selection="true"
   />
  ....
handlerSelectable(row) {
  // 检查当前行是否已被选择
  const isChecked = this.currentSelection.includes(row.deviceId)
  // 如果当前行已被选择,则允许取消选择
  if (isChecked) {
     return true
  } else {
    // 如果当前行未被选择,并且已选择的行数超过max,则禁止选择新的行
     return this.currentSelection.length < this.maxLength
   }
 },
 handlerSelect(val, row) {
    this.currentSelection.indexOf(row.deviceId) === -1
      ? this.currentSelection.push(row.deviceId)
      : this.currentSelection.splice(this.currentSelection.indexOf(row.deviceId), 1)
 },
 handlerSelectAll(val) {
   if (val.length) {
     // 进来此处说明:
     // 1 当前页的全选 2 其他页有数据然后当前页的取消全选
     // 比较全选或者取消全选与当前页的数据,得到差集
     // 如果tableData中的数据在val中不存在,则说明是取消全选,需要从currentSelection中移除
     // 如果tableData中所有的数据都在val中存在,则说明是全选,需要将差集添加到currentSelection中
     const isAllSelect = this.tableData.every(item =>
     val.some(valItem => valItem.deviceId === item.deviceId)
    )
    if (isAllSelect) {
       // 全选中新增的差集
      const diff = val.filter(
      item => !this.currentSelection.some(deviceId => deviceId === item.deviceId)
     )
     if (this.currentSelection.length + diff.length > this.maxLength) {
         this.currentSelection = this.currentSelection.concat(
           diff.splice(0, this.maxLength - this.currentSelection.length)
              .map(item => item.deviceId))
         diff.forEach(item => this.$refs.multipleTable.toggleRowSelection(item, false))
     } else {
         this.currentSelection = this.currentSelection.concat(diff.map(item => item.deviceId))
     }
    } else {
        this.currentSelection = this.currentSelection.filter(
          deviceId => !this.tableData.some(item => item.deviceId === deviceId)
        )
      }
    } else {
      // 进来此处说明:
      // 其他页并无勾选数据,且当前页取消勾选
      this.currentSelection = []
    }
  },


// getTableData 
async getCameraList() {
...
const res = await this.$api.getCameraListByGroup(data)
  if (res.cameraList && res.cameraList.length) {
      this.totalNum = res.pageInfo.totalNum
      this.tableData = res.cameraList
// 需要这部分,每当切换分页获取新一页的tableData时,双循环遍历把当前tableData中有存在维护已选数据中的id手动设置勾选
      if (this.currentSelection.length) {
            this.$nextTick(() => {
              this.currentSelection.forEach(row => {
                this.tableData.forEach(item => {
                  if (item.deviceId === row) {
                    this.$refs.multipleTable?.toggleRowSelection(item, true)
                  }
                })
              })
            })
          } else {
            this.$refs.multipleTable?.clearSelection()
          }
          this.$nextTick(() => {
            this.$refs.multipleTable?.doLayout()
            this.$refs.multipleTable?.setScrollTop(0)
          })
        }
...
      } 
    }

至于打开弹框再回显数据,就让后端返参,返回所有的勾选数据,前端直接赋值ids给currentSelection即可。

<think> 我们正在处理关于element-plusel-table-v2虚拟表格多选框取消勾选状态的问题。根据引用内容,我们知道这个表格用于大数据量场景,且实现了多选功能,包括全选、反选、已选行展示等。 关键点: 1. 引用[1]提到:使用独立的响应式数组`selectedRows`来存储选中的行对象,且每行数据有一个`_selected`字段表示选中状态。 2. 表头复选框绑定一个计算属性`isAllSelected`,用于全选/取消全选。 3. 自定义列渲染器插入复选框组件,双向绑定每行的`_selected`字段。 因此,要取消多选框的勾选状态,实际上就是要修改行的`_selected`字段为false,且从`selectedRows`数组中移除对应的行。 根据引用[1]的描述,我们有以下思路: - 如果是要取消单个行的勾选,那么直接设置该行的`_selected`为false,且从`selectedRows`中移除该行。 - 如果是要取消所有行的勾选(即清空选择),那么遍历所有数据,将每一行的`_selected`设置为false,清空`selectedRows`数组。 在引用[1]中还提到,动态数据生成时需要重置选中状态,所以我们可以参考重置的逻辑来实现取消勾选。 具体步骤: 1. 在定义表格数据时,确保每一行都有一个`_selected`属性(初始为false表示未选中)。 2. 在自定义列中,我们使用复选框,绑定到该行的`_selected`属性。当用户取消勾选时,该行的`_selected`会变为false,同时我们需要从`selectedRows`中移除这一行。 3. 另外,我们还需要一个响应式数组`selectedRows`来存储当前选中的行(即那些`_selected`为true的行)。 所以,取消勾选状态有两种情况: 情况一:取消某一行的勾选 我们可以通过监听单行复选框的变化,当复选框从选中变为取消选中时,执行移除操作。 情况二:取消所有行的勾选(清空选择) 我们可以提供一个清空选择的方法,将`selectedRows`数组清空,遍历所有数据将每一行的`_selected`设置为false。 根据引用[1]的代码结构,我们可以这样实现: 首先,在Vue组件的data(或setup函数中使用ref/reactive)中,我们会有: - tableData: 表格数据(每行有_selected属性) - selectedRows: 存储选中的行 然后,我们有一个方法用于处理单行复选框的变化(或者我们直接使用双向绑定,然后在变化时更新selectedRows): - 当某一行的复选框被取消勾选时,该行的_selected变为false,同时我们需要在selectedRows中删除这一行。 但是,为了确保selectedRows的准确性,我们可能会在每次变化时根据tableData重新计算?但这样效率不高。更好的做法是直接操作。 我们可以这样设计: 1. 在自定义列中,我们使用一个复选框组件,使用v-model绑定到该行的_selected属性,监听变化事件,在变化时调用一个方法(比如`handleSelectChange(row)`)来更新selectedRows。 2. 或者,我们可以使用一个watch来深度监听tableData,但数据量大的时候性能不好,所以不推荐。 推荐在每次复选框变化时,我们直接更新selectedRows: - 当选中时,将行添加到selectedRows; - 当取消选中时,从selectedRows中移除该行。 同时,全选/取消全选也需要更新所有行的_selected和selectedRows。 因此,我们分别实现: 1. 单行选中/取消选中的处理函数: ```javascript function handleSelectChange(row) { if (row._selected) { // 如果该行被选中,则添加到selectedRows selectedRows.value.push(row); } else { // 如果该行被取消选中,则从selectedRows中移除 const index = selectedRows.value.findIndex(item => item.id === row.id); // 假设每行有唯一id if (index !== -1) { selectedRows.value.splice(index, 1); } } } ``` 在模板中,我们可以为复选框添加一个change事件: ```vue <el-checkbox v-model="row._selected" @change="handleSelectChange(row)"></el-checkbox> ``` 2. 清空所有选择(取消所有勾选): ```javascript function clearAllSelection() { // 清空selectedRows selectedRows.value = []; // 将tableData中每一行的_selected设置为false tableData.value.forEach(row => { row._selected = false; }); } ``` 另外,全选/取消全选的功能(表头复选框): 计算属性isAllSelected(用于表头复选框的绑定): ```javascript const isAllSelected = computed({ get() { // 当前页数据不为空且每一行都被选中 return tableData.value.length > 0 && tableData.value.every(row => row._selected); }, set(value) { // 当表头复选框被点击时,设置当前页所有行的选中状态 tableData.value.forEach(row => { row._selected = value; // 需要同步更新selectedRows if (value) { // 如果全选,将当前页的行都加入(注意去重) const newRows = tableData.value.filter(row => !selectedRows.value.some(item => item.id === row.id)); selectedRows.value = [...selectedRows.value, ...newRows]; } else { // 取消全选,将当前页的行从selectedRows中移除 selectedRows.value = selectedRows.value.filter(item => !tableData.value.some(row => row.id === item.id)); } }); } }); ``` 但是注意,上述全选的处理在虚拟滚动中可能当前页不是全部数据?这里引用[1]提到的是“当前页全选/取消全选”,所以这里的tableData指的是当前可见区域的数据(或者当前分页的数据,但el-table-v2是虚拟滚动,没有分页,所以这里的“当前页”实际上是指当前渲染的数据?但虚拟滚动是动态渲染的,所以全选操作只对当前渲染出来的数据有效?这取决于需求。根据引用[1],需求是“支持表头复选框,实现当前页全选/取消全选”,所以这里只操作当前可见的数据。 如果用户需要取消所有行的勾选(包括不在当前可见区域的行),那么我们需要操作整个数据集(而不仅仅是tableData,因为tableData可能只是当前渲染的一部分)。因此,我们可能需要一个完整的数据源(比如allData)来存储所有数据,然后遍历allData来取消所有选择。 因此,在清空选择时,我们不应该只操作当前tableData(它是当前渲染的数据,也就是可见区域的数据),而应该操作整个数据源。所以,我们需要一个完整的数据源(假设为allData)来存储所有数据。 调整思路: - 我们维护一个完整的数据源allData,这个数组包含所有行数据,且每一行都有_selected属性。 - tableData是el-table-v2要渲染的数据,它可能是allData的一个子集(虚拟滚动动态截取)。 - 当清空选择时,我们需要遍历allData,将每一行的_selected设置为false,且清空selectedRows。 因此,修改清空选择的方法: ```javascript function clearAllSelection() { selectedRows.value = []; allData.value.forEach(row => { row._selected = false; }); } ``` 同样,在单行选择变化时,我们操作的是allData中对应行的_selected(因为row实际上就是allData中的某个对象,因为tableData是引用的allData的一部分)。所以,在单行变化时,我们不需要操作allData,因为row就是allData中的对象,直接修改row._selected就是修改了allData中的对应行。 但是,在虚拟滚动中,tableData是动态变化的,所以我们不能直接依赖tableData来代表所有数据。因此,我们使用allData作为所有数据的存储,而tableData是el-table-v2的:data属性绑定的数据(由虚拟滚动组件管理,我们不需要手动截取,但我们需要保证allData是响应式的,el-table-v2会根据滚动位置自动从allData中取数据渲染)。 所以,在组件中,我们可能有: ```javascript const allData = ref([]); // 所有数据 // 在需要的时候生成或获取所有数据 // 在表格组件中,绑定的是allData <el-table-v2 :data="allData" ... /> ``` 但是注意,虚拟滚动表格(el-table-v2)通常需要固定行高,且能够计算总高度,所以绑定整个allData是可以的,因为它内部会做虚拟滚动。 因此,总结: - 要取消单个勾选:将该行的_selected设为false,将该行从selectedRows中移除(通过handleSelectChange方法)。 - 要取消所有勾选:调用clearAllSelection方法,清空selectedRows,将allData中每行的_selected设为false。 另外,在引用[1]中提到,动态数据生成时重置选中状态,所以当数据变化(比如重新生成数据)时,我们也应该清空选中状态。 所以,用户想要取消勾选状态,可以根据具体场景调用上述方法。 注意:在自定义列中,我们展示的是当前行的_selected,而该行属于allData,所以修改会直接反映到数据源。 因此,回答用户问题:如何取消el-table-v2虚拟化表格多选框的勾选状态? - 如果是取消某个特定行的勾选:直接设置该行数据对象的_selected属性为false,从selectedRows数组中移除该行。 - 如果是取消所有行的勾选:清空selectedRows数组,遍历整个数据源(allData)将每一行的_selected属性设置为false。 在代码实现上,我们可以提供两个方法分别处理这两种情况。 示例代码: 在Vue组件中: ```vue <template> <div> <el-button @click="clearAllSelection">清空选择</el-button> <el-table-v2 :columns="columns" :data="allData" :height="500" :row-height="60" /> </div> </template> <script setup> import { ref, computed } from 'vue'; // 假设我们有一个生成数据的函数 const generateData = (count) => { return Array.from({ length: count }, (_, index) => ({ id: index, name: `Name ${index}`, _selected: false, // 初始未选中 })); }; // 所有数据 const allData = ref(generateData(10000)); // 选中的行 const selectedRows = ref([]); // 清空所有选择 const clearAllSelection = () => { selectedRows.value = []; allData.value.forEach(row => { row._selected = false; }); }; // 处理单行选择变化 const handleSelectChange = (row) => { if (row._selected) { // 选中时添加 selectedRows.value.push(row); } else { // 取消选中时移除 const index = selectedRows.value.findIndex(item => item.id === row.id); if (index !== -1) { selectedRows.value.splice(index, 1); } } }; // 定义列,其中选择列自定义 const columns = [ { key: 'selection', width: 50, cellRenderer: ({ rowData }) => ( <el-checkbox v-model={rowData._selected} onChange={() => handleSelectChange(rowData)} /> ), headerRenderer: () => ( <el-checkbox modelValue={isAllSelected.value} onUpdate:modelValue={(val) => { isAllSelected.value = val; }} /> ), }, // 其他列... ]; // 全选计算属性 const isAllSelected = computed({ get() { // 这里注意:我们只考虑当前渲染的数据?但虚拟滚动没有分页,所以我们无法获取当前“页”的所有数据(因为虚拟滚动是动态的,没有固定的页) // 但是,根据需求,表头全选应该是针对所有数据的还是当前可视区域的?引用[1]说的是“当前页全选”,但虚拟滚动没有分页,所以这里可能是针对所有数据的全选?或者需求中的“当前页”指的是当前可视区域? // 实际上,在虚拟滚动中,表头全选通常是指全选所有数据,而不是当前可视区域。但是引用[1]没有明确说明,所以我们按当前可视区域全选是不现实的(因为用户可能滚动查看,当前可视区域变化)。 // 因此,根据常见的需求,表头全选应该是指全选所有数据。 // 所以,这里我们判断是否所有数据都被选中 return allData.value.length > 0 && allData.value.every(row => row._selected); }, set(value) { // 全选或取消全选所有数据 allData.value.forEach(row => { row._selected = value; }); if (value) { // 全选 selectedRows.value = [...allData.value]; } else { // 取消全选 selectedRows.value = []; } } }); </script> ``` 注意:上面的代码在表头全选的处理中,我们操作的是所有数据(allData),所以全选/取消全选会影响整个数据集。 但是,引用[1]中说的是“支持表头复选框,实现当前页全选/取消全选”,而虚拟滚动没有分页,所以“当前页”可能不适用。如果需求确实是只全选当前可视区域的行,那么我们需要获取当前可视区域的行数据,但el-table-v2没有直接提供当前可视区域的数据。因此,我们这里按照全选所有数据来实现。 如果需求是只全选当前可视区域(这在虚拟滚动中不常见),那么可能需要更复杂的处理,这里不展开。 因此,用户想要取消勾选状态,可以: - 取消单个:点击该行的复选框,会触发handleSelectChange,从而取消。 - 取消所有:调用clearAllSelection方法(例如通过一个清空按钮)。 另外,用户可能想通过代码取消某个特定行的选中,那么可以直接设置该行的_selected为false,更新selectedRows(移除该行)。 所以,回答用户的问题: 要取消el-table-v2虚拟化表格多选框的勾选状态,可以通过以下方式: 1. 取消单个勾选:直接设置该行数据对象的`_selected`属性为`false`,确保从`selectedRows`数组中移除该行。 2. 取消所有勾选:调用清空选择的方法(如示例中的`clearAllSelection`),该方法会遍历所有数据将`_selected`设为`false`,清空`selectedRows`。 注意:在实现中,我们使用了响应式数据,所以视图会自动更新。 最后,根据用户的问题,他可能只需要清空所有选择的方法,所以重点在`clearAllSelection`方法。 </think> 要取消 Element Plus 中 `el-table-v2` 虚拟化表格的多选框勾选状态,核心是操作数据源中的选中状态字段和存储选中行的数组。以下是具体实现方法: ### 解决方案 1. **数据准备** 在数据源中添加 `_selected` 字段标记选中状态,使用 `selectedRows` 数组存储选中行: ```typescript const tableData = ref([]); // 表格数据源 const selectedRows = ref([]); // 存储选中行 // 初始化数据(示例) const generateData = (count) => { return Array.from({ length: count }, (_, i) => ({ id: i + 1, name: `Item ${i + 1}`, _selected: false // 初始未选中 })); }; ``` 2. **取消单个勾选** 在自定义列中绑定复选框的 `v-model` 到行的 `_selected` 属性: ```vue <el-table-v2 :columns="columns" :data="tableData"> <template #column-check="{ rowData }"> <el-checkbox v-model="rowData._selected" @change="handleSelectChange(rowData)" /> </template> </el-table-v2> ``` ```typescript const handleSelectChange = (row) => { if (!row._selected) { // 取消勾选时从选中数组中移除 const index = selectedRows.value.findIndex(item => item.id === row.id); if (index !== -1) { selectedRows.value.splice(index, 1); } } else { // 勾选时添加 selectedRows.value.push(row); } }; ``` 3. **取消所有勾选(清空选择)** 清空 `selectedRows` 重置所有行的 `_selected` 状态: ```typescript const clearAllSelection = () => { // 重置所有行的选中状态 tableData.value.forEach(row => (row._selected = false)); // 清空选中数组 selectedRows.value = []; }; ``` 在模板中添加清空按钮: ```vue <el-button @click="clearAllSelection">清空选择</el-button> ``` ### 关键实现说明 1. **状态同步** `selectedRows` 数组与数据源的 `_selected` 字段需保持同步: - 勾选/取消时修改 `_selected` 更新 `selectedRows` - 清空操作需遍历所有数据重置状态 2. **虚拟滚动优化** 由于 `el-table-v2` 采用虚拟滚动,**避免直接操作整个大数组**: - 使用响应式 API(`ref`/`reactive`)确保视图更新 - 操作具体行数据而非重新赋值整个 `tableData` 3. **全选/反选联动** 表头全选功能需额外处理: ```typescript const isAllSelected = computed({ get: () => tableData.value.every(row => row._selected), set: (val) => { tableData.value.forEach(row => (row._selected = val)); selectedRows.value = val ? [...tableData.value] : []; } }); ``` ### 完整示例 ```vue <template> <div> <el-button @click="clearAllSelection">清空选择</el-button> <p>已选 {{ selectedRows.length }} 行</p> <el-table-v2 :columns="columns" :data="tableData" :width="700" :height="400" :row-height="60" /> </div> </template> <script setup> import { ref, computed } from 'vue'; const tableData = ref([]); const selectedRows = ref([]); // 初始化数据 tableData.value = generateData(5000); // 列定义 const columns = [ { key: 'selection', width: 50, cellRenderer: ({ rowData }) => ( <el-checkbox v-model={rowData._selected} onChange={() => handleSelectChange(rowData)} /> ) }, { key: 'id', title: 'ID', width: 100 }, { key: 'name', title: '名称', width: 200 } ]; // 清空选择 const clearAllSelection = () => { tableData.value.forEach(row => (row._selected = false)); selectedRows.value = []; }; // 处理单行选择变化 const handleSelectChange = (row) => { if (!row._selected) { const index = selectedRows.value.findIndex(item => item.id === row.id); if (index !== -1) selectedRows.value.splice(index, 1); } else { selectedRows.value.push(row); } }; </script> ``` ### 注意事项 1. **大数据量性能** 操作万级以上数据时,优先使用 `forEach` 而非 `map`/`filter` 避免创建新数组 2. **唯一标识** 确保每行数据有唯一 `id` 字段用于精确匹配选中行 3. **响应式更新** 当动态更新 `tableData` 时(如过滤/排序),需同步重置 `selectedRows` 和 `_selected` 状态 通过操作数据源的 `_selected` 字段和 `selectedRows` 数组,即可高效管理虚拟化表格的选择状态[^1][^2]。 --- ### 相关问题 1. 如何实现 `el-table-v2` 的动态数据过滤保持选中状态? 2. 在虚拟滚动表格中如何处理跨页全选功能? 3. `el-table-v2` 和 `el-table` 在多选功能上有哪些核心差异? 4. 如何优化万级数据量下复选框的渲染性能? [^1]: Vue3+el-table-v2虚拟表格大数据量多选功能详细教程 [^2]: 在vue3-element-admin中优化表格性能的方法 [^3]: Element UI 树形控件的数据结构处理
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值