1、有时候操作菜单功能的时候难免回遇见这一类问题: 有一个菜单,菜单里面还有子菜单,子菜单里面还有子菜单。。。。。等等,我们需要一次性将数据组装好发给前端工程师。
2、这个时候思路其实也很多的,有的人说用存储过程!我个人是不推荐的,毕竟使用存储过程是在数据库端,对应java代码的维护要做相应改变,所有我个人推荐使用Java代码层面递归构建这个树形结构!
3、在实现功能前我们可以看看数据库表中的数据:当然这些都是我写的假数据。
可以看见有Pid(就是父类编号)、id(自己)、name(名称)其实最主要就是这个三个字段在操纵。
4、接下来我编写一个菜单实体类:
package com.java.entity;
import java.io.Serializable;
import java.util.List;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Title: SystemMenuVO.java
* @Package com.amt.model
* @Description: TODO(系统菜单类)
* @author lys
* @date 2019年05月15日 上午11:40:20
* Copyright (c) ©1994-2018 Scjydz.com All Rights Reserved.
*/
@Data
public class SystemMenuAndChildrenVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4585856963713032353L;
// 菜单编号
@ApiModelProperty(value = "菜单编号(仅供编辑使用)", example = "1")
private Long id;
// 父级编号
@ApiModelProperty(value = "父级编号", example = "1")
private Long pid;
// 菜单名称
@ApiModelProperty(value = "菜单名称", example = "检查菜单")
private String name;
// 类型 0:目录 1:菜单 2:按钮
@ApiModelProperty(value = "类型 0:目录 1:菜单 2:按钮", example = "0")
private Short type;
// 子菜单
private List<SystemMenuAndChildrenVO> children;
}
5、接下来编写一个递归工具类:
package com.java.util;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.java.entity.SystemMenuAndChildrenVO;
/**
*
* @Title: MenuTreeUtil.java
* @Package com.amt.util
* @Description: 递归构造树型结构
* @author lys
* @date 2019年5月15日 上午11:31:54
* Copyright (c) ©1994-2019 Scjydz.com All Rights Reserved.
*/
public class MenuTreeUtil {
//保存菜单
public List<SystemMenuAndChildrenVO> menuCommon;
//数据结果绑定生成结果集合
public List<Object> list = new ArrayList<Object>();
/**
*
* @description:
* @param menu 所有需要组装的数据集合
* @return 组装为树形结构的集合数据
*/
public List<Object> menuList(List<SystemMenuAndChildrenVO> menu){
this.menuCommon = menu;
for (SystemMenuAndChildrenVO x : menu) {
Map<String,Object> mapArr = new LinkedHashMap<String, Object>();
if(x.getPid()!=null&&x.getPid()==0){
mapArr.put("id", x.getId());
mapArr.put("name", x.getName());
mapArr.put("pid", x.getPid());
mapArr.put("children", menuChild(x.getId()));
list.add(mapArr);
}
}
return list;
}
/**
*
* @description: 用于获取子类方法
* @param id 父类Id
* @return 子类集合
*/
public List<?> menuChild(Long id){
List<Object> lists = new ArrayList<Object>();
for(SystemMenuAndChildrenVO a:menuCommon){
Map<String,Object> childArray = new LinkedHashMap<String, Object>();
if(a.getPid()!=null&&a.getPid() == id){
childArray.put("id", a.getId());
childArray.put("name", a.getName());
childArray.put("pid", a.getPid());
childArray.put("children", menuChild(a.getId()));
lists.add(childArray);
}
}
return lists;
}
}
6、写好之后我们写一个Controller来测试一下:
/**
* Project Name:StudySpringApp
* File Name:TestTreeMenu.java
* Package Name:com.java.controller
* Date:2019年6月14日下午2:17:05
* Copyright (c) ©1994-2019 Scjydz.com All Rights Reserved.
*/
package com.java.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.java.entity.SystemMenuAndChildrenVO;
import com.java.mapper.systemMenuEntityMapper;
import com.java.util.MenuTreeUtil;
import com.java.util.ResponseData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
/**
* @Title: TestTreeMenu.java
* @Package com.java.controller
* @Description: TODO(测试菜单组合控制器)
* @author lys
* @date 2019年6月14日 下午2:17:05
* Copyright (c) ©1994-2019 Scjydz.com All Rights Reserved.
*/
@Api(tags = "测试TestTreeMenuAPI")
@RestController
@Slf4j
public class TestTreeMenu {
@Autowired
private systemMenuEntityMapper menuEntityMapper;
@ApiOperation(value = "菜单测试", notes = "菜单组合测试")
@ApiResponses({ @ApiResponse(code = 200, message = "查询成功", response =SystemMenuAndChildrenVO.class),
@ApiResponse(code = 201, message = "无查询数据", response = Void.class) })
@GetMapping("/testTreeMenu")
public ResponseData testTreeMenu() {
//统一参数返回
ResponseData data = new ResponseData();
//查询所有菜单准备组装数据
List<SystemMenuAndChildrenVO> Menus = menuEntityMapper.selectAll();//这里只需要一个查询菜单表所有数据即可
//获得递归组装工具
MenuTreeUtil treeUtil = new MenuTreeUtil();
//组装数据
List<Object> info = treeUtil.menuList(Menus);
//日志打印看看组装结构
log.info(info.toString());
data.getData().put("Menus", info);
return data;
}
}
启动项目测试组装结果:
看看控制台日志打印:
2019-06-14 14:43:38,483 INFO (TestTreeMenu.java:58)- [{id=38, name=体检用户, pid=0, children=[{id=107, name=体检账号, pid=38, children=[]}, {id=108, name=体检预约人, pid=38, children=[]}]}, {id=39, name=体检套餐, pid=0, children=[{id=78, name=套餐管理, pid=39, children=[]}, {id=86, name=体检项管理, pid=39, children=[]}]}, {id=40, name=体检问卷, pid=0, children=[{id=109, name=问卷管理, pid=40, children=[{id=111, name=关联试题, pid=109, children=[]}, {id=112, name=删除, pid=109, children=[]}, {id=113, name=编辑, pid=109, children=[]}, {id=114, name=详情, pid=109, children=[]}, {id=134, name=添加, pid=109, children=[]}]}, {id=110, name=试题管理, pid=40, children=[{id=115, name=关联检查项, pid=110, children=[]}, {id=117, name=编辑, pid=110, children=[]}, {id=118, name=详情, pid=110, children=[]}, {id=135, name=添加, pid=110, children=[]}, {id=147, name=删除, pid=110, children=[]}]}]}, {id=41, name=满意度调查, pid=0, children=[{id=85, name=满意度调查, pid=41, children=[]}]}, {id=42, name=订单管理, pid=0, children=[{id=66, name=体检预约订单, pid=42, children=[{id=138, name=详情, pid=66, children=[]}]}, {id=67, name=陪护床预约订单, pid=42, children=[{id=93, name=退款, pid=67, children=[]}, {id=94, name=发床, pid=67, children=[]}, {id=95, name=驳回, pid=67, children=[]}, {id=119, name=详情, pid=67, children=[]}]}, {id=68, name=住院床位订单, pid=42, children=[{id=136, name=详情, pid=68, children=[]}]}, {id=69, name=检验检查订单, pid=42, children=[{id=137, name=详情, pid=69, children=[]}]}, {id=70, name=病历寄送订单, pid=42, children=[{id=96, name=补录单号, pid=70, children=[]}, {id=120, name=详情, pid=70, children=[]}]}]}, {id=43, name=咨询管理, pid=0, children=[{id=71, name=检验咨询, pid=43, children=[{id=97, name=编辑, pid=71, children=[]}, {id=98, name=删除, pid=71, children=[]}, {id=125, name=详情, pid=71, children=[]}, {id=130, name=添加, pid=71, children=[]}]}, {id=72, name=导诊咨询, pid=43, children=[{id=99, name=编辑, pid=72, children=[]}, {id=100, name=删除, pid=72, children=[]}, {id=122, name=详情, pid=72, children=[]}, {id=131, name=添加, pid=72, children=[]}]}, {id=73, name=体检咨询, pid=43, children=[{id=101, name=编辑, pid=73, children=[]}, {id=102, name=删除, pid=73, children=[]}, {id=123, name=详情, pid=73, children=[]}, {id=132, name=添加, pid=73, children=[]}]}, {id=74, name=用药咨询, pid=43, children=[{id=103, name=编辑, pid=74, children=[]}, {id=104, name=删除, pid=74, children=[]}, {id=124, name=详情, pid=74, children=[]}, {id=133, name=添加, pid=74, children=[]}]}]}, {id=44, name=系统管理, pid=0, children=[{id=47, name=用户管理, pid=44, children=[{id=48, name=编辑, pid=47, children=[]}, {id=49, name=删除, pid=47, children=[]}, {id=58, name=添加, pid=47, children=[]}, {id=139, name=详情, pid=47, children=[]}]}, {id=51, name=角色管理, pid=44, children=[{id=63, name=添加, pid=51, children=[]}, {id=64, name=编辑, pid=51, children=[]}, {id=65, name=删除, pid=51, children=[]}, {id=140, name=详情, pid=51, children=[]}]}, {id=52, name=菜单管理, pid=44, children=[{id=60, name=新增, pid=52, children=[]}, {id=61, name=编辑, pid=52, children=[]}, {id=62, name=删除, pid=52, children=[]}, {id=141, name=详情, pid=52, children=[]}]}, {id=54, name=参数管理, pid=44, children=[{id=143, name=添加, pid=54, children=[]}, {id=144, name=编辑, pid=54, children=[]}, {id=145, name=删除, pid=54, children=[]}, {id=146, name=详情, pid=54, children=[]}]}]}]
这里是前端得到的数据,不知道是不是你想要的结果!
{
"code": 200,
"message": "ok",
"data": {
"Menus": [
{
"id": 38,
"name": "体检用户",
"pid": 0,
"children": [
{
"id": 107,
"name": "体检账号",
"pid": 38,
"children": []
},
{
"id": 108,
"name": "体检预约人",
"pid": 38,
"children": []
}
]
},
{
"id": 39,
"name": "体检套餐",
"pid": 0,
"children": [
{
"id": 78,
"name": "套餐管理",
"pid": 39,
"children": []
},
{
"id": 86,
"name": "体检项管理",
"pid": 39,
"children": []
}
]
},
{
"id": 40,
"name": "体检问卷",
"pid": 0,
"children": [
{
"id": 109,
"name": "问卷管理",
"pid": 40,
"children": [
{
"id": 111,
"name": "关联试题",
"pid": 109,
"children": []
},
{
"id": 112,
"name": "删除",
"pid": 109,
"children": []
},
{
"id": 113,
"name": "编辑",
"pid": 109,
"children": []
},
{
"id": 114,
"name": "详情",
"pid": 109,
"children": []
},
{
"id": 134,
"name": "添加",
"pid": 109,
"children": []
}
]
},
{
"id": 110,
"name": "试题管理",
"pid": 40,
"children": [
{
"id": 115,
"name": "关联检查项",
"pid": 110,
"children": []
},
{
"id": 117,
"name": "编辑",
"pid": 110,
"children": []
},
{
"id": 118,
"name": "详情",
"pid": 110,
"children": []
},
{
"id": 135,
"name": "添加",
"pid": 110,
"children": []
},
{
"id": 147,
"name": "删除",
"pid": 110,
"children": []
}
]
}
]
},
{
"id": 41,
"name": "满意度调查",
"pid": 0,
"children": [
{
"id": 85,
"name": "满意度调查",
"pid": 41,
"children": []
}
]
},
{
"id": 42,
"name": "订单管理",
"pid": 0,
"children": [
{
"id": 66,
"name": "体检预约订单",
"pid": 42,
"children": [
{
"id": 138,
"name": "详情",
"pid": 66,
"children": []
}
]
},
{
"id": 67,
"name": "陪护床预约订单",
"pid": 42,
"children": [
{
"id": 93,
"name": "退款",
"pid": 67,
"children": []
},
{
"id": 94,
"name": "发床",
"pid": 67,
"children": []
},
{
"id": 95,
"name": "驳回",
"pid": 67,
"children": []
},
{
"id": 119,
"name": "详情",
"pid": 67,
"children": []
}
]
},
{
"id": 68,
"name": "住院床位订单",
"pid": 42,
"children": [
{
"id": 136,
"name": "详情",
"pid": 68,
"children": []
}
]
},
{
"id": 69,
"name": "检验检查订单",
"pid": 42,
"children": [
{
"id": 137,
"name": "详情",
"pid": 69,
"children": []
}
]
},
{
"id": 70,
"name": "病历寄送订单",
"pid": 42,
"children": [
{
"id": 96,
"name": "补录单号",
"pid": 70,
"children": []
},
{
"id": 120,
"name": "详情",
"pid": 70,
"children": []
}
]
}
]
},
{
"id": 43,
"name": "咨询管理",
"pid": 0,
"children": [
{
"id": 71,
"name": "检验咨询",
"pid": 43,
"children": [
{
"id": 97,
"name": "编辑",
"pid": 71,
"children": []
},
{
"id": 98,
"name": "删除",
"pid": 71,
"children": []
},
{
"id": 125,
"name": "详情",
"pid": 71,
"children": []
},
{
"id": 130,
"name": "添加",
"pid": 71,
"children": []
}
]
},
{
"id": 72,
"name": "导诊咨询",
"pid": 43,
"children": [
{
"id": 99,
"name": "编辑",
"pid": 72,
"children": []
},
{
"id": 100,
"name": "删除",
"pid": 72,
"children": []
},
{
"id": 122,
"name": "详情",
"pid": 72,
"children": []
},
{
"id": 131,
"name": "添加",
"pid": 72,
"children": []
}
]
},
{
"id": 73,
"name": "体检咨询",
"pid": 43,
"children": [
{
"id": 101,
"name": "编辑",
"pid": 73,
"children": []
},
{
"id": 102,
"name": "删除",
"pid": 73,
"children": []
},
{
"id": 123,
"name": "详情",
"pid": 73,
"children": []
},
{
"id": 132,
"name": "添加",
"pid": 73,
"children": []
}
]
},
{
"id": 74,
"name": "用药咨询",
"pid": 43,
"children": [
{
"id": 103,
"name": "编辑",
"pid": 74,
"children": []
},
{
"id": 104,
"name": "删除",
"pid": 74,
"children": []
},
{
"id": 124,
"name": "详情",
"pid": 74,
"children": []
},
{
"id": 133,
"name": "添加",
"pid": 74,
"children": []
}
]
}
]
},
{
"id": 44,
"name": "系统管理",
"pid": 0,
"children": [
{
"id": 47,
"name": "用户管理",
"pid": 44,
"children": [
{
"id": 48,
"name": "编辑",
"pid": 47,
"children": []
},
{
"id": 49,
"name": "删除",
"pid": 47,
"children": []
},
{
"id": 58,
"name": "添加",
"pid": 47,
"children": []
},
{
"id": 139,
"name": "详情",
"pid": 47,
"children": []
}
]
},
{
"id": 51,
"name": "角色管理",
"pid": 44,
"children": [
{
"id": 63,
"name": "添加",
"pid": 51,
"children": []
},
{
"id": 64,
"name": "编辑",
"pid": 51,
"children": []
},
{
"id": 65,
"name": "删除",
"pid": 51,
"children": []
},
{
"id": 140,
"name": "详情",
"pid": 51,
"children": []
}
]
},
{
"id": 52,
"name": "菜单管理",
"pid": 44,
"children": [
{
"id": 60,
"name": "新增",
"pid": 52,
"children": []
},
{
"id": 61,
"name": "编辑",
"pid": 52,
"children": []
},
{
"id": 62,
"name": "删除",
"pid": 52,
"children": []
},
{
"id": 141,
"name": "详情",
"pid": 52,
"children": []
}
]
},
{
"id": 54,
"name": "参数管理",
"pid": 44,
"children": [
{
"id": 143,
"name": "添加",
"pid": 54,
"children": []
},
{
"id": 144,
"name": "编辑",
"pid": 54,
"children": []
},
{
"id": 145,
"name": "删除",
"pid": 54,
"children": []
},
{
"id": 146,
"name": "详情",
"pid": 54,
"children": []
}
]
}
]
}
]
}
}
代码和实现思路都很简单,笔者才开始写,写得不好的,大神们多多指点(#.#)!
这是Sql文件需要数据的朋友可以下载:https://pan.baidu.com/s/1Jfwqm0uLPPWb8s3ez5fIAw
提取码:iny2
笔者外附加一个前端传入菜单树形结构Josn数据,然后后端得到数据解析工具类供大家参考如果觉得实用可以点点收藏:
package com.java.util;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
/**
* @Title: JosnMenuTreeUtil.java
* @Package com.amt.util
* @Description: 递归获取Josn数据ID和Name
* @author lys
* @date 2019年5月15日 上午11:30:12 Copyright (c) ©1994-2019 Scjydz.com All Rights
* Reserved.
*/
public class JosnMenuTreeUtil {
// 用于保存id和name拼接值
public Map<String, StringBuffer> mapArr = new LinkedHashMap<String, StringBuffer>();
// 用于拼接id格式为(id,id2,id3,id4,)
public StringBuffer id = new StringBuffer();
// 用于拼接name格式为(name1,name2,name3,name4,)
public StringBuffer name = new StringBuffer();
/**
* @description: 解析json树数据将所有的id和name已Map格式返回
* @param menu Json List集合
* @param jsonId json的Id字段
* @param jsonName json的name字段
* @param jsonChildren json的children字段
* @return Map<id,name> 已id和name打包返回
*/
@SuppressWarnings("rawtypes")
public Map<String, StringBuffer> menuList(List menu, String jsonId, String jsonName, String jsonChildren) {
for (int index = 0; index < menu.size(); index++) {
id.append(JSONObject.parseObject(menu.get(index).toString()).getString(jsonId) + ",");
name.append(JSONObject.parseObject(menu.get(index).toString()).getString(jsonName) + ",");
List myListTwo = JSONObject.parseObject(menu.get(index).toString()).getObject(jsonChildren, List.class);
menuChild(myListTwo, jsonId, jsonName, jsonChildren);
}
mapArr.put("id", id);
mapArr.put("name", name);
return mapArr;
}
/**
*
* @description: 解析json树数据将所有的id和name递归拼接直到List为null
* @param List Json List集合
* @param jsonId json的Id字段
* @param jsonName json的name字段
* @param jsonChildren json的children字段
*/
@SuppressWarnings("rawtypes")
public void menuChild(List list, String jsonId, String jsonName, String jsonChildren) {
List myListTwo = null;
for (int index = 0; index < list.size(); index++) {
id.append(JSONObject.parseObject(list.get(index).toString()).getString(jsonId) + ",");
name.append(JSONObject.parseObject(list.get(index).toString()).getString(jsonName) + ",");
myListTwo = JSONObject.parseObject(list.get(index).toString()).getObject(jsonChildren, List.class);
if (myListTwo != null) {
menuChild(myListTwo, jsonId, jsonName, jsonChildren);
}
}
}
/**
*
* @description: main方法测试
* @param args
*/
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
JosnMenuTreeUtil util = new JosnMenuTreeUtil();
String json = "{\"roleName\":\"\",\"roleSign\":[{\"id\":38,\"name\":\"体检用户\",\"pid\":0,\"children\":[{\"id\":46,\"name\":\"体检账号\",\"pid\":38,\"children\":[{\"id\":81,\"name\":\"体检详情\",\"pid\":46,\"children\":[]}]},{\"id\":82,\"name\":\"体检预约人\",\"pid\":38,\"children\":[]}]},{\"id\":39,\"name\":\"体检套餐\",\"pid\":0,\"children\":[{\"id\":78,\"name\":\"套餐管理\",\"pid\":39,\"children\":[]},{\"id\":86,\"name\":\"体检项管理\",\"pid\":39,\"children\":[]}]}],\"remark\":\"\"}";
JSONObject pa = JSONObject.parseObject(json);
List myList = pa.getObject("roleSign", List.class);
Map<String, StringBuffer> map = util.menuList(myList, "id", "name", "children");
System.out.println(map.get("id"));
System.out.println(map.get("name"));
}
}
有main方法测试的,如果需求不一样的自行修改。原理都差不多