公共Hooks封装之自定义表格数据列渲染useTableColumns

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

项目环境

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

封装思考:何为自定义表格数据列渲染,其为何种场景服务

根据实际业务场景而来,为避免法律风险,部分截图内容脱敏处理

如下图,当表格内容的列非常多时,正常情况下,我们通常采取的方式是左右两侧的列,即左侧 Key 列和右侧操作列固定,中间列内容区域滚动。而当数据列内容非常多并且每列数据都具备其真实意义时,我们该考虑采取更好的方式,对于不同的系统使用人员,通常每个人的关注点都是不一样的,因此,可以做成每个人主动去改变表格列项展示为其关注的数据列。

tableColumnsDemo.png 结合上述场景,以及优化考虑,便得到如下的个人解决方案,useTableColumns的封装

封装分解:主要思路 Columns

结合实际使用的 UI 框架Ant Design Vue,Column 列描述数据对象,是 Columns 中的一项,Column 使用相同的 API.
常见的数据结构如下:

columns: [
    {
        title: '姓名',
        dataIndex: 'name',
        key: 'name',
    },
    {
        title: '年龄',
        dataIndex: 'age',
        key: 'age',
    },
    {
        title: '住址',
        dataIndex: 'address',
        key: 'address',
    },
],

上面的数据结构中,指出了表格内每一列数据对应的表头、数据索引等相关信息,此外,还有官方文档中提供的一系列 API。结合上面已经提供的截图,思路其实很明显了,动态去变更这个 JSON 数据而已。实际使用中,将需要参与变更的数据列,添加自定义属性:addFilter.

封装分解:下拉菜单-筛选展示数据列组件

<template #headerCell="{ column }">
  <template v-if="column.key === 'action'">
    <div class="flex justify-between items-center">
      <span>操作</span>
      <table-field-filter :columns="columns" @change="onFilterColumnChange" />
    </div>
  </template>
</template>

以上代码基于实际项目 UI FrameWork -- Ant Design Vue,类似的也可以看下其他框架,如ElementUITable组件提供的相关 API 或写法.

项目内,我选择了将onFilterColumnChange放置在了表格操作列的表头上,实际使用过程中,也可以放置在其他区域,例如筛选模块、标题等,具体还是看怎么设计,不破坏现有的布局或需要 UI 设计师稍微设计一下~

封装分解:Columns 动态变更

let includeColumns = []; // 参与过滤的列表字段集合

let excludeColumns = []; // 不参与过滤的列表字段集合

const columns = shallowRef([]); // 需要展示的列表字段集合

if (options?.initFilterKeys.length > 0) {
  for (let i = 0, len = options.initFilterKeys.length; i < len; i++) {
    const key = options.initFilterKeys[i];
    const index = $columns.findIndex((item) => item.key === key);
    $columns.splice(index, 1);
  }
}

目前实现方式是将参与筛选的表格列放到一个数组内,再将不参与过滤的项放到另一个数组内,再将其合并。结合实际业务中,对于某些特殊场景可能没有对应数据,不参与显示控制,例如企业后台-企业微信/或其他类似生态账号登录的体系中,实际对于账号控制是不需要的(或因政策限制等其他原因,考虑屏蔽表格内账号显示),当然这个是结合实际业务来的,在此不做赘述~

封装分解:utils--getArrayDiff

/**
 * @description 筛选两个数组不同的元素
 */
export function getArrayDiff(arr1, arr2) {
  return arr1
    .concat(arr2)
    .filter((item, index, arr) => arr.indexOf(item) === arr.lastIndexOf(item));
}

useTableColumns.js 完整代码

import { shallowRef } from "vue";
import { cloneDeep } from "lodash-es";
import { getArrayDiff } from "@/utils/util";

export function useTableColumns(defaultColumns, options) {
  if (!defaultColumns || !Array.isArray(defaultColumns)) return;

  let includeColumns = []; // 参与过滤的列表字段集合

  let excludeColumns = []; // 不参与过滤的列表字段集合

  const columns = shallowRef([]); // 需要展示的列表字段集合

  const onFilterColumnChange = (keys) => {
    const orKeys = includeColumns.map((item) => item.key);
    const delKeys = getArrayDiff(orKeys, keys);
    const $columns = cloneDeep(includeColumns);
    for (let i = 0, len = delKeys.length; i < len; i++) {
      const key = delKeys[i];
      const index = $columns.findIndex((item) => item.key === key);
      $columns.splice(index, 1);
    }
    columns.value = [...$columns, ...excludeColumns];
  };

  // 初始化字段处理
  const initColumns = () => {
    const $columns = cloneDeep(defaultColumns);
    if (options?.initFilterKeys.length > 0) {
      for (let i = 0, len = options.initFilterKeys.length; i < len; i++) {
        const key = options.initFilterKeys[i];
        const index = $columns.findIndex((item) => item.key === key);
        $columns.splice(index, 1);
      }
    }
    for (let i = 0, len = $columns.length; i < len; i++) {
      const item = $columns[i];
      if (item.addFilter) {
        includeColumns.push(item);
      } else {
        excludeColumns.push(item);
      }
    }
    columns.value = $columns;
  };

  initColumns();

  return {
    columns,
    onFilterColumnChange,
  };
}

后续思考:当前的封装是否有问题呢?如何优化呢?

Hooks 中对于 Columns 的处理是使用 2 个数组来分别暂存需要筛选和不需要筛选的表格列,由于实际项目中暂时只有一个页面「文中截图所示」需要处理上述操作,而对于表格列数据,目前除了操作列,其他的都添加了addFilter自定义属性,而在实际业务需求中,可能表格字段会出现以下情况:

仅简要说明

[字段A, 字段B, 字段C, 字段D, 字段E, 字段F, 字段G, 操作列 ]

如果除操作列都加上addFilter自定义属性,则是和我目前使用的方式无异,也不存在问题,

如果以上字段中,作为标识(字段 A、字段 C、字段 G)不参与筛选,其余的字段参与筛选,目前封装的 Hooks 是不满足需求的,因为按照现有的写法会导致 colums 数组经过onFilterChange之后,会变成

[字段A, 字段C, 字段G, 字段B, 字段E, 字段F, 操作列 ]

这种情况,对于原表格来说,原有的索引顺序被打破,仅满足了筛选,但不是原位置"删除/隐藏"的筛选,对于实际业务来说,可能不满足~
该如何解决呢?目前由于需求迭代,暂时没时间处理~待后续进行优化~

当然也更期待读者盆友们提供解决思路,或一起优化下代码吧~