公共Hooks封装之表格选择操作useTableRowSelection

发布时间 2024-01-04 14:56:04作者: 柯基与佩奇

项目环境

Vue3.x + Ant Design Vue3.x + Vite3.x

封装思考:为什么封装 useTableRowSelection.js

首先, 基于 Hooks(useTableData.jsuseQueryParams.js)的封装,作为管理后台表格常见操作的批量删除、批量编辑相关,封装useTableRowSelection.js并使用,可以仅在页面 table 组件中绑定的 :row-selection="rowSelection"进行处理即可,不用在每个单页面文件中写表格选择的重复代码。
另外,我们可以在Ant Design VueAnt Design官方的组件文档中,看到 Table 组件的选择示例,在进行翻页后,先前选择的数据就被清除了。好吧,现在官方已经能够提供这个能力了,泪奔~
PS: 至少 2022 年 8 月份肯定没有这个能力,因为我的 Hooks 在那个时候封装的,只是最近稍微空闲些,才又开始写文章~

封装分解:表格多选

由于封装本 Hooks 时,官方未提供对应跨页多选的能力,而现在官方已经提供了相关能力,故相关代码仅介绍封装思想。
多选时,将需要进行跨页选择的表格,对于已选择的数据进行缓存。checkBox 需要考虑选择和全选(每个分页表头的全选)功能。
对于已选择的 selectKeysselectItems 进行处理,并暴露出来,之后提供给需要表格选择的页面进行数据处理,包括但不限于请求业务接口需要进行的传参,或回显。

// 选择
onSelect: (record, selected) => {
    if (selected) {
        selectKeys.value.push(record[rowId]);
        if ($cacheItem) selectItems.value = [...selectItems.value, ...[record]];
    } else {
        const index = selectKeys.value.findIndex(key => key === record[rowId]);
        if (index >= 0) {
            selectKeys.value.splice(index, 1);
            if ($cacheItem) {
                const $cacheSelectItems = selectItems.value;
                $cacheSelectItems.splice(index, 1);
                selectItems.value = [...$cacheSelectItems];
            }
        }
    }
},

// 全选
onSelectAll: selected => {
    if (selected) {
        tableData.value.forEach(item => {
            const index = selectKeys.value.findIndex(id => item[rowId] === id);
            if (index < 0) {
                selectKeys.value.push(item[rowId]);
                if ($cacheItem) selectItems.value = [...selectItems.value, ...[item]];
            }
        });
    } else {
        tableData.value.forEach(item => {
            const index = selectKeys.value.findIndex(key => key === item[rowId]);
            if (index >= 0) {
                selectKeys.value.splice(index, 1);
                if ($cacheItem) {
                    const $cacheSelectItems = selectItems.value;
                    $cacheSelectItems.splice(index, 1);
                    selectItems.value = [...$cacheSelectItems];
                }
            }
        });
    }
},

封装思考:完整的表格选择 Hooks

一开始出于和useTableDatauseQueryParams相关表格 Hooks 的使用目的,减少项目内重复代码的考虑。
完整的useTableRowSelection应该需要同时考虑表格内多选和单选。虽然多选能实现单选的功能但是页面内多选和单选的 UI 呈现方式还是有区别的~,因此 Hooks 接收表格源数据tableData、筛选类型selectType、唯一 KeyrowId以及是否开启缓存(即跨页筛选,毕竟不是所有的表格都需要跨页筛选)cacheItem = false
在这里,封装的 Hooks,和现有官方提供的能力有一点点的不同,官方文档内表格的筛选是跨页选择的,考虑实际业务(当然仅个人公司涉及业务场景考虑,可能不是通用的),仅在需要进行选择人员参加某实际业务项目或调整相关可见范围等需求。

封装分解:暴露 clearKeys、isEmptyKeys

对外暴露 clearKeys 方法,用于清除表格筛选,对外暴露 isEmptyKeys 方法,用于判断表格的筛选是否为空

useTableRowSelection.js 完整代码

