创建Menu类
创建menu类,键名必须和layui文档一致,否则不能读取数据
public class Menu {
private int id;
private String title;
private int pId;
private List<Menu> children = new ArrayList<>();
//getter setter constructor省略
}
layui接收的数据是嵌套的json数组
data = [{
title: '一级1'
,id: 1
,field: 'name1'
,checked: true
,spread: true
,children: [{
title: '二级1-1 可允许跳转'
,id: 3
,field: 'name11'
,href: 'https://www.layui.com/'
,children: [{
title: '三级1-1-3'
,id: 23
,field: ''
,children: [{
title: '四级1-1-3-1'
,id: 24
,field: ''
,children: [{
title: '五级1-1-3-1-1'
,id: 30
,field: ''
},{
title: '五级1-1-3-1-2'
,id: 31
,field: ''
}]
}]
},{
title: '三级1-1-1'
,id: 7
,field: ''
,children: [{
title: '四级1-1-1-1 可允许跳转'
,id: 15
,field: ''
,href: 'https://www.layui.com/doc/'
}]
},{
title: '三级1-1-2'
,id: 8
,field: ''
,children: [{
title: '四级1-1-2-1'
,id: 32
,field: ''
}]
}]
},{
title: '二级1-2'
,id: 4
,spread: true
,children: [{
title: '三级1-2-1'
,id: 9
,field: ''
,disabled: true
},{
title: '三级1-2-2'
,id: 10
,field: ''
}]
},{
title: '二级1-3'
,id: 20
,field: ''
,children: [{
title: '三级1-3-1'
,id: 21
,field: ''
},{
title: '三级1-3-2'
,id: 22
,field: ''
}]
}]
},{
title: '一级2'
,id: 2
,field: ''
,spread: true
,children: [{
title: '二级2-1'
,id: 5
,field: ''
,spread: true
,children: [{
title: '三级2-1-1'
,id: 11
,field: ''
},{
title: '三级2-1-2'
,id: 12
,field: ''
}]
},{
title: '二级2-2'
,id: 6
,field: ''
,children: [{
title: '三级2-2-1'
,id: 13
,field: ''
},{
title: '三级2-2-2'
,id: 14
,field: ''
,disabled: true
}]
}]
},{
title: '一级3'
,id: 16
,field: ''
,children: [{
title: '二级3-1'
,id: 17
,field: ''
,fixed: true
,children: [{
title: '三级3-1-1'
,id: 18
,field: ''
},{
title: '三级3-1-2'
,id: 19
,field: ''
}]
},{
title: '二级3-2'
,id: 27
,field: ''
,children: [{
title: '三级3-2-1'
,id: 28
,field: ''
},{
title: '三级3-2-2'
,id: 29
,field: ''
}]
}]
}]
Mapper
public interface MenuMapper {
@Select("select * from menu")
List<Menu> selectAllMenu();
@Select("select * from menu where id = #{id}")
Menu selectMenuById(Integer id);
//查询一个父类ID下的所有子类
@Select("select * from menu where pId = #{pId}")
List<Menu> selectMenuByPId(Integer pId);
//添加分类,顶级pid为0
@Insert("insert into menu (id,title,pId) value(#{id},#{title},#{pId})")
int addMenu(Menu menu);
@Update("update menu set title = #{title} , pId = #{pId} where id = #{id}")
int updateMenu(Menu menu);
@Delete("delete from menu where id = #{id}")
int delMenuById(Menu menu);
@Delete("delete from menu where title = #{title}")
int delMenuByTitle(Menu menu);
}
Service及实现类省略,没有特殊的代码
Controller
@Controller
@RequestMapping("/admin/menu")
public class MenuController {
@Resource
private MenuService menuService;
@GetMapping("/")
public String allMenu() {
return "/admin/menu/list";
}
@GetMapping("/list")
@ResponseBody
public JsonResult findAllMenu() {
List<Menu> menus = menuService.selectAllMenu();
List<Menu> menusTree = TreeUtil.toTree(menus);//下面类方法将所有菜单数据转化为json嵌套数据
for (Menu menu : menusTree) {
System.out.println(menu.toString());
}
return JsonResult.success(menusTree);
}
@PostMapping("/add")
@ResponseBody
public JsonResult addMenu(Menu parent) {
System.out.println(parent);
Menu menu = new Menu();
menu.setpId(parent.getId());
menu.setTitle("未命名");
int i = menuService.addMenu(menu);
if (i > 0) {
return JsonResult.success();
} else {
return JsonResult.failed("1", "添加失败");
}
}
@PostMapping("/edit")
@ResponseBody
public JsonResult editMenu(Menu menu) {
System.out.println(menu.getId());
String editTitle = menu.getTitle();
Menu selMenu = new Menu();
selMenu.setTitle(editTitle);
Menu resMenu = menuService.selectMenuByTitle(selMenu);
if (resMenu != null) {
return JsonResult.failed("1","已存在该类名");
}
int i = menuService.updateMenu(menu);
if (i > 0) {
return JsonResult.success();
} else {
return JsonResult.failed("1", "修改失败");
}
}
@PostMapping("/del")
@ResponseBody
public JsonResult delMenu(Menu menu) {
int i = menuService.delMenuById(menu);
if (i > 0) {
return JsonResult.success();
} else {
return JsonResult.failed("1", "删除失败");
}
}
}
TreeUtil工具类,将menu列表转化为嵌套json
public class TreeUtil {
/**
* 所有待用"菜单"
*/
private static List<Menu> all = null;
/**
* 转换为树形
* @param list 所有节点
* @return 转换后的树结构菜单
*/
public static List<Menu> toTree(List<Menu> list) {
// 最初, 所有的 "菜单" 都是待用的
all = new ArrayList<>(list);
// 拿到所有的顶级 "菜单"
List<Menu> roots = new ArrayList<>();
for (Menu menu : list) {
if (menu.getpId() == 0) {
roots.add(menu);
}
}
// 将所有顶级菜单从 "待用菜单列表" 中删除
all.removeAll(roots);
//调用下面静态方法,将顶级菜单中的子节点加入children列表,同时该方法会将子节点的子节点持续循环
for (Menu menu : roots) {
menu.setChildren(getCurrentNodeChildren(menu));
}
return roots;
}
/**
* 递归函数
* 递归目的: 拿到子节点
* 递归终止条件: 没有子节点
* @param parent 父节点
* @return 子节点
*/
private static List<Menu> getCurrentNodeChildren(Menu parent) {
// 判断当前节点有没有子节点, 没有则创建一个空长度的 List, 有就使用之前已有的所有子节点.
List<Menu> childList = parent.getChildren() == null ? new ArrayList<>() : parent.getChildren();
// 从 "待用菜单列表" 中找到当前节点的所有子节点
for (Menu child : all) {
if (parent.getId() == child.getpId()) {
childList.add(child);
}
}
// 将当前节点的所有子节点从 "待用菜单列表" 中删除
all.removeAll(childList);
// 所有的子节点再寻找它们自己的子节点
for (Menu menu : childList) {
menu.setChildren(getCurrentNodeChildren(menu));
}
return childList;
}
}
前端
<body>
<div class="menu-center">
<div class="layui-btn-container">
<button type="button" class="layui-btn layui-btn-sm" lay-demo="openAll">展开所有菜单</button>
<button type="button" class="layui-btn layui-btn-sm" lay-demo="closeAll">收起所有菜单</button>
</div>
<!-- 只需要配置id -->
<div id="test9" class="demo-tree demo-tree-box" style="width: 500px; height: 600px; overflow: scroll;"></div>
</div>
<script th:inline="none">
layui.config({
base: '/layuiAdmin/src/layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['tree'], function() {
var $ = layui.$
,tree = layui.tree;
tree.render({
elem: '#test9'
,data: getData()//获取所有json的方法
,id: 'menuTree'//用于重载时指定
,edit: ['add', 'update', 'del'] //操作节点的图标
,operate: function (obj) {//操作事件
var type = obj.type; //得到操作类型:add、edit、del
var data = obj.data; //得到当前节点的数据
var elem = obj.elem; //得到当前节点元素
var objId = data.id; //当前节点id
if (type === 'add') {
$.ajax({
url: '/admin/menu/add',
data: data,
type: 'post',
dataType: 'json',
success: function (res) {
layer.msg(res.msg);
//layui中添加的是未命名的dom节点,没有id所以不能编辑改名,所以需要重载,参考链接中有直接弹窗添加的方法
tree.reload('menuTree',{ //tree组件的ID,渲染时需要设置
//展开操作节点的所有父类节点
data: openTree(getData(),objId)
});
}
});
} else if (type === 'update') {
$.ajax({
url: "/admin/menu/edit",
data: data,
type: 'post',
dataType: 'json',
success: function (res) {
layer.msg(res.msg);
}
})
} else {
$.ajax({
url: "/admin/menu/del",
data: data,
type: 'post',
dataType: 'json',
success: function (res) {
layer.msg(res.msg);
}
})
}
}
});
//点击按钮展开所有菜单,循环所有节点和子节点改spread属性
$("button[lay-demo='openAll']").click(function () {
var treeData = getData();
function each(data){
data.forEach(function (value) {
value.spread = true;
each(value.children)
})
}
each(treeData);
tree.reload('menuTree',{
data: treeData
})
});
//点击按钮收起所有菜单
$("button[lay-demo='closeAll']").click(function () {
var treeData = getData();
treeData.forEach(function (value) {
value.spread = false;
});
tree.reload('menuTree',{
data: treeData
})
});
//获取所有树状json数据
function getData() {
var data = [];
$.ajax({
url: "/admin/menu/list",
method: "get",
async:false, //同步执行,否则不会返回数据
success: function (res) {
data = res.data;
console.log(data);
}
});
return data;
}
//使操作节点的所有上级节点增加layui展开属性,treeData:所有树节点,objId:操作节点的ID
var openTree = function (treeData, objId) {
var nodeId = familyTree(treeData, objId);//获得操作节点的所有的父类ID数组
//循环所有节点,在nodeId中的加上展开属性
function each(data) {
data.forEach(function (e) {
if (nodeId.indexOf(e.id) !== -1) {
e.spread = true;
}
if (e.children.length > 0) {
each(e.children);
}
})
}
//执行循环
each(treeData);
return treeData;
};
//获取操作节点的所有父节点
var familyTree = function (treeData, objId) {
var temp = [];//父节点数组
var forFn = function (arr,id) {
//传入所有数组后循环,id=操作节点id的放进数组,并在他的父类子类中继续循环
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (item.id === id) {
temp.push(item.id);
forFn(treeData, item.pId);
break;
} else {
if (item.children) {
forFn(item.children, id);
}
}
}
};
//执行循环
forFn(treeData, objId);
return temp;
}
});
</script>
</body>
效果