• Element UI 实战:跨页保存表格选中状态与判断状态可选性的高效方案


    引言

            在前文中,我们曾深入探讨了在修改数据后跨页时提醒用户可能丢失数据的问题。虽然这种方式对于一些场景是足够的,但当涉及选择框时,我们需要更为智能和高效的解决方案。在本文中,我们将分享一种基于 Element UI 的实际案例,旨在实现跨页保存选中项与禁选特定项的需求。通过以下详细讨论,你将了解到这一方案的实现原理及其用户体验效果。

    问题背景

            在许多 Web 应用中,数据分页是常见的操作方式。当用户在一个页面中选择了一些数据项,然后切换到另一页时,保持之前选中的项通常是用户友好的体验。同时,可能存在一些需要禁选的执行项,例如在某些状态下,用户不应该选择或执行某些操作:如当数据可以进行执行相关操作,并且需要消耗一定时间时,我们为了任务的完整执行,会在任务执行期间,禁止选中该任务的选择框(可以预防用户进行删除等操作),后续执行完毕可以恢复初始选中状态,也可以放弃选中状态。

    方案设计与实现

            在 Element UI 中,表格(Table)组件提供了丰富的特性和事件。可以利用这些特性和事件来实现跨页保存选中项和禁选执行项的需求。

    实现

            1.跨页保存

            在许多 Web 应用中,数据分页是常见的操作方式。当用户在一个页面中选择了一些数据项,然后切换到另一页时,保持之前选中的项通常是用户友好的体验。

            模板
    1. <el-table
    2. ref="multipleTable"
    3. :data="tableData"
    4. @select="handleSelectionChange"
    5. @select-all="handleSelectionAll"
    6. >
    7. <el-table-column type="selection" align="center">el-table-column>
    8. <el-table-column prop="name" label="name" align="center">el-table-column>
    9. <el-table-column prop="age" label="age" align="center">el-table-column>
    10. el-table>
    11. <el-pagination
    12. :background="true"
    13. :current-page.sync="queryParams.page"
    14. :page-size.sync="queryParams.pageSize"
    15. layout="total,prev,pager,next,sizes"
    16. :total="total"
    17. :page-sizes="[5, 10, 20, 40]"
    18. @size-change="getList"
    19. @current-change="getList" />
            脚本
    1. data() {
    2. return {
    3. total: void 0,
    4. queryParams: {
    5. page: 1,
    6. pageSize: 10,
    7. },
    8. allData: [],
    9. tableData: [],
    10. multipleSelection: [],
    11. listId: [],
    12. };
    13. },
    14. mounted() {
    15. this.getList();
    16. },
    17. methods: {
    18. getList() {
    19. this.allData = [
    20. { name: 'A', age: 1 },
    21. { name: 'B', age: 2 },
    22. { name: 'C', age: 3 },
    23. { name: 'D', age: 4 },
    24. { name: 'E', age: 5 },
    25. { name: 'F', age: 6 },
    26. { name: 'G', age: 7 },
    27. { name: 'H', age: 8 },
    28. { name: 'I', age: 9 },
    29. { name: 'J', age: 10 },
    30. { name: 'K', age: 11 },
    31. { name: 'L', age: 12 },
    32. { name: 'M', age: 13 },
    33. { name: 'N', age: 14 },
    34. { name: 'O', age: 15 },
    35. { name: 'P', age: 16 },
    36. { name: 'Q', age: 17 },
    37. { name: 'R', age: 18 },
    38. { name: 'S', age: 19 },
    39. { name: 'T', age: 20 },
    40. { name: 'U', age: 21 },
    41. { name: 'V', age: 22 },
    42. { name: 'W', age: 23 },
    43. { name: 'X', age: 24 },
    44. { name: 'Y', age: 25 },
    45. { name: 'Z', age: 26 },
    46. ];
    47. this.total = this.allData.length;
    48. let currentPageIndex = (this.queryParams.page - 1) * this.queryParams.pageSize;
    49. let currentPageSize = this.queryParams.pageSize;
    50. this.tableData = this.allData.slice(currentPageIndex, currentPageIndex + currentPageSize);
    51. this.listId = [];
    52. this.tableData.forEach(item => this.listId.push(item.name));
    53. this.$nextTick(() => {
    54. this.tableData.forEach((item, index) => {
    55. if (this.multipleSelection.findIndex(v => v == item.name) >= 0) {
    56. this.$refs.multipleTable.toggleRowSelection(this.$refs.multipleTable.data[index], true);
    57. }
    58. });
    59. });
    60. },
    61. // 全选
    62. handleSelectionAll(val) {
    63. if (val.length) {
    64. const result = [];
    65. this.listId.forEach(id => {
    66. if (this.multipleSelection.every(item => item !== id)) result.push(id);
    67. });
    68. this.multipleSelection.push(...result);
    69. } else {
    70. this.listId.forEach(id => {
    71. this.multipleSelection = this.multipleSelection.filter(item => item !== id);
    72. });
    73. }
    74. },
    75. // 单选
    76. handleSelectionChange(rows, row) {
    77. if (this.multipleSelection.find(item => item === row.name)) this.multipleSelection = this.multipleSelection.filter(item => item != row.name); // 过滤(删除)
    78. else this.multipleSelection.push(row.name);
    79. },
    80. }
            解析
    1. getList 方法:

      • 发送 HTTP GET 请求,获取数据(/.../是请求的地址,queryParams是请求参数)。
      • 在请求成功的回调中,判断返回数据的状态是否为200,如果是,将返回的数据赋值给 tableData
      • 构建 listId 数组,存储 tableData 中每一项的 id
      • 利用 $nextTick,确保在 Vue 更新 DOM 后执行,遍历 tableData,对于已经在 multipleSelection 中的项,在表格中选中对应的行。
    2. handleSelectionAll 方法:

      • 接受一个参数 val,即当前页选中的所有行数据。
      • 如果 val.length 大于 0,表示当前页有选中的行,遍历 listId,将不在 multipleSelection 中的项添加到 multipleSelection 中。
      • 如果 val.length 为 0,表示当前页没有选中的行,遍历 listId,将在 multipleSelection 中的项从中移除。
    3. handleSelectionChange 方法:

      • 接受两个参数,rows 是当前页选中的所有行数据,row 是当前操作的行数据。
      • 如果 multipleSelection 中已经存在 row.id,则将其从 multipleSelection 中移除,否则将其添加到 multipleSelection 中。

            这些方法共同实现了跨页保存选中状态的功能,通过维护 multipleSelection 数组来保存用户选择的行的 id,从而在表格分页切换时保持选中状态。

            2.禁选执行项

            数据拥有执行状态之类的字段,并且需要消耗一定时间时,我们为了任务的完整执行,会在任务执行期间,禁止选中该任务的选择框(以避免删除等操作)。执行完毕后可以恢复初始选中状态,也可以放弃选中状态。

             以下代码选择的是后者,即在重新开始运行时,禁用该项选择框并且执行完毕后不重新选中。如果希望保留,在对应执行的函数(下述为 reloadTask)中不删除 multipleSelection 中对应的id即可。

            模板
    1. <el-table
    2. ref="multipleTable"
    3. :data="tableData"
    4. @select="handleSelectionChange"
    5. @select-all="handleSelectionAll"
    6. >
    7. <el-table-column type="selection" align="center" :selectable="selectable">el-table-column>
    8. <el-table-column prop="name" label="name" align="center">el-table-column>
    9. <el-table-column prop="age" label="age" align="center">el-table-column>
    10. <el-table-column label="status">
    11. <template slot-scope="scope">
    12. <span
    13. style="margin-right:10px; cursor: pointer;"
    14. :class="scope.row.status !== '执行中' ? 'el-icon-caret-right' : 'el-icon-loading'"
    15. size="small"
    16. :disabled="scope.row.status === '执行中'"
    17. @click="reloadTask(scope.row)">span>
    18. <el-tag :type="scope.row.status !== '执行中' ? '' : 'info'">{{scope.row.status}}el-tag>
    19. template>
    20. el-table-column>
    21. el-table>
    22. <el-pagination
    23. :background="true"
    24. :current-page.sync="queryParams.page"
    25. :page-size.sync="queryParams.pageSize"
    26. layout="total,prev,pager,next,sizes"
    27. :total="total"
    28. :page-sizes="[5, 10, 20, 40]"
    29. @size-change="getList"
    30. @current-change="getList" />
            脚本
    1. data() {
    2. return {
    3. total: void 0,
    4. queryParams: {
    5. page: 1,
    6. pageSize: 10,
    7. },
    8. allData: [],
    9. tableData: [],
    10. multipleSelection: [],
    11. listId: [],
    12. loadingSelection: new Set(),
    13. };
    14. },
    15. mounted() {
    16. this.getList();
    17. },
    18. methods: {
    19. getList(flag = false) {
    20. if (!flag) {
    21. this.allData = [
    22. { name: 'A', age: 1, status: '执行完毕' },
    23. { name: 'B', age: 2, status: '执行中' },
    24. { name: 'C', age: 3, status: '执行完毕' },
    25. { name: 'D', age: 4, status: '未执行' },
    26. { name: 'E', age: 5, status: '未执行' },
    27. { name: 'F', age: 6, status: '执行中' },
    28. { name: 'G', age: 7, status: '执行中' },
    29. { name: 'H', age: 8, status: '执行中' },
    30. { name: 'I', age: 9, status: '执行中' },
    31. { name: 'J', age: 10, status: '未执行' },
    32. { name: 'K', age: 11, status: '未执行' },
    33. { name: 'L', age: 12, status: '未执行' },
    34. { name: 'M', age: 13, status: '未执行' },
    35. { name: 'N', age: 14, status: '未执行' },
    36. { name: 'O', age: 15, status: '未执行' },
    37. { name: 'P', age: 16, status: '未执行' },
    38. { name: 'Q', age: 17, status: '未执行' },
    39. { name: 'R', age: 18, status: '未执行' },
    40. { name: 'S', age: 19, status: '未执行' },
    41. { name: 'T', age: 20, status: '未执行' },
    42. { name: 'U', age: 21, status: '未执行' },
    43. { name: 'V', age: 22, status: '未执行' },
    44. { name: 'W', age: 23, status: '未执行' },
    45. { name: 'X', age: 24, status: '未执行' },
    46. { name: 'Y', age: 25, status: '未执行' },
    47. { name: 'Z', age: 26, status: '未执行' },
    48. ];
    49. }
    50. this.total = this.allData.length;
    51. let currentPageIndex = (this.queryParams.page - 1) * this.queryParams.pageSize;
    52. let currentPageSize = this.queryParams.pageSize;
    53. this.tableData = this.allData.slice(currentPageIndex, currentPageIndex + currentPageSize);
    54. this.listId = [];
    55. this.tableData.forEach(item => this.listId.push(item.name));
    56. this.$nextTick(() => {
    57. this.tableData.forEach((item, index) => {
    58. if (this.multipleSelection.findIndex(v => v == item.name) >= 0) {
    59. this.$refs.multipleTable.toggleRowSelection(this.$refs.multipleTable.data[index], true);
    60. }
    61. });
    62. });
    63. },
    64. // 执行任务
    65. reloadTask(row) {
    66. this.multipleSelection = this.multipleSelection.filter(item => item !== row.name);
    67. row.status = '执行中';
    68. this.getList(true);
    69. },
    70. // 判断可选性
    71. selectable(row) {
    72. if (row.status !== '执行中') {
    73. if (this.loadingSelection.has(row.name)) this.loadingSelection.delete(row.name);
    74. return true;
    75. } else {
    76. this.loadingSelection.add(row.name);
    77. return false;
    78. }
    79. },
    80. // 全选
    81. handleSelectionAll(val) {
    82. if (val.length) {
    83. const result = [];
    84. this.listId.forEach(id => {
    85. if (this.multipleSelection.every(item => item !== id) && !this.loadingSelection.has(id)) result.push(id);
    86. });
    87. this.multipleSelection.push(...result);
    88. } else {
    89. this.listId.forEach(id => {
    90. this.multipleSelection = this.multipleSelection.filter(item => item !== id);
    91. });
    92. }
    93. },
    94. // 单选
    95. handleSelectionChange(rows, row) {
    96. if (this.multipleSelection.find(item => item === row.name)) this.multipleSelection = this.multipleSelection.filter(item => item != row.name); // 过滤(删除)
    97. else this.multipleSelection.push(row.name);
    98. },
    99. },
             解析

            这部分代码经过修改后主要涉及到对行的可选性(selectable 方法)以及全选处理(handleSelectionAll 方法)。下面是对修改部分代码的详细解释:

    1. selectable 方法:

      • selectable 方法用于确定给定行 row 是否可选。如果行的状态 status 不是 '执行中',则认为该行可选。
      • 如果行不可选,而且 loadingSelection 集合中已经存在该行的 id,则将其从 loadingSelection 中删除,表示加载完成。
      • 如果行可选,将其 id 添加到 loadingSelection 集合中,表示正在加载中,并返回 false 表示不可选;否则,返回 true 表示可选。
    2. handleSelectionAll 方法:

      • 该方法用于处理全选操作。接收参数 val,即当前页选中的所有行数据。
      • 如果有选中的行,遍历 listId,将不在 multipleSelection 中且不在 loadingSelection 中的项添加到 multipleSelection 中。
      • 如果没有选中的行,遍历 listId,将在 multipleSelection 中的项从中移除。

            这些修改主要增加了对行的可选性的判断,以及对加载状态的管理,通过 loadingSelection 集合来标记哪些行正在加载中。这样可以更好地控制在某些条件下禁止选择或在加载中时保持选择状态。

    实现效果

    跨页保存

    禁选某些状态

  • 相关阅读:
    Jetpack Compose干货,如何让Compose Dialog从屏幕任意方向进入
    MATLAB从0开始搭建简单的GUI界面
    根据代码获取properties对应信息
    虚拟机centos设置网络模式(桥接|NAT)
    抖音小店运营计划书怎么写?运营技巧及入住条件资料
    Oauth2系列8:何谓JWT令牌?
    2022年,消费品企业应该选择什么样的会员系统?
    LVS集群 ----------------(直接路由 )DR模式部署 (二)
    使用MySQL和SQL Server生成最近七天的日期
    华为校招机试题- 机器人活动区域-2023年
  • 原文地址:https://blog.csdn.net/qq_51588894/article/details/134438002