前言
在上篇博客中,我们学习了layui的入门,还实现了后台的登陆注册,今天我们就来完成后台首页的重要组成部分——左侧导航树形菜单
一、左侧导航
1.1 概述
- 导航一般指页面引导性频道集合,多以菜单的形式呈现,可应用于头部和侧边,是整个网页画龙点晴般的存在。 面包屑结构简单,支持自定义分隔符。
1.2 树形菜单
概述
- 树形菜单是一种常见的界面组件,用于在网页或应用程序中呈现层级结构的菜单。它通常以树的形式展示,其中每个节点表示一个菜单项,节点之间的嵌套关系表示菜单项的层级关系。
- 树形菜单适用于具有多级层次结构的菜单导航,例如文件浏览器、系统设置、产品分类等。它的主要优点是能够清晰地显示和组织大量的菜单选项,并且用户可以通过展开和折叠节点来快速浏览和选择适当的菜单项。
一般而言,树形菜单的基本特点包括:
- 节点可以展开和折叠,以显示或隐藏其子菜单项。
- 子菜单项嵌套在父菜单项下面,形成层级结构。
- 点击菜单项可以执行相应的操作或导航到指定的页面。
- 提供了一种直观的方式来浏览和导航具有层级关系的菜单选项。
注:千万不要忘了加载 element模块。虽然大部分行为都是在加载完该模块后自动完成的,但一些交互操作,
如呼出二级菜单等,需借助element模块才能使用。
参考地址:layui导航
二、导入数据表及无限级分类
2.1 导入数据
DROP TABLE IF EXISTS `t_easyui_permission`; CREATE TABLE `t_easyui_permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '权限id', `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限名字', `description` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限描述', `url` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单路径', `pid` bigint(20) NULL DEFAULT NULL COMMENT '父权限', `ismenu` int(11) NULL DEFAULT 1 COMMENT '是否为菜单 1、菜单 2、按钮', `displayno` bigint(20) NULL DEFAULT 0 COMMENT '展现顺序', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_easyui_permission -- ---------------------------- INSERT INTO `t_easyui_permission` VALUES (1, '书籍管理', '书籍管理', NULL, 0, 1, 0); INSERT INTO `t_easyui_permission` VALUES (2, '新增(crud)', '书籍新增', '/bg/addBook.jsp', 1, 1, 0); INSERT INTO `t_easyui_permission` VALUES (3, '未上架', '未上架书籍', '/bg/listBook1.jsp', 1, 1, 0); INSERT INTO `t_easyui_permission` VALUES (4, '已上架', '已上架书籍', '/bg/listBook2.jsp', 1, 1, 0); INSERT INTO `t_easyui_permission` VALUES (5, '已下架', '已下架书籍', '/bg/listBook3.jsp', 1, 1, 0); INSERT INTO `t_easyui_permission` VALUES (6, '订单管理', '商家的订单管理模块', NULL, 0, 1, 0); INSERT INTO `t_easyui_permission` VALUES (7, '未发货', '未发货订单', '/bg/listOrder1.jsp', 6, 1, 0); INSERT INTO `t_easyui_permission` VALUES (8, '已发货', '已发货订单', '/bg/listOrder2.jsp', 6, 1, 0); INSERT INTO `t_easyui_permission` VALUES (9, '已签收', '已签收订单', '/bg/listOrder3.jsp', 6, 1, 0); INSERT INTO `t_easyui_permission` VALUES (10, '订单管理', '会员的订单管理模块', '', 0, 1, 0); INSERT INTO `t_easyui_permission` VALUES (11, '未发货', '未发货订单', '/bg/listMyOrder1.jsp', 10, 1, 0); INSERT INTO `t_easyui_permission` VALUES (12, '已发货', '已发货订单', '/bg/listMyOrder2.jsp', 10, 1, 0); INSERT INTO `t_easyui_permission` VALUES (13, '已签收', '已签收订单', '/bg/listMyOrder3.jsp', 10, 1, 0); INSERT INTO `t_easyui_permission` VALUES (14, '订单项', '订单明细', '/bg/listOrderItem.jsp', 6, 1, 0); INSERT INTO `t_easyui_permission` VALUES (15, '权限管理', '顶级权限节点', NULL, -1, 1, 0);
2.2 无限级分类
父亲找儿子的过程,将对应的儿子放在父亲下面,形成树结构。(递归算法)
假设你有以下的无限级分类数据源:
var data = [ { id: 1, name: '父亲1', parentId: null }, { id: 2, name: '父亲2', parentId: null }, { id: 3, name: '儿子1', parentId: 1 }, { id: 4, name: '儿子2', parentId: 1 }, { id: 5, name: '孙子1', parentId: 3 }, { id: 6, name: '孙子2', parentId: 3 }, { id: 7, name: '儿子3', parentId: 2 } ];
使用以下递归函数来构建树结构:
function buildTree(data, parentId) { var children = []; for (var i = 0; i < data.length; i++) { if (data[i].parentId === parentId) { var child = { id: data[i].id, name: data[i].name, children: buildTree(data, data[i].id) }; children.push(child); } } return children; }
调用函数以构建树结构:
var tree = buildTree(data, null); console.log(tree);
三、Book实例
3.1 环境准备
各种jar包、配置文件的导入在这里我就不介绍了,想看的请到上篇博客layui入门+登入注册实例
3.3 导入封装的工具类
TreeVo
package com.zking.util; import java.util.ArrayList; import java.util.List; import java.util.Map; /**树形结构的节点对象 * @author W许潜行 * @param <T> */ public class TreeVo<T> { /** * 节点ID */ private String id; /** * 显示节点文本 */ private String text; /** * 节点状态,open closed */ private Map<String, Object> state; /** * 节点是否被选中 true false */ private boolean checked = false; /** * 节点属性 */ private Map<String, Object> attributes; /** * 节点的子节点 */ private List<TreeVo<T>> children = new ArrayList<TreeVo<T>>(); /** * 父ID */ private String parentId; /** * 是否有父节点 */ private boolean hasParent = false; /** * 是否有子节点 */ private boolean hasChildren = false; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Map<String, Object> getState() { return state; } public void setState(Map<String, Object> state) { this.state = state; } public boolean isChecked() { return checked; } public void setChecked(boolean checked) { this.checked = checked; } public Map<String, Object> getAttributes() { return attributes; } public void setAttributes(Map<String, Object> attributes) { this.attributes = attributes; } public List<TreeVo<T>> getChildren() { return children; } public void setChildren(List<TreeVo<T>> children) { this.children = children; } public boolean isHasParent() { return hasParent; } public void setHasParent(boolean isParent) { this.hasParent = isParent; } public boolean isHasChildren() { return hasChildren; } public void setChildren(boolean isChildren) { this.hasChildren = isChildren; } public String getParentId() { return parentId; } public void setParentId(String parentId) { this.parentId = parentId; } public TreeVo(String id, String text, Map<String, Object> state, boolean checked, Map<String, Object> attributes, List<TreeVo<T>> children, boolean isParent, boolean isChildren, String parentID) { super(); this.id = id; this.text = text; this.state = state; this.checked = checked; this.attributes = attributes; this.children = children; this.hasParent = isParent; this.hasChildren = isChildren; this.parentId = parentID; } public TreeVo() { super(); } }
BuildTree
package com.zking.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class BuildTree { /** * 默认-1为顶级节点 * @param nodes * @param <T> * @return */ public static <T> TreeVo<T> build(List<TreeVo<T>> nodes) { if (nodes == null) { return null; } List<TreeVo<T>> topNodes = new ArrayList<TreeVo<T>>(); for (TreeVo<T> children : nodes) { String pid = children.getParentId(); if (pid == null || "-1".equals(pid)) { topNodes.add(children); continue; } for (TreeVo<T> parent : nodes) { String id = parent.getId(); if (id != null && id.equals(pid)) { parent.getChildren().add(children); children.setHasParent(true); parent.setChildren(true); continue; } } } TreeVo<T> root = new TreeVo<T>(); if (topNodes.size() == 1) { root = topNodes.get(0); } else { root.setId("000"); root.setParentId("-1"); root.setHasParent(false); root.setChildren(true); root.setChecked(true); root.setChildren(topNodes); root.setText("顶级节点"); Map<String, Object> state = new HashMap<>(16); state.put("opened", true); root.setState(state); } return root; } /** * 指定idparam为顶级节点 * @param nodes * @param idParam * @param <T> * @return */ public static <T> List<TreeVo<T>> buildList(List<TreeVo<T>> nodes, String idParam) { if (nodes == null) { return null; } List<TreeVo<T>> topNodes = new ArrayList<TreeVo<T>>(); for (TreeVo<T> children : nodes) { String pid = children.getParentId(); if (pid == null || idParam.equals(pid)) { topNodes.add(children); continue; } for (TreeVo<T> parent : nodes) { String id = parent.getId(); if (id != null && id.equals(pid)) { parent.getChildren().add(children); children.setHasParent(true); parent.setChildren(true); continue; } } } return topNodes; } }
3.3 实体类及dao方法的编写
entity
package com.xqx.entity; /** * @author W许潜行 * 2023年7月12日 上午9:43:11 */ public class Book { /** * 权限id * 权限名字 * 权限描述 * 菜单路径 * 父权限 * 是否为菜单 1、菜单 2、 * 按钮 * 展现顺序 */ private long id; private String name; private String description; private String url; private long pid; private int ismenu; private long displayno; public Book() { // TODO Auto-generated constructor stub } public Book(long id, String name, String description, String url, long pid, int ismenu, long displayno) { super(); this.id = id; this.name = name; this.description = description; this.url = url; this.pid = pid; this.ismenu = ismenu; this.displayno = displayno; } @Override public String toString() { return "Book [id=" + id + ", name=" + name + ", description=" + description + ", url=" + url + ", pid=" + pid + ", ismenu=" + ismenu + ", displayno=" + displayno + "]"; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public long getPid() { return pid; } public void setPid(long pid) { this.pid = pid; } public int getIsmenu() { return ismenu; } public void setIsmenu(int ismenu) { this.ismenu = ismenu; } public long getDisplayno() { return displayno; } public void setDisplayno(long displayno) { this.displayno = displayno; } }
Dao
package com.xqx.dao; import java.util.ArrayList; import java.util.List; import com.xqx.entity.Book; import com.zking.util.BaseDao; import com.zking.util.BuildTree; import com.zking.util.PageBean; import com.zking.util.TreeVo; public class BookDao extends BaseDao<Book> { /**拿到所有数据 * @param clz * @param pageBean * @return * @throws Exception */ public List<Book> list(Class<Book> clz, PageBean pageBean) throws Exception { String sql ="select *from t_easyui_permission"; return super.executeQuery(sql, clz, pageBean); } /**将list()查出的数据——平级数据(一级)转成父子关系数据——二级菜单 * @param clz * @param pageBean * @return * @throws Exception */ public List<TreeVo<Book>> menus(Class<Book> clz, PageBean pageBean) throws Exception { List<TreeVo<Book>> list=new ArrayList<TreeVo<Book>>(); List<Book> blist = this.list(clz, pageBean); Book b=new Book(); for (Book book : blist) { TreeVo<Book> vo=new TreeVo<>(); vo.setId(book.getId()+""); vo.setText(book.getName()); vo.setParentId(book.getPid()+""); list.add(vo); } return BuildTree.buildList(list,"0"); }
3.4 编写Servlet
package com.xqx.web; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xqx.dao.BookDao; import com.xqx.entity.Book; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.ResponseUtil; import com.zking.util.TreeVo; public class BookAction extends ActionSupport implements ModelDriver<Book> { Book book=new Book(); BookDao bd=new BookDao(); public void menus(HttpServletRequest req, HttpServletResponse resp) { try { List<TreeVo<Book>> menus = bd.menus(Book.class, null); ResponseUtil.writeJson(resp, menus); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public Book getModel() { // TODO Auto-generated method stub return book; } }
3.5 编写Jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <%@ include file="common/head.jsp"%> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>首页</title> </head> <body> <div class="layui-layout layui-layout-admin"> <div class="layui-header"> <div class="layui-logo layui-hide-xs layui-bg-black">layout demo</div> <!-- 头部区域(可配合layui 已有的水平导航) --> <ul class="layui-nav layui-layout-left"> <!-- 移动端显示 --> <li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-header-event="menuLeft"><i class="layui-icon layui-icon-spread-left"></i></li> <!-- Top导航栏 --> <li class="layui-nav-item layui-hide-xs"><a href="">nav 1</a></li> <li class="layui-nav-item layui-hide-xs"><a href="">nav 2</a></li> <li class="layui-nav-item layui-hide-xs"><a href="">nav 3</a></li> <li class="layui-nav-item"><a href="javascript:;">nav groups</a> <dl class="layui-nav-child"> <dd> <a href="">menu 11</a> </dd> <dd> <a href="">menu 22</a> </dd> <dd> <a href="">menu 33</a> </dd> </dl></li> </ul> <!-- 个人头像及账号操作 --> <ul class="layui-nav layui-layout-right"> <li class="layui-nav-item layui-hide layui-show-md-inline-block"> <a href="javascript:;"> <img src="//tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" class="layui-nav-img"> tester </a> <dl class="layui-nav-child"> <dd> <a href="">Your Profile</a> </dd> <dd> <a href="">Settings</a> </dd> <dd> <a href="login.jsp">Sign out</a> </dd> </dl> </li> <li class="layui-nav-item" lay-header-event="menuRight" lay-unselect> <a href="javascript:;"> <i class="layui-icon layui-icon-more-vertical"></i> </a> </li> </ul> </div> <div class="layui-side layui-bg-black"> <div class="layui-side-scroll"> <!-- 左侧导航区域(可配合layui已有的垂直导航) --> <ul id="menu" class="layui-nav layui-nav-tree" lay-filter="menu"> <!-- <li class="layui-nav-item layui-nav-itemed"> <a class="" href="javascript:;">menu group 1</a> <dl class="layui-nav-child"> <dd><a href="javascript:;">menu 1</a></dd> <dd><a href="javascript:;">menu 2</a></dd> <dd><a href="javascript:;">menu 3</a></dd> <dd><a href="">the links</a></dd> </dl> </li> --> </ul> </div> </div> <div class="layui-body"> <!-- 内容主体区域 --> <div style="padding: 15px;">内容主体区域。记得修改 layui.css 和 js 的路径——许潜行</div> </div> <div class="layui-footer"> <!-- 底部固定区域 --> 底部固定区域 </div> </div> <script> //JS layui.use(['element', 'layer', 'util'], function(){ var element = layui.element ,layer = layui.layer ,util = layui.util ,$ = layui.$; $.ajax({ url:"${pageContext.request.contextPath }/book.action?methodName=menus", dataType:'json', success:function(data){ var html=''; $.each(data,function(i,n){ html += '<li class="layui-nav-item layui-nav-itemed">'; html += '<a class="" href="javascript:;">'+n.text+'</a>'; if (n.hasChildren) { var children=n.children; console.log(children); html += ' <dl class="layui-nav-child">'; $.each(children,function(index,cn){ html += ' <dd><a href="javascript:;">'+cn.text+'</a></dd>'; }) html += ' </dl>'; } html += '</li>'; }); $("#menu").html(html); element.render('menu'); } }); //头部事件 util.event('lay-header-event', { //左侧菜单事件 menuLeft: function(othis){ layer.msg('展开左侧菜单的操作', {icon: 0}); } ,menuRight: function(){ layer.open({ type: 1 ,content: '<div style="padding: 15px;">处理右侧面板的操作</div>' ,area: ['260px', '100%'] ,offset: 'rt' //右上角 ,anim: 5 ,shadeClose: true }); } }); }); </script> </body> </html>
其实埋了一个小伏笔哦,这张表有爷子孙的关系也就是可以实现三级菜单,想知道的小伙伴可以私信留言或者等下篇博客!
好啦,本篇分享就到此为止!如果你看完本篇文章有所收获,请不要忘记点个赞,或者留下你的思考与问题,祝你变得更强!!!