之前讲过树形结构的扁平化,那么在项目中究竟会有哪些场景呢?
下面我们以 antd 组件的 TreeSelect 组件为例:
我们采用的比较手段比较简单粗暴,初次加载时,把一二级(因为这个案例的需求是最多两层数据)数据拿到,先请求第一个接口,拿到第一个请求的结果后,这里使用 promise.all() 静态方法,用第一次请求的列表数据作为参数,之后返回一个 promise 实例,promise.all() 的用法之后会详细讲解,通过传入一个参数列表,列表的每一项都是一个 promise , 只有当所有的 promise 都请求成功后才会进入下面的 .then , 否则会进入 catch , 在本案例中, 依赖第一个请求的数据中的 id(跟节点数据),请求子节点的数据, 并将每轮子节点的数据里面加入 pid 字段,值就是父级的 id,此时当所有请求执行完成后,是一个二维数组的结构,我们利用 flat() (此方法之前篇章有做讲解) 将其扁平化,然后将子节点的数据和父节点的数据合并成扁平的数据,并传递给我写好的 convertToTreeSelectData 方法。
// 获取验收项信息
getPileList = async () => {
const pileList = await Service.getPile();
Promise.all(pileList.map(item => {
return get(`/measurement/standardItem/query?typeId=${item.id}&inspectionType=pile`).then(list => {
return list.map(each => ({...each, pid: item.id}));
});
})).then(res => {
const treeData = this.convertToTreeSelectData(res.flat().concat(pileList));
this.setState({
treeData
});
});
};
此方法扁平化的数据(无论是多少层级的数据都传递)
convertToTreeSelectData(data) {
// 首先,需要将数据按照父子关系进行分组
data.map((item) => {
if (item.pid == "-1") {
item.id = item.id + "*";
} else {
item.pid = item.pid + "*";
}
});
// 这一步目的就是将传递的数据以pid的值为键名,进行分组。
const groupedData = {};
data.forEach((item) => {
const pid = item.pid || -1;
if (!groupedData[pid]) {
groupedData[pid] = [];
}
groupedData[pid].push(item);
});
// 上述的代码可以等同于loadsh中的 _.groupBy方法实现
const groupedData = _.groupBy(data, 'pid' 或者 data => data.pid)
// 然后,定义一个递归函数,将分组后的数据转换为树形结构
function convert(data) {
return data.map((item) => {
const children = groupedData[item.id]
? convert(groupedData[item.id])
: [];
return {
title: item.name,
value: item.id,
key: item.id,
children,
};
});
}
// 最后,返回根节点的子节点作为 TreeSelect 的数据源
return convert(groupedData[-1]);
}