关于管理系统中的菜单管理问题——树形结构+是否被选中

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/huxiutao/article/details/93739362

最近做了菜单管理的功能,之前对这个功能了解的比较少,也没有仔细想过这个问题。刚开始面对这个问题时,想法很简单,有几层菜单就新建几张DB表,然后通过主键关联起来就行了。但是,当做到新增时就遇到问题了——没法垂直扩展!也就是说,如果菜单层级数是变化的,现有的表就没办法满足需求了,坦白的说,如果层级数变了,我总不能动态生成DB表吧(当然Hibernate动态生成,很显然这不是解决问题的方法)。

在网上一查才恍然大悟,原来是将所有菜单写到一张DB表中,只不过是要定义一个Parent字段来指定该菜单的上一级菜单:
(以下DB表设计参考:https://blog.csdn.net/qq_39187822/article/details/83374049)
菜单表:
在这里插入图片描述
角色表:
在这里插入图片描述
角色与菜单关联表:
在这里插入图片描述
用户表:
在这里插入图片描述
用户与角色关联表:
在这里插入图片描述
经过如上设计,就实现了垂直扩展(水平扩展自然而然实现)。这样一来的话,就会存在另一个问题,如何查数据,如何解析成树形的菜单??

递归方法解析!

这里引用另一篇博客:
https://www.cnblogs.com/lucky-pin/p/10740037.html
但是,该博客并不完整,因为在将菜单分配给角色时,需要指定到叶子菜单,叶子菜单所属的父级菜单也应该自动指定给角色。下面我将结合该篇博客完成完整的角色分配菜单功能。
在这里插入图片描述

public class Menu {
    private String id;
    private String parentId;
    private String text;
    private String url;
    private boolean selected=false;	// 默认是没有被选中的
    private String yxbz;
    private List<Menu> children;     
    public Menu(String id,String parentId,String text,String url,String yxbz) {
        this.id=id;
        this.parentId=parentId;
        this.text=text;
        this.url=url;
        this.yxbz=yxbz;   
    }
        /*省略get\set*/  
}

创建树形结构的类MenuTree。方法getRootNode获取所有根节点,方法builTree将根节点汇总创建树形结构,buildChilTree为节点建立次级树并拼接上当前树,递归调用buildChilTree不断为当前树开枝散叶直至找不到新的子树。完成递归,获取树形结构。

public class MenuTree {
    private List<Menu> menuList = new ArrayList<Menu>();
    public MenuTree(List<Menu> menuList) {
        this.menuList=menuList;
    }

    //建立树形结构
    public List<Menu> builTree(){
        List<Menu> treeMenus =new  ArrayList<Menu>();
        for(Menu menuNode : getRootNode()) {
            menuNode=buildChilTree(menuNode);
            treeMenus.add(menuNode);
        }
        return treeMenus;
    }

    //递归,建立子树形结构
    private Menu buildChilTree(Menu pNode){
        List<Menu> chilMenus =new  ArrayList<Menu>();
        for(Menu menuNode : menuList) {
            if(menuNode.getParentId().equals(pNode.getId())) {
                chilMenus.add(buildChilTree(menuNode));
            }
        }
        pNode.setChildren(chilMenus);
        return pNode;
    }

    //获取根节点
    private List<Menu> getRootNode() {         
        List<Menu> rootMenuLists =new  ArrayList<Menu>();
        for(Menu menuNode : menuList) {
            if(menuNode.getParentId().equals("0")) {
                rootMenuLists.add(menuNode);
            }
        }
        return rootMenuLists;
    }
}

在指定菜单是否被选中时,在菜单实体中新增了private boolean selected=false;成员变量,下面的代码就是指定叶子菜单给角色时的工具类,最完整的代码如下:(当然该类中的方法完全可以写到上面的MenuTree类中)

import java.util.ArrayList;
import java.util.List;

public class MenuTreeSelectedAndBuildTree {
	
	// 全部菜单
	private List<Menu> allMenuList = new ArrayList<Menu>();
	// 已经被选中的叶子菜单(只有最底层的菜单)
	private List<Menu> selectedMenuList = new ArrayList<Menu>();
	
	public MenuTreeSelectedAndBuildTree(List<Menu> allMenuList, List<Menu> selectedMenuList) {
		super();
		this.allMenuList = allMenuList;
		this.selectedMenuList = selectedMenuList;
	}
	
	// 设置是否选中状态的公开方法
	public List<Menu> setMenuSelected() {
		for (Menu menu : selectedMenuList) {
			String selectedMenuId = menu.getId();
			allMenuList = setSelectMeu(allMenuList, selectedMenuId);
		}
		
		return allMenuList;
	}

	// 对allMenuList中的每个对象开始遍历并判断是否需要设置flag
	private List<Menu> setSelectMeu(List<Menu> allMenuList2, String selectedMenuId) {
		// TODO Auto-generated method stub
		for (Menu menu : allMenuList2) {
			if (menu.getId().equals(selectedMenuId)) { 	// 如果相同则说明已经被选中了
				menu.setSelected(true);
				if (menu.getParentId() != null) {	// 递归的终止条件
					selectedMenuId = menu.getParentId();	// 以备下一次递归传入的参数
					setSelectMeu(allMenuList2, selectedMenuId);	// 开始递归调用
				}
			}
		}
		
		return allMenuList2;
	}
	
	//建立树形结构
    public List<Menu> builTree(){
        List<Menu> treeMenus =new ArrayList<Menu>();
        for(Menu menuNode : getRootNode()) {
            menuNode=buildChilTree(menuNode);
            treeMenus.add(menuNode);
        }
        return treeMenus;
    }

    //获取根节点
    private List<Menu> getRootNode() {         
        List<Menu> rootMenuLists =new  ArrayList<Menu>();
        for(Menu menuNode : allMenuList) {
            if(menuNode.getParentId().equals("0")) {
                rootMenuLists.add(menuNode);
            }
        }
        return rootMenuLists;
    }
    
    //递归,建立子树形结构
    private Menu buildChilTree(Menu pNode){
        List<Menu> chilMenus =new  ArrayList<Menu>();
        for(Menu menuNode : allMenuList) {
            if(menuNode.getParentId().equals(pNode.getId())) {
                chilMenus.add(buildChilTree(menuNode));
            }
        }
        pNode.setChildren(chilMenus);
        
        return pNode;
    }
}

最后,插入一些数据试试效果。得到的json就可以生成图一菜单了。

POM.xml中引入依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.56</version>
</dependency>

测试类:
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;

public class Hello {
    public static void main(String []args) {
    	// 所有的菜单
        List<Menu>  allMenuList= new ArrayList<Menu>();
        /*插入一些数据*/
        allMenuList.add(new Menu("GN001D000","0","系统管理","/admin","Y"));
        allMenuList.add(new Menu("GN001D100","GN001D000","权限管理","/admin","Y"));
        allMenuList.add(new Menu("GN001D110","GN001D100","密码修改","/admin","Y"));
        allMenuList.add(new Menu("GN001D120","GN001D100","新加用户","/admin","Y"));
        allMenuList.add(new Menu("GN001D200","GN001D000","系统监控","/admin","Y"));
        allMenuList.add(new Menu("GN001D210","GN001D200","在线用户","/admin","Y"));
        allMenuList.add(new Menu("GN002D000","0","订阅区","/admin","Y"));
        allMenuList.add(new Menu("GN003D000","0","未知领域","/admin","Y"));
        
        // 已经选中的菜单
        List<Menu>  selectedMenuList= new ArrayList<Menu>();
        Menu selectedMenu = new Menu("GN001D120","GN001D100","新加用户","/admin","Y");
        selectedMenu.setSelected(true);	// 设置新加用户被选中
        selectedMenuList.add(selectedMenu);
        
        /*设置菜单的选中情况并创建树*/
        MenuTreeSelectedAndBuildTree menuTree =new MenuTreeSelectedAndBuildTree(allMenuList, selectedMenuList);
        allMenuList = menuTree.setMenuSelected();	// 调用设置是否选中方法
        allMenuList = menuTree.setMenuSelected();	// 调用建立树形结构方法
        
        
        /*转为json看看效果*/
        String jsonOutput= JSON.toJSONString(allMenuList);
        System.out.println(jsonOutput);
    }
}

运行结果:

[{
	"id": "GN001D000",
	"parentId": "0",
	"selected": true,
	"text": "系统管理",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN001D100",
	"parentId": "GN001D000",
	"selected": true,
	"text": "权限管理",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN001D110",
	"parentId": "GN001D100",
	"selected": false,
	"text": "密码修改",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN001D120",
	"parentId": "GN001D100",
	"selected": true,
	"text": "新加用户",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN001D200",
	"parentId": "GN001D000",
	"selected": false,
	"text": "系统监控",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN001D210",
	"parentId": "GN001D200",
	"selected": false,
	"text": "在线用户",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN002D000",
	"parentId": "0",
	"selected": false,
	"text": "订阅区",
	"url": "/admin",
	"yxbz": "Y"
}, {
	"id": "GN003D000",
	"parentId": "0",
	"selected": false,
	"text": "未知领域",
	"url": "/admin",
	"yxbz": "Y"
}]

前端通过flag就可以判断是否被选中了。

猜你喜欢

转载自blog.csdn.net/huxiutao/article/details/93739362