管理系统必备技(1):递归生成树形结构菜单

3,525 阅读2分钟

微信公众号:潇雷
当努力到一定程度,幸运自与你不期而遇

1、概述

后台管理系统是web开发必备的一个项目,因此有必要对系统的一些基本功能做个梳理,今天要看的内容是如何递归生成树形结构菜单,效果图如下:

菜单分为一级菜单、二级菜单、复杂的菜单甚至包含三级菜单和四级菜单,这些数据在表里面都是在一个菜单目录表里。

表的核心设计如下:

字段名称备注
idint主键id
parent_idint一级菜单为0 ,其他菜单指向父类的id
menu_pathvarchar资源路径
sortint排序
menu_buttontinyint是否作为目录展示,或者隐藏等需求

2、代码实现

思路比较统一:

1、首先找到菜单数据的所有数据

2、然后找到所有的一级菜单,将它的子树拼接上

3、子树也会有子菜单,需要递归拼接子树的子菜单。

首先对原先的实体类,我们需要增加一个字段存放子菜单的列表,加上注解表示该字段不在数据库中拥有。

2.1 基础版实现

  @Override
    public List<ChildMenuVO> listWithTree() {
        //1、查出所有菜单
        List<CategoryEntity> categoryEntities = categoryDao.selectList(null);
        //2、组装成父子的树形结构
        List<ChildMenuVO> childMenuVOS =treeMenu(categoryEntities);
        return childMenuVOS;
    }

    private List<ChildMenuVO> treeMenu(List<CategoryEntity> categoryEntities) {
        List<ChildMenuVO> childMenuVOS=new ArrayList<>();
        for (int i = 0; i < categoryEntities.size(); i++) {
            CategoryEntity categoryEntity=categoryEntities.get(i);
            //如果父id为0 的话,就不执行后续方法
            if(0!=categoryEntity.getParentCid().intValue()){
                continue;
            }
            ChildMenuVO childMenuVO = new ChildMenuVO();
            BeanUtils.copyProperties(categoryEntity,childMenuVO);
            //递归判断 当前menu 是否有子节点,如果有则set
            childMenuVO.setChild(subMenu(categoryEntity,categoryEntities));
            childMenuVOS.add(childMenuVO);
        }
        return childMenuVOS;
    }

    private List<ChildMenuVO> subMenu(CategoryEntity parentMenu, List<CategoryEntity> categoryEntities) {
        List<ChildMenuVO> childMenuVOS =new ArrayList<>();
        //遍历所有目录
        for (int i = 0; i < categoryEntities.size(); i++) {
            CategoryEntity subCategory=categoryEntities.get(i);
            if(subCategory.getParentCid().equals(parentMenu.getCatId())){
                ChildMenuVO childMenuVO = new ChildMenuVO();
                BeanUtils.copyProperties(subCategory,childMenuVO);
                childMenuVO.setChild(subMenu(subCategory,categoryEntities));
                childMenuVOS.add(childMenuVO);
            }
        }
        return childMenuVOS;
    }

2.2 进阶版实现

 public List<CategoryEntity> listWithTree() {
        //1、查出所有菜单
        List<CategoryEntity> categoryEntities = categoryDao.selectList(null);
        //2、组装成父子的树形结构
            //2.1 找到所有的一级分类
            List<CategoryEntity> level1Menus = categoryEntities.stream().filter(categoryEntity -> {
                return categoryEntity.getParentCid() == 0;
            }).map(menu->{
                menu.setChildren(getChildrens(menu,categoryEntities));
                return menu;
            }).sorted((menu1,menu2)->{
                return (menu1.getSort()==null?0:menu1.getSort())-(menu2.getSort()==null?0:menu2.getSort());
            })
              .collect(Collectors.toList());
            return level1Menus;
    }

    //递归找到当前菜单的子菜单
    private List<CategoryEntity> getChildrens(CategoryEntity  root,List<CategoryEntity> all){
        List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
            return categoryEntity.getParentCid().equals(root.getCatId());
        }).map(categoryEntity -> {
            categoryEntity.setChildren(getChildrens(categoryEntity,all));
            return categoryEntity;
        }).sorted((menu1,menu2)->{
            return (menu1.getSort()==null?0:menu1.getSort())-(menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());
        return children;
    }

不得不说java8的stream流极大的简洁了代码,后续也会记录java8的文章,效果也达到了,stream流有点像sql,可以做出排序、过滤、判断等各种简洁的操作。

3、新增(遍历树形结构)

前面讲的是如何生成 树形结构,而这里,我们将树形结构还原成list,如何解析他们呢,也可以用递归来实现。

/**
 * 深度遍历,遍历树形结构
 * @param wiringPointPrintVOS
 * @param xbList
 * @return
 */
public ArrayList<WiringPointPrintVO> unTree(ArrayList<WiringPointPrintVO> wiringPointPrintVOS, List<WiringPointPrintVO> xbList){
    for (WiringPointPrintVO wiringPointPrintVO : xbList) {
        if(wiringPointPrintVO.getChild()!=null){
            wiringPointPrintVOS.add(wiringPointPrintVO);
            unTree(wiringPointPrintVOS,wiringPointPrintVO.getChild());
        }else{
            wiringPointPrintVOS.addAll(xbList);
            break;
        }
    }
    return wiringPointPrintVOS;
}

image.png

今天的分享就到这里,有兴趣可以关注,后续更多内容干货敬请期待。