导言:
搞了个后端对接若依前端,对接菜单管理时候懵懵的就搞完了,也是搞了很久。记一下逻辑和要注意的东西,以后做想似的能有个改造思路。
逻辑:
主要是要把后端传过的数组列表做成类似
这样的,所以要转格式
后端传过来的数据:
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 和 nodeIdsfor (let d of data) {let parentId = d[config.parentId]; // 获取当前节点的父节点 idif (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]; // 获取当前节点的父节点 idif (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-tablev-if="refreshTable"v-loading="loading":data="menuList"row-key="id":default-expand-all="isExpandAll":tree-props="{children: 'children', hasChildren: 'hasChildren'}">