导言:
搞了个后端对接若依前端,对接菜单管理时候懵懵的就搞完了,也是搞了很久。记一下逻辑和要注意的东西,以后做想似的能有个改造思路。
逻辑:
主要是要把后端传过的数组列表做成类似
这样的,所以要转格式
后端传过来的数据:
const data = [
{ id: 1, name: '根节点1', parentId: null },
{ id: 2, name: '根节点2', parentId: null },
{ id: 3, name: '子节点1-1', parentId: 1 },
{ id: 4, name: '子节点1-2', parentId: 1 },
{ id: 5, name: '子节点2-1', parentId: 2 },
{ id: 6, name: '子节点1-1-1', parentId: 3 },
{ id: 7, name: '子节点1-1-2', parentId: 3 }
];
转换成el-table要的
const tree = [
{
id: 1,
name: '根节点1',
parentId: null,
children: [
{
id: 3,
name: '子节点1-1',
parentId: 1,
children: [
{ id: 6, name: '子节点1-1-1', parentId: 3 },
{ id: 7, name: '子节点1-1-2', parentId: 3 }
]
},
{ id: 4, name: '子节点1-2', parentId: 1 }
]
},
{
id: 2,
name: '根节点2',
parentId: null,
children: [
{ id: 5, name: '子节点2-1', parentId: 2 }
]
}
];
若依是定义了一个方法来实现转换的过程,大致思路:
- 第一次循环用于建立节点之间的父子关系映射。
- 第二次循环用于识别根节点并初始化树的结构。
- 第三次循环则利用递归构建完整的树形结构,确保所有节点的层级关系正确。
讲实话我还是懵懵的,模糊理解
1.把扁平数组每个数据看成点,两点之间连线
2.分别把两点连线是父节点的一端找到
3.对应接起来
没什么实感..
实现
1.index.vue
请求后端,获得数据,调用handleTree转换格式,绑到表格上
/** 查询菜单列表 */
getList() {
this.loading = true;
listMenu(this.queryParams).then(response => {
console.log("请求信息response:",response);
this.menuList = this.handleTree(response.data, "id","parentUID");
console.log("请求信息this.menuList:",this.menuList);
this.loading = false;
});
},
2.handleTree()转换
在src\utils\ruoyi.js里.
把扁平化的数组数据结构(通常是包含父子关系的节点)转换为一个树形结构
/**
* 构造树型结构数据
* @param {*} data 数据源 // 输入的原始数据,是一个扁平结构的数组
* @param {*} id id字段 默认 'id' // 数据中表示唯一标识符的字段名,默认为 'id'
* @param {*} parentId 父节点字段 默认 'parentId' // 数据中表示父节点的字段名,默认为 'parentId'
* @param {*} children 孩子节点字段 默认 'children' // 用来存储子节点的字段名,默认为 'children'
*/
export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children') {
// 配置对象,定义了 id 字段、parentId 字段、children 字段的名称
let config = {
id: id || 'id', // id 标识字段,默认为 'id'
parentId: parentId || 'parentId', // 父节点标识字段,默认为 'parentId'
childrenList: children || 'children' // 子节点列表字段,默认为 'children'
};
// 存储每个父节点对应的子节点数组
var childrenListMap = {};
// 存储所有节点的 id 及其节点对象,用于快速查找
var nodeIds = {};
// 最终的树形结构数组,存储根节点
var tree = [];
// 第一次循环:构建 childrenListMap 和 nodeIds
for (let d of data) {
let parentId = d[config.parentId]; // 获取当前节点的父节点 id
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = []; // 如果该父节点还没有子节点数组,则初始化
}
nodeIds[d[config.id]] = d; // 保存当前节点的 id 和节点本身,方便快速查找
childrenListMap[parentId].push(d); // 将当前节点加入到其父节点对应的子节点数组中
}
// 第二次循环:找出根节点,并添加到 tree 数组中
for (let d of data) {
let parentId = d[config.parentId]; // 获取当前节点的父节点 id
if (nodeIds[parentId] == null ) { // 如果当前节点的父节点不存在,说明它是根节点
tree.push(d); // 将根节点加入到树结构中
}
}
// 第三次循环:递归遍历根节点的子节点,并构造完整的树形结构
for (let t of tree) {
adaptToChildrenList(t); // 递归处理每个根节点,生成其子节点结构
}
/**
* 递归地将子节点添加到对应的父节点中
* @param {*} o 当前正在处理的节点对象
*/
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]]; // 将子节点数组添加到当前节点的 children 字段
}
if (o[config.childrenList]) { // 如果当前节点有子节点
for (let c of o[config.childrenList]) {
adaptToChildrenList(c); // 递归处理每个子节点
}
}
}
// 返回最终构造好的树形结构
return tree;
}
注意的点
1.handleTree()
这方法开始定义了config对象,其中把每个字段的父节点id,子节点的名字都规范好了,如果后端这俩传来的不一样的话在调用handleTree()时对应传过去就行,不然不一样用了默认的话会报错。
2.后端数据
后端传过来的每条数据都应该带上parentId字段,不管有没有,不然也会报错
3.el-table绑定
绑定的是本身的id,不是父节点的,绑父节点的会报键重复
<el-table
v-if="refreshTable"
v-loading="loading"
:data="menuList"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>