菜单中的递归函数

http://localhost:8080/login?username=admin&password=admin
返回token
在Authorization字段加上token 访问localhost:8080/menu/findNavTree?userName=admin
接口返回的json对象如下:

{
    "status": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,         
            "parentId": 0,
            "name": "系统管理",
            "url": null,           
            "type": 0,
             ...
            "level": 0,
            "children": [
                {
                    "id": 2,                   
                    "parentId": 1,
                    "name": "用户管理",
                    "url": "/sys/user",                    
                    "type": 1,                   
                      ...   省略其他字段                
                    "level": 1,
                    "children": []
                }
                .......//省略
            ]
        },
        {
            "id": 43,            
            "parentId": 0,
            "name": "服务治理",
            "url": "",           
            "type": 0,           
            "level": 0,
            "children": [
                {
                    "id": 44,                   
                    "parentId": 43,
                    "name": "注册中心",
                    "url": "http://192.168.109.11:8500",                    
                    "type": 1,
                     .....                   
                    "level": 1,
                    "children": []
                }
            ]
        }
       .......//省略
    ]
}

获取后端菜单数据,然后以格式化形式存入routes中

 getRequest('/menu/findNavTree?userName=' + userName).then(res => {
        // 添加动态路由
        let dynamicRoutes = addDynamicRoutes(res.data)
        console.log('dynamicRoutes: ', dynamicRoutes)
        router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
        router.addRoutes(router.options.routes)
        // 保存加载状态
        store.commit('menuRouteLoaded', true)
        // 保存菜单树
        store.commit('setNavTree', res.data)
    })

参数menuList表示需要格式化的数据,routes存放格式化数据

原理:

假设后端json数据简化后对应的层级如下:系统管理的children有用户管理和机构管理。机构管理的children有查询操作和删除操作…
addDynamicRoutes函数的解析过程如下:遍历menuList [{系统管理},{服务治理},{接口文档}],发现系统管理和服务治理的children属性均不为空数组[],将他们的children存入temp数组。而接口文档的children为[],将接口文档push到routes。遍历完menuList然后判断temp数组是否加入了结点,如果加入了结点就递归解析。
第二轮遍历的menuList为[{用户管理,机构管理},{机构中心}]发现用户管理和机构管理的children为[],将用户管理和机构管理push到routes…不断重复此过程。
我们发现存入路由中的数据children为[],类似于树的叶子节点。路由过程的意义在于用户点击菜单项,到达叶子菜单项后,路由会根据component字段渲染相应的页面。

系统管理
----用户管理
----机构管理
服务治理
----机构中心
接口文档

注意: 保存菜单树使用的是后端数据,而不是格式化的路由数据。

store.commit(‘setNavTree’, res.data)

function addDynamicRoutes(menuList = [],routes =[]) { 
    var temp = []
    for (var i = 0; i < menuList.length; i++) {
    //children不为[],将children存入temp数组,之后处理
        if (menuList[i].children && menuList[i].children.length >= 1) {
            temp = temp.concat(menuList[i].children)
        } else if (menuList[i].url && /\S/.test(menuList[i].url)) {
            menuList[i].url = menuList[i].url.replace(/^\//, '')
            // 创建路由配置
            var route = {
                path: menuList[i].url,
                name: menuList[i].name,
                component: null,
                meta: {
                    icon: menuList[i].icon,
                    index: menuList[i].id
                }
            }
            try {
                // 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储
                // 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到
                let array = menuList[i].url.split('/')
                let url = ''
                for (let i = 0; i < array.length; i++) {                   
                    url += array[i].substring(0, 1).toUpperCase() + array[i].substring(1) + '/'
                }
                url = url.substring(0, url.length - 1)
                console.log('url',url)
                route['component'] = resolve => require(['@/views/' + url + '.vue'], resolve)
            } catch (e) {
            }           
            routes.push(route)
        }
    }
    if (temp.length >= 1) {
        addDynamicRoutes(temp, routes)
    } else {
        console.log('动态路由加载...')
        console.log(routes)
        console.log('动态路由加载完成.')
    }
    return routes
}

后端的查询菜单方法

findTree方法先查询顶级菜单,然后查找子菜单。

public List<SysMenu> findTree(String userName, int menuType) {
		List<SysMenu> sysMenus = new ArrayList<>();
		List<SysMenu> menus = findByUser(userName);
		for (SysMenu menu : menus) {
			if (menu.getParentId() == null || menu.getParentId() == 0) {
				menu.setLevel(0);    //顶级菜单
				if(!exists(sysMenus, menu)) { //防止重复添加
					sysMenus.add(menu);
				}
			}
		}
		sysMenus.sort((o1, o2) -> o1.getOrderNum().compareTo(o2.getOrderNum()));
		findChildren(sysMenus, menus, menuType);
		return sysMenus;
	}

findChidren方法是一个递归查询操作,递归出口SysMenus==NULL。
参数:SysMenus 父级别菜单;menus 当前用户查询到的所有菜单
menuType 标识变量,判断是否查询按钮操作菜单
按钮菜单比如查看、删除等属于第三级别,不属于菜单项范畴。

private void findChildren(List<SysMenu> SysMenus, List<SysMenu> menus, int menuType) {
		for (SysMenu SysMenu : SysMenus) {
			List<SysMenu> children = new ArrayList<>();
			for (SysMenu menu : menus) {
				if(menuType == 1 && menu.getType() == 2) {
					// 如果是获取类型不需要按钮,且菜单类型是按钮的,直接过滤掉
					continue ;
				}
				if (SysMenu.getId() != null && SysMenu.getId().equals(menu.getParentId())) {
				   //找到父菜单的子菜单
					menu.setParentName(SysMenu.getName());
					menu.setLevel(SysMenu.getLevel() + 1);
					if(!exists(children, menu)) {
						children.add(menu);
					}
				}
			}
			//设置children
			SysMenu.setChildren(children);
			children.sort((o1, o2) -> o1.getOrderNum().compareTo(o2.getOrderNum()));
			findChildren(children, menus, menuType);
		}  //end of loop
	}

总结:后端查询父菜单,然后分别查询子菜单,将子菜单添加到父菜单的children属性中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值