首先, 就是在设计后台数据库里的菜单表, 无论字段是怎么在变化, 但是不变的大致有三个:
主键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
第二步:
准备一个 容纳 顶级菜单(也就是parentId==0的所有菜单就是最顶级的菜单) 的 List容器
//准备容器
List
第三步:
遍历 由第一步 所得到的所有菜单, 根据条件判断, 得到所有顶级菜单, 并将其放入 第二步 准备的容器中.
for (Object o : menu) {
TbMenu menuOne = (TbMenu)o;//先转换成你已经写好的菜单实体类
//接着遍历出父最顶级一层的菜单. 条件就是 parentId ==0
if (0==menuOne.getParentId()){
menuList.add(menuOne);
}
}
第四步:
这里就要开始准备写递归方法了. getChild(父菜单id, 所有菜单)
// id: 父菜单id; rootMenu: 所有菜单
private List getChild(long id, List
以上的递归只要看明白了就好.
第五步:
这里的代码准确来说是在 第三步 得到所有 顶级菜单后执行的. 第四步就是个独立的用来递归的方法而已.
// 遍历顶级菜单.调用递归方法.
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