logo

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

May 8, 2023 · 6 min

写在前面#

对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks. 本篇文章为useTableColumns.js

基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人

项目环境#

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

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

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

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

tableColumnsDemo

结合上述场景,以及优化考虑,便得到如下的个人解决方案,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, 操作列 ]

这种情况,对于原表格来说,原有的索引顺序被打破,仅满足了筛选,但不是原位置”删除/隐藏”的筛选,对于实际业务来说,可能不满足~
该如何解决呢?目前由于需求迭代,暂时没时间处理~待后续进行优化~
当然也更期待读者盆友们提供解决思路,或一起优化下代码吧~

> cd ..