在使用ant-design-vue的框架时,a-tree是比较常用的组件,比较适合处理树形结构的数据。
但是在与后台数据进行授权交互时,就不友好了。
在原生官方文档的例子中,若子项被勾选,则父级节点会被关联勾选,但这勾选并不一定是选中的意思。有可能是半选中,通过方框样式选中,也就是说父级节点的值不会出现在checkedKeys
的数组中。
<a-tree
v-model:checkedKeys="checkedKeys"
checkable
:tree-data="treeData"
>
</a-tree>
这对后端数据授权处理是不友好的。因为一般授权子节点时,父级节点必须是也授权的,否则应用无法到达子节点的功能。也就是说,关联被授权的父子节点的ID都要传给后端。
提交的时候,到还好说。将所有选中的列表和半选中的列表合并起来提交就可以了。
const handleCheck = (checkedKeys, e) => {
const {
halfCheckedKeys } = e;
const allCheckedKeys = [...checkedKeys, ...halfCheckedKeys];
};
但是数据回写的时候就难受了,数据库里的授权列表回写到a-tree组件时,由于父节点被回传过来,那么当父节点被选中时,所有子节点都会被选中,这与实际不符。
所以需要将服务传过来的数据allCheckedKeys
处理,再还原成选中checkedKeys
和半选中halfCheckedKeys
的两部分。
这个业务逻辑比较复杂,需要比较授权列表和树形结构数据。
我在网上找了很久都没找到关于此业务的算法逻辑。但是找到了另外的两种解决办法:
一是更改数据库结构,添加授权列表的字段分为选中和半选中的状态,传给后端服务时ID分类传输。
二是父子节点不再关联,即添加a-tree的属性为checkStrictly="true"
,分别独立处理单个节点的逻辑。
第一种方法缺点就是数据表设计不规范,数据库存储了适应前端页面的数据。优点是避免了js编写筛出未全部选中的父级id的工作。
第二种方法缺点是勾选父级节点时,子节点不会被关联勾选,层级很多时,操作不方便。同时子节点勾选时,可能遗漏父节点的勾选。当然可以再写代码,补上往上和往下关联的逻辑。但是还是有缺点,父节点被勾选时,是通过“√”表示,若不展开下级列表,会不知道子节点是否被全选上。
最后思来想去,感觉两种方法都有缺点。我还是觉得自己将这个业务逻辑的算法写出来吧。。。
代码如下:
/**
* 提供两个方法:
* 一是转换自定义树形对象数据为a-tree识别的树形对象列表
* 二是将数据库存储的已分配id列表重新转化为checkedList
*
* @param {string} idKey - 数据项 ID 的键名,默认为 'id'
* @param {string} nameKey - 数据项名称的键名,默认为 'name'
* @param {string} childrenKey - 子节点列表的键名,默认为 'children'
*/
export const useTreeConverter = (
idKey: string