el-table 表格多选(后端接口搜索分页)实现已选中的记忆功能。实现表格数据和已选数据(前端分页)动态同步更新。

实现效果:(可拉代码下来看:vue-demo: vueDemo

左侧表格为点击查询调用接口查询出来的数据,右侧表格为左侧表格所有选择的数据,由前端实现分页。

两个el-table勾选数据联动更新

实现逻辑:

el-table表格的selection-change方法(element组件的table表格)结合分页组件,自定义一个用于存储(左侧表格)当前页的已勾选数据;一个用于存储所勾选的所有数据(右侧表格所有数据);一个用于存储(右侧表格)当前页的展示数据。

代码实现:

所有代码:

html 复制代码
<template>
  <div>
    <Search v-show="showSearch" label-width="100" :limit-height-param="false" @search="handleQuery" @clear="resetQuery">
      <div>
        <span class="label">账号名称</span>
        <el-select
          v-model="queryParams.companyCodeList"
          placeholder="请选择账号名称"
          clearable
          filterable
          default-first-option
          multiple
          size="small"
        >
          <el-option
            v-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-select
          v-model="queryParams.accountType"
          placeholder="请选择账户类型"
          clearable
          filterable
          default-first-option
          size="small"
        >
            <el-option
              v-for="dict in accountTypeOptions"
              :key="dict.dictValue"
              :label="dict.dictLabel"
              :value="dict.dictValue"
            >
          </el-option>
        </el-select>
      </div>
      <div>
        <span class="label">银行卡号</span>
        <el-input
          v-model="queryParams.cardNum"
          placeholder="请输入银行卡号"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </div>
    </Search>

    <el-row :gutter="20">
      <el-col :span="12">
        <el-table
          v-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>
        <pagination
          v-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-table
          v-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-column
            label="操作"
            align="center"
            fixed="right"
            width="100"
          >
            <template slot-scope="scope">
              <el-button
                class="delBtn"
                size="small"
                type="text"
                icon="el-icon-delete"
                @click="handleDelete(scope.row)"
              >删除</el-button>
            </template>
          </el-table-column>
        </el-table>
        <pagination
          v-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 = true
      getDepartmentsList(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 = 1
      this.getList()
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.queryParams = this.$options.data().queryParams
      this.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
      // 页码最小值为1
      this.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
      // 获取所有选中数据的id
      const 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 = selection
      this.$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]) || []

      // 获取当前页选中的id
      let selectIds = []
      this.multipleSelection.forEach(row => {
        selectIds.push(row[idKey])
        // 如果总选择里面不包含当前页选中的数据,那么就加入到总选择集合里
        // (左侧表格勾选数据,加入到右侧表格中)
        if (selectAllIds.indexOf(row[idKey]) < 0) {
          this.selectedData.push(row)
        }
      })

      // 得到当前页没有选中的id
      let 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()):

javascript 复制代码
// 设置选中的方法
    setSelectRow() {
      if (!this.selectedData || this.selectedData.length <= 0) {
        return
      }
      // 标识当前行的唯一键的名称
      let idKey = this.idKey
      // 获取所有选中数据的id
      const 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)

javascript 复制代码
// 多选框选中数据
    handleSelectionChange(selection) {
      this.multipleSelection = selection
      this.$nextTick(() => {
        this.changePageCoreRecordData()
      })
    },

勾选右侧表格数据触发记忆选择核心方法 changePageCoreRecordData:

javascript 复制代码
// 记忆选择核心方法(操作搜索表格------左侧,联动改变勾选表格------右侧)
    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]) || []

      // 获取当前页选中的id
      let selectIds = []
      this.multipleSelection.forEach(row => {
        selectIds.push(row[idKey])
        // 如果总选择里面不包含当前页选中的数据,那么就加入到总选择集合里
        // (左侧表格勾选数据,加入到右侧表格中)
        if (selectAllIds.indexOf(row[idKey]) < 0) {
          this.selectedData.push(row)
        }
      })

      // 得到当前页没有选中的id
      let 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博客

javascript 复制代码
// 对表格数据分页
    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
      // 页码最小值为1
      this.pageNum = this.pageNum < 1 ? 1 : this.pageNum
      // 对表格数据分页
      this.getSelectList()
    },

前面已经讲了左侧表格勾选,取消勾选时,对右侧表格的联动改变。下面来讲,右侧表格删除一条数据,如何让左侧表格取消勾选。其实也很简单,就是从右侧表格全部数据中,将要删除的数据去掉,若这条数据在左侧表格当前页存在,则触发el-table的toggleRowSelection(row,false)方法,取消勾选。若这条数据左侧表格在当前页不存在,则直接对右侧表格重新分页就好了。因为下次当左侧表格重新调用getList()方法(触发记忆选择核心方法 changePageCoreRecordData)的时候,右侧表格内已经没有要删除的数据了。

javascript 复制代码
// 删除
    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博客

相关推荐
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
程序媛小果2 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
小光学长2 小时前
基于vue框架的的流浪宠物救助系统25128(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库·vue.js·宠物
阿伟来咯~2 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app