原版若依使用到了迭代器进行递归等,由于迭代器比较早了。我几乎没怎么用过。而且uoyi-vue-plus版本当中使用增强for与stream流简化过代码。因此在这里讲plus版本的递归生成树。原理是一样的。
值得注意的是在ruoyi-vue-plus版本当中使用了Hutool当中的TreeUtil来构建树形菜单,十分的简便。
public List<Tree<Long>> buildMenuTreeSelect(List<SysMenu> menus) {
if (CollUtil.isEmpty(menus)) {
return CollUtil.newArrayList();
}
return TreeBuildUtils.build(menus, (menu, tree) ->
tree.setId(menu.getMenuId())
.setParentId(menu.getParentId())
.setName(menu.getMenuName())
.setWeight(menu.getOrderNum()));
}
原版使用buildMenuTree方法进行一顿操作。。。
@Override
public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus)
{
List<SysMenu> menuTrees = buildMenuTree(menus);
return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
}
不过在路由接口获取菜单时依旧按照原版若依的思路来(这是因为需要将菜单组装为router,使用Hutool当中的TreeUtil返回的为Tree列表类型,不方便。而原版单纯返回menu列表方便router组装),只是简化了以下代码。
主要有以下几个方法
/**
* 根据父节点的ID获取所有子节点
*
* @param list 分类表
* @param parentId 传入的父节点ID
* @return String
*/
public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {
List<SysMenu> returnList = new ArrayList<>();
for (SysMenu t : list) {
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getParentId() == parentId) {
recursionFn(list, t);
returnList.add(t);
}
}
return returnList;
}
/**
* 递归列表
*
* @param list
* @param t
*/
private void recursionFn(List<SysMenu> list, SysMenu t) {
// 得到子节点列表
List<SysMenu> childList = getChildList(list, t);
t.setChildren(childList);
for (SysMenu tChild : childList) {
if (hasChild(list, tChild)) {
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
return StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId()));
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<SysMenu> list, SysMenu t) {
return CollUtil.isNotEmpty(getChildList(list, t));
}
其中第一个方法getChildPerms(),寻找到最顶层(pid=0)的几个菜单,然后将每一个顶层菜单进行递归,看有没有子菜单,最后将这几个递归完成的顶层菜单(此时已经将子菜单递归出来了)add到returnList里面。
recursionFn()方法比较简单了获取当前菜单的子菜单,存入父亲菜单。然后看看子菜单里面有没有子菜单的子菜单,如果有的化,递归调用recursionFn()方法就行了。递归出口在最后一个子菜单没有自己的子菜单的化,那么其childList的size=0,那么下面的增加for循环就无法进行。自然无法继续调用recursionFn()方法,就会直接运行结束。
这里还有一种使用stream流更加简化的方案,原理是一样的
private List<Menu> builderMenuTree(List<Menu> menus, Long parentId) {
List<Menu> menuTree = menus.stream()
.filter(menu -> menu.getParentId().equals(parentId))
.map(menu -> menu.setChildren(getChildren(menu, menus)))
.collect(Collectors.toList());
return menuTree;
}
/**
* 获取存入参数的 子Menu集合
* @param menu
* @param menus
* @return
*/
private List<Menu> getChildren(Menu menu, List<Menu> menus) {
List<Menu> childrenList = menus.stream()
.filter(m -> m.getParentId().equals(menu.getId()))
.map(m->m.setChildren(getChildren(m,menus)))
.collect(Collectors.toList());
return childrenList;
}