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博客

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
沈梦研5 小时前
【Vscode】Vscode不能执行vue脚本的原因及解决方法
ide·vue.js·vscode
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
轻口味6 小时前
Vue.js 组件之间的通信模式
vue.js
九酒6 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔7 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm