写在前面#
对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks.
本篇文章为useTableColumns.js
基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人
项目环境#
Vue3.x + Ant Design Vue3.x + Vite3.x
封装思考:何为自定义表格数据列渲染,其为何种场景服务#
根据实际业务场景而来,为避免法律风险,部分截图内容脱敏处理
如下图,当表格内容的列非常多时,正常情况下,我们通常采取的方式是左右两侧的列,即左侧Key列和右侧操作列固定,中间列内容区域滚动。而当数据列内容非常多并且每列数据都具备其真实意义时,我们该考虑采取更好的方式,对于不同的系统使用人员,通常每个人的关注点都是不一样的,因此,可以做成每个人主动去改变表格列项展示为其关注的数据列。
结合上述场景,以及优化考虑,便得到如下的个人解决方案,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
,类似的也可以看下其他框架,如ElementUI
的Table
组件提供的相关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, 操作列 ]
这种情况,对于原表格来说,原有的索引顺序被打破,仅满足了筛选,但不是原位置”删除/隐藏”的筛选,对于实际业务来说,可能不满足~
该如何解决呢?目前由于需求迭代,暂时没时间处理~待后续进行优化~
当然也更期待读者盆友们提供解决思路,或一起优化下代码吧~