import { computed, ref, shallowRef, isRef } from "vue";
import { ROW_SELECT_TYPE } from "@/enums";

export function useTableRowSelection(
  tableData,
  selectType,
  rowId,
  cacheItem = false
) {
  if (!isRef(tableData)) throw new Error("参数 tableData 必须为 Ref 类型");

  const selectKeys = ref([]);

  const selectItems = shallowRef([]);

  const rowSelection = computed(() => {
    const $selectType = isRef(selectType) ? selectType.value : selectType;
    const $cacheItem = isRef(cacheItem) ? selectType.value : cacheItem;
    if ($selectType === ROW_SELECT_TYPE.CHECKBOX) {
      return {
        onSelect: (record, selected) => {
          if (selected) {
            selectKeys.value.push(record[rowId]);
            if ($cacheItem)
              selectItems.value = [...selectItems.value, ...[record]];
          } else {
            const index = selectKeys.value.findIndex(
              (key) => key === record[rowId]
            );
            if (index >= 0) {
              selectKeys.value.splice(index, 1);
              if ($cacheItem) {
                const $cacheSelectItems = selectItems.value;
                $cacheSelectItems.splice(index, 1);
                selectItems.value = [...$cacheSelectItems];
              }
            }
          }
        },
        onSelectAll: (selected) => {
          if (selected) {
            tableData.value.forEach((item) => {
              const index = selectKeys.value.findIndex(
                (id) => item[rowId] === id
              );
              if (index < 0) {
                selectKeys.value.push(item[rowId]);
                if ($cacheItem)
                  selectItems.value = [...selectItems.value, ...[item]];
              }
            });
          } else {
            tableData.value.forEach((item) => {
              const index = selectKeys.value.findIndex(
                (key) => key === item[rowId]
              );
              if (index >= 0) {
                selectKeys.value.splice(index, 1);
                if ($cacheItem) {
                  const $cacheSelectItems = selectItems.value;
                  $cacheSelectItems.splice(index, 1);
                  selectItems.value = [...$cacheSelectItems];
                }
              }
            });
          }
        },
        getCheckboxProps: (record) => {
          return {
            disabled: record.disabled,
          };
        },
        selectedRowKeys: selectKeys.value,
        type: ROW_SELECT_TYPE.CHECKBOX,
      };
    } else {
      return {
        onSelect: (record) => {
          selectKeys.value = [record[rowId]];
          if ($cacheItem) selectItems.value = [record];
        },
        selectedRowKeys: selectKeys.value,
        getCheckboxProps: (record) => {
          return {
            disabled: record.disabled,
          };
        },
        type: ROW_SELECT_TYPE.RADIO,
      };
    }
  });

  const clearKeys = () => {
    selectKeys.value.length = 0;
    selectItems.value = [];
  };

  const isEmptyKeys = () => {
    return selectKeys.value.length === 0;
  };

  return {
    selectItems,
    selectKeys,
    rowSelection,
    clearKeys,
    isEmptyKeys,
  };
}

实际使用:参考示例

  • 页面组件Page.vue
<a-table
  size="small"
  class="mt-2"
  row-key="id"
  :data-source="tableData"
  :loading="loading"
  :columns="columns"
  :row-selection="rowSelection"
  @change="onTableChange"
/>
  • 具体使用
const { rowSelection, selectKeys, clearKeys, isEmptyKeys } =
  useTableRowSelection(tableData, ROW_SELECT_TYPE.CHECKBOX, "id");

const confirmSelect = () => {
  if (isEmptyKeys()) {
    // 确认按钮,业务场景,表格筛选项非空校验
    proxy.$message.warning("请选择****");
    return;
  }
  const selectItem = tableData.value.find(
    (item) => item.id === selectKeys.value[0]
  );
  emits("on-select", {
    id: selectItem.id,
  });
  closeModal();
};

const closeModal = () => {
  clearKeys();
  resetParams(); // 还记得这个吗~`useQueryParams.js`Hooks内提供的方法~
};