关于后台无限菜单的形成

首先, 就是在设计后台数据库里的菜单表, 无论字段是怎么在变化, 但是不变的大致有三个:  

主键id,  

对应的URL, 

parentId(表明该数据是某个菜单下的子菜单).

 

单纯的从java后台来说. 我们需要做的就是遍历出这些数据,然后按一定顺序组成一串json数据返回给前台. 

返回的数据格式也有常规:

id, URL, parentId,Children(这里的类型是List, 这个就算是一个重点了). 

一般来说实体类最基础包含就是要有:  id, URL, parentId,Children. 

大致模样如下, 其他字段是为了满足我所使用的前台框架需要的条件, 这就要根据自己用的框架进行配合了:

[
  {
    "id": 1,
    "parentId": 0,
    "url": "",
    "text": "系统设置",
    "checked": "false",
    "content": "测试",
    "state": "open",
    "children": [
      {
        "id": 2,
        "parentId": 1,
        "url": "",
        "text": "菜单设置",
        "checked": "false",
        "content": "测试",
        "state": "null",
        "children": [
          {
            "id": 5,
            "parentId": 2,
            "url": "sys/menu/index",
            "text": "分配权限",
            "checked": "false",
            "content": "测试",
            "state": "null",
            "children": []
          }
        ]
      },
      {
        "id": 3,
        "parentId": 1,
        "url": "sys/menu/index",
        "text": "角色管理",
        "checked": "false",
        "content": "测试",
        "state": "null",
        "children": []
      },
      {
        "id": 4,
        "parentId": 1,
        "url": "sys/menu/index",
        "text": "用户管理",
        "checked": "false",
        "content": "测试",
        "state": "null",
        "children": []
      }
    ]
  },
  {
    "id": 8,
    "parentId": 0,
    "url": "",
    "text": "基础设置",
    "checked": "false",
    "content": "测试",
    "state": "open"
  }
]

不用觉得很复杂. 把这几条数据拆开看. 无非就是  一个菜单(有或没有子菜单).

如果没有子菜单,则parentId就要是0,  这里的parentId就是一个辨识符号的作用. 如果不为0,则表示它是某一个菜单的子菜单.

如果是子菜单, 则将其放入对应的父菜单里面的Children这个List里面去.

 

大致意思就这样子. 

接下来就是具体的代码. 按常规来讲, 提到无限菜单的形成大家立马想起的就是用 "递归". 

这里简介下递归, 最为简单的来讲就是:

写一个方法A, 在这个方法A里面 通过判断某些条件,继续调用方法A, 直到满足了我们最初设定的跳出这个方法的条件才结束.

第一步: 

先通过一个最简单的查询方法, 查询出所有的菜单.以List形式返回.

 List<Object> menu = menuService.getMenu();

第二步: 

准备一个 容纳 顶级菜单(也就是parentId==0的所有菜单就是最顶级的菜单) 的 List容器

 //准备容器
 List<Object> menuList = new ArrayList<>();

第三步: 

遍历 由第一步 所得到的所有菜单, 根据条件判断, 得到所有顶级菜单, 并将其放入 第二步 准备的容器中.

 for (Object o : menu) {
            TbMenu menuOne = (TbMenu)o;//先转换成你已经写好的菜单实体类
            //接着遍历出父最顶级一层的菜单. 条件就是 parentId ==0
            if (0==menuOne.getParentId()){
                menuList.add(menuOne);
            }
        }

第四步:

这里就要开始准备写递归方法了.   getChild(父菜单id, 所有菜单)

//   id: 父菜单id;  rootMenu: 所有菜单
    private List<TbMenu> getChild(long id, List<Object> rootMenu) {

        // 准备接收子菜单的 容器
        List<TbMenu> ChildrenList= new ArrayList<>();

        //遍历带过来的 所有菜单
        for (Object menus : rootMenu) {
            TbMenu menu = (TbMenu)menus;//先转换成项目中具体实体类

            // 如果有菜单的 父id 不为0 ,并且,该菜单的父id 和传递进来的一级菜单id相同.则证明它是传递进来的菜单下的子菜单
            if (menu.getParentId()!=0&&menu.getParentId()==id) {
                    childList.add(menu);
            }
        }

        //  这里算是重点: 开始条用递归方法. 也就是 将 childList 这个子菜单再循环一遍.
        for (TbMenu menu : ChildrenList) {
            // 明确: 如果一行数据中的url没有值,则表明它是一个父级菜单(其下一定有子菜单)
            if (StringUtils.isBlank(menu.getUrl())) {
                //递归(再次调用方法, 以该菜单id为辨认.再次和所有菜单进行遍历比较.找出它的子菜单)并将它的子菜单赋值进Children这个实体类shuxin
                menu.setChildren(getChild(menu.getId(), rootMenu));
            }
        }

        // 重点二: 这里就是递归退出条件: 如果childList.size() == 0,则表明传递进来的菜单下没有子菜单.
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }

 

以上的递归只要看明白了就好.

 

第五步:

这里的代码准确来说是在 第三步 得到所有 顶级菜单后执行的. 第四步就是个独立的用来递归的方法而已.

//        遍历顶级菜单.调用递归方法.
        for (Object b : menuList) {
            TbMenu menuTwo= (TbMenu)b;//先转换
            //调用递归方法: getChild(顶级菜单id, 所有菜单内容).得到该菜单下的子菜单.
            menuTwo.setChildren(getChild(menuTwo.getId(), menu));
        }

用文字说明(啰嗦下就是): 

遍历所有 顶级菜单

 

循环, 拿到一个 (A)父菜单Id, 和全部菜单进行比较, 找出  A 的所有子菜单, 然后把这些子菜单放进 一个List.

 

再遍历得到的这个  List, 循环, 拿到 一个(Aa)子菜单的id, 再次和全部菜单进行比较.

 

每一次遍历就是 一个对象. 每一次递归方法结束, 都要用 该对象.setChildren(子菜单).

 

最后: 全部代码记录如下:

package manage.controller;


import com.alibaba.fastjson.JSON;
import manage.pojo.TbMenu;
import manage.service.MenuService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

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

@Controller
@RequestMapping("/admin/menu")
public class MenuController {

    @Autowired
    private MenuService menuService;

    @RequestMapping(value="/tree",produces = "text/json;charset=UTF-8")
    @ResponseBody
    public String getTree() {
        List<Object> menu = menuService.getMenu();

        //准备容器
        List<Object> menuList = new ArrayList<>();

        for (Object o : menu) {
            TbMenu menuOne = (TbMenu)o;//先转换
            //接着遍历出父最顶级一层的菜单
            if (0==menuOne.getParentId()){
                menuList.add(menuOne);
            }
        }

//        遍历顶级菜单.调用递归方法.
        for (Object b : menuList) {
            TbMenu menuTwo= (TbMenu)b;//先转换
            //调用递归方法: getChild(顶级菜单id, 所有菜单内容).得到该菜单下的子菜单.
            menuTwo.setChildren(getChild(menuTwo.getId(), menu));
        }

//        将按顺序装好的菜单List转换成前台需要的json格式.
        final String result = JSON.toJSONString(menuList);
        System.out.println(result);
        return result;
    }

//   id: 一级菜单id;  rootMenu: 所有菜单
    private List<TbMenu> getChild(long id, List<Object> rootMenu) {

        // 准备接收子菜单
        List<TbMenu> childList = new ArrayList<>();

        //遍历所有菜单
        for (Object menus : rootMenu) {
            TbMenu menu = (TbMenu)menus;//先转换成项目中具体实体类

            // 如果有菜单的 父id 不为0 ,并且,该菜单的父id 和传递进来的一级菜单id相同.则证明它是传递进来的一级菜单下的子菜单
            if (menu.getParentId()!=0&&menu.getParentId()==id) {
                    childList.add(menu);
            }
        }

        //  将 childList 这个子菜单再循环一遍.
        for (TbMenu menu : childList) {
            // 明确: 如果一行数据中的url没有值,则表明它是一个父级菜单(其下一定有子菜单)
            if (StringUtils.isBlank(menu.getUrl())) {
                // 递归(再次调用方法, 以该菜单id为辨认.再次和所有菜单进行遍历比较.找出它的子菜单)
                menu.setChildren(getChild(menu.getId(), rootMenu));
            }
        }

        // 递归退出条件: 如果childList.size() == 0,则表明传递进来的菜单下没有子菜单.
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }
}

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值