前言
树形菜单是一种常见的网页导航菜单形式,它使用树形结构来展示菜单项之间的层级关系。
树形结构是一种层次化的数据结构,它由一个或多个节点组成,每个节点可以有零个或多个子节点。在树形菜单中,每个菜单项都对应树中的一个节点,菜单项之间通过父子关系来表示层级关系。
树形菜单通常以一个根节点开始,根节点的子节点对应一级菜单项,每个一级菜单项又可以有自己的子节点,形成多级菜单结构。用户可以通过点击菜单项的展开/折叠按钮或者点击菜单项本身来展开或收起下级菜单项。
树形菜单通常用于展示复杂的导航结构,特别适用于有多层次菜单结构的场景,如文件目录浏览、组织架构展示等。它可以提供清晰的层级关系,让用户方便地浏览和选择所需的菜单项。
请跟着思维导图我一起来看一看吧。
1. 了解LayUI官网提供的菜单模版
1.1 jsp页面代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Layui</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <link rel="stylesheet" href="../../res/layui/dist/css/layui.css" media="all"> <!-- 注意:如果你直接复制所有代码到本地,上述css路径需要改成你本地的 --> </head> <body> <blockquote class="layui-elem-quote"> 以下为「基础菜单」的静态展示,更多操作可见:<a href="dropdown.html" class="layui-btn layui-btn-primary layui-border-blue">下拉菜单组件</a> </blockquote> <div class="layui-btn-container"> <button type="button" class="layui-btn" lay-active="lg">大尺寸</button> <button type="button" class="layui-btn" lay-active="normal">常规尺寸</button> </div> <div class="layui-row layui-col-space30" id="demo-box" style="margin: 0 auto; max-width: 970px;"> <div class="layui-col-xs9 layui-col-md3"> <div class="layui-panel"> <ul class="layui-menu" id="demo1"> <li lay-options="{id: 100}"> <div class="layui-menu-body-title">menu item 1</div> </li> <li lay-options="{id: 101}"> <div class="layui-menu-body-title"> <a href="">menu item 2 <span class="layui-badge-dot"></span></a> </div> </li> <li class="layui-menu-item-divider"></li> <li class="layui-menu-item-group layui-menu-item-down" lay-options="{type: 'group'}"> <div class="layui-menu-body-title"> menu group <i class="layui-icon layui-icon-up"></i> </div> <ul> <li lay-options="{id: 103}"> <div class="layui-menu-body-title">menu item 3-1</div> </li> <li class="layui-menu-item-group" lay-options="{type: 'group', isAllowSpread: false}"> <div class="layui-menu-body-title">menu group 2</div> <ul> <li class="layui-menu-item-checked"> <div class="layui-menu-body-title">menu item 3-2-1</div> </li> <li><div class="layui-menu-body-title">menu item 3-2-2</div></li> </ul> </li> <li><div class="layui-menu-body-title">menu item 3-3</div></li> </ul> </li> <li class="layui-menu-item-divider"></li> <li><div class="layui-menu-body-title">menu item 4 <span class="layui-badge">1</span></div></li> <li><div class="layui-menu-body-title">menu item 5</div></li> <li><div class="layui-menu-body-title">menu item 6</div></li> <li class="layui-menu-item-parent" lay-options="{type: 'parent'}"> <div class="layui-menu-body-title"> menu item 7 Children <i class="layui-icon layui-icon-right"></i> </div> <div class="layui-panel layui-menu-body-panel"> <ul> <li class="layui-menu-item-parent" lay-options="{type: 'parent'}"> <div class="layui-menu-body-title"> menu item 7-1 <i class="layui-icon layui-icon-right"></i> </div> <div class="layui-panel layui-menu-body-panel"> <ul> <li><div class="layui-menu-body-title">menu item 7-2-1</div></li> <li><div class="layui-menu-body-title">menu item 7-2-2</div></li> <li><div class="layui-menu-body-title">menu item 7-2-3</div></li> <li><div class="layui-menu-body-title">menu item 7-2-4</div></li> </ul> </div> </li> <li><div class="layui-menu-body-title">menu item 7-2</div></li> <li><div class="layui-menu-body-title">menu item 7-3</div></li> </ul> </div> </li> <li>menu item 8</li> <li class="layui-menu-item-divider"></li> <li class="layui-menu-item-group" lay-options="{type: 'group'}"> <div class="layui-menu-body-title">menu group 9</div> <ul> <li><div class="layui-menu-body-title">menu item 9-1</div></li> <li class="layui-menu-item-parent" lay-options="{type: 'parent'}"> <div class="layui-menu-body-title"> menu item 9-2 <i class="layui-icon layui-icon-right"></i> </div> <div class="layui-panel layui-menu-body-panel"> <ul> <li><div class="layui-menu-body-title">menu item 9-2-1</div></li> <li><div class="layui-menu-body-title">menu item 9-2-2</div></li> <li><div class="layui-menu-body-title">menu item 9-2-3</div></li> </ul> </div> </li> <li><div class="layui-menu-body-title">menu item 9-31</div></li> </ul> </li> <li class="layui-menu-item-divider"></li> <li><div class="layui-menu-body-title">menu item 10</div></li> </ul> </div> </div> <div class="layui-col-xs9 layui-col-md3"> <div class="layui-panel"> <ul class="layui-menu" id="docDemoMenu1"> <li lay-options="{id: 100}"> <div class="layui-menu-body-title">menu item 1</div> </li> <li lay-options="{id: 101}"> <div class="layui-menu-body-title"> <a href="">menu item 2 <span class="layui-badge-dot"></span></a> </div> </li> <li class="layui-menu-item-divider"></li> <li class="layui-menu-item-group layui-menu-item-down" lay-options="{type: 'group', isAllowSpread: false}"> <div class="layui-menu-body-title"> menu group </div> <ul> <li lay-options="{id: 1031}"><div class="layui-menu-body-title">menu item 3-1</div></li> <li lay-options="{id: 1032}"> <div class="layui-menu-body-title">menu item 3-2</div> </li> </ul> </li> <li class="layui-menu-item-divider"></li> <li class="layui-menu-item-group layui-menu-item-down" lay-options="{type: 'group', isAllowSpread: false}"> <div class="layui-menu-body-title">menu group 2</div> <ul> <li lay-options="{id: 1031}"><div class="layui-menu-body-title">menu item 4-1</div></li> <li lay-options="{id: 1032}"> <div class="layui-menu-body-title">menu item 4-2</div> </li> </ul> </li> <li class="layui-menu-item-divider"></li> <li class="layui-menu-item-parent" lay-options="{type: 'parent'}"> <div class="layui-menu-body-title"> menu item 5 <i class="layui-icon layui-icon-right"></i> </div> <div class="layui-panel layui-menu-body-panel"> <ul> <li lay-options="{id: 1051}"> <div class="layui-menu-body-title">menu item 5-1</div> </li> <li lay-options="{id: 1051}"> <div class="layui-menu-body-title">menu item 5-2</div> </li> </ul> </div> </li> <li lay-options="{id: 106}"> <div class="layui-menu-body-title">menu item 6</div> </li> </ul> </div> </div> </div> <script src="//res/layui/dist/layui.js" charset="utf-8"></script> <!-- 注意:如果你直接复制所有代码到本地,上述 JS 路径需要改成你本地的 --> <script> layui.use(['dropdown', 'util'], function(){ var dropdown = layui.dropdown ,util = layui.util ,$ = layui.jquery; //菜单点击事件 dropdown.on('click(demo1)', function(options){ var thisElem = $(this); console.log(thisElem, options); }); util.event('lay-active', { normal: function(){ $('#demo-box').children().addClass('layui-col-md3').removeClass('layui-col-md4'); $('#demo-box').find('.layui-menu').removeClass('layui-menu-lg'); } ,lg: function(){ $('#demo-box').children().addClass('layui-col-md4').removeClass('layui-col-md3') $('#demo-box').find('.layui-menu').addClass('layui-menu-lg'); } }); }); </script> </body> </html>
页面展示效
2.用LayUI实现树形菜单
2.1 引入LayUI的依赖和样式文件。
2.2 编写树形菜单对象的实体类
Permission类
package com.YX.entity; /**树形菜单对应的实体类 * @author 君易--鑨 * 2023年7月11日下午7:35:25 * */ public class Permission { //定义属性 private long id; private String name; private String description; private String url; private long pid; private int ismenu;//控制当前系统的权限是菜单还是按钮 private long displayno;//菜单排序字段 /** * 无参构造 */ public Permission() { // TODO Auto-generated constructor stub } 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; } @Override public String toString() { return "Permission [id=" + id + ", name=" + name + ", description=" + description + ", url=" + url + ", pid=" + pid + ", ismenu=" + ismenu + ", displayno=" + displayno + "]"; } /** * 有参构造 * @param id * @param name * @param description * @param url * @param pid * @param ismenu * @param displayno */ public Permission(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; } }
2.3 编写Dao方法类
PermissionDao类
package com.YX.dao; import java.util.ArrayList; import java.util.List; import com.YX.entity.Permission; import com.fasterxml.jackson.databind.ObjectMapper; import com.zking.util.BaseDao; import com.zking.util.BuildTree; import com.zking.util.PageBean; import com.zking.util.TreeVo; /** * Dao方法类 * * @author 君易--鑨 2023年7月11日下午8:01:00 * */ public class PermissionDao extends BaseDao<Permission> { /** * 获取树形菜单选项 * * @param permission * @param pageBean * @return * @throws Exception */ public List<Permission> list(Permission permission, PageBean pageBean) throws Exception { String sql = "select * from t_oa_permission"; return super.executeQuery(sql, Permission.class, pageBean); } // 将数据库中查询出的平级数据,转换成有父子关系的数据 /** * 有父子关系结果的方法 * * @param permission * @param pageBean * @return * @throws Exception */ public List<TreeVo<Permission>> menus(Permission permission, PageBean pageBean) throws Exception { // 实例化一个有父子关系的集合 List<TreeVo<Permission>> lst = new ArrayList<TreeVo<Permission>>(); // 重新调用方法获取选项集合 List<Permission> list = this.list(permission, pageBean); // 遍历 for (Permission p : list) { // 实例化树形菜单工具类 TreeVo<Permission> vo = new TreeVo<>(); // 设置属性 vo.setId(p.getId() + ""); vo.setText(p.getName()); vo.setParentId(p.getPid() + ""); lst.add(vo); } return BuildTree.buildList(lst, "-1"); } // 测试 public static void main(String[] args) { PermissionDao pd = new PermissionDao(); try { List<TreeVo<Permission>> list = pd.menus(null, null); ObjectMapper om = new ObjectMapper(); System.out.println(om.writeValueAsString(list)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
调用list()方法后将结果进行json解析工具解析结果如下
调用menus()方法后将结果进行json解析工具解析结果如下
2.4 树形菜单工具类
package com.zking.util; import java.util.ArrayList; import java.util.List; import java.util.Map; /**树形菜单工具类 * @author 君易--鑨 * 2023年7月11日下午8:41:05 * @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(); } }
2.5 PermissionAction类
package com.YX.web; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.YX.dao.PermissionDao; import com.YX.entity.Permission; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.ResponseUtil; import com.zking.util.TreeVo; /**树形菜单子控制器类 * @author 君易--鑨 * 2023年7月11日下午8:44:42 * */ public class PermissionAction extends ActionSupport implements ModelDriver<Permission> { //实例化实体对象和dao方法 private Permission permission=new Permission(); private PermissionDao permissionDao=new PermissionDao(); public void menus(HttpServletRequest req, HttpServletResponse resp) { // 调用含有父子关系的方法 try { List<TreeVo<Permission>> menus = permissionDao.menus(null, null); // 回显客户端 ResponseUtil.writeJson(resp, menus); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public Permission getModel() { // TODO Auto-generated method stub return permission; } }
2.6 配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?> <config> <action path="/blog" type="com.zking.web.BlogAction"> <forward name="list" path="/blogList.jsp" redirect="false" /> <forward name="toList" path="/blog.action?methodName=list" redirect="true" /> <forward name="toEdit" path="/blogEdit.jsp" redirect="false" /> </action> <action path="/user" type="com.YX.web.UserAction"> </action> <action path="/permission" type="com.YX.web.PermissionAction"> </action> </config>
2.7 jsp页面代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="common/header.jsp"%> <!DOCTYPE html> <html> <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> <li class="layui-nav-item"> <a href="javascript:;">menu group 2</a> <dl class="layui-nav-child"> <dd><a href="javascript:;">list 1</a></dd> <dd><a href="javascript:;">list 2</a></dd> <dd><a href="">超链接</a></dd> </dl> </li> <li class="layui-nav-item"><a href="javascript:;">click menu item</a></li> <li class="layui-nav-item"><a href="">the links</a></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 }/permission.action?methodName=menus", dataType:'json', success:function(data){ console.log(data); //定义变量拼接 var htmlstr = ''; //遍历 $.each(data,function(i,n){ //拼接 htmlstr += '<li class="layui-nav-item layui-nav-itemed">'; htmlstr += '<a class="" href="javascript:;">'+n.text+' </a>' ; //判断是否有子选择菜单 if (n.hasChildren) { //获取子选项 var children=n.children; htmlstr += '<dl class="layui-nav-child">' ; //遍历子选项 $.each(children,function(idx,node){ htmlstr += '<dd><a href="javascript:;">'+node.text+'</a></dd>' }); htmlstr += '</dl>' ; } htmlstr += '</li>' }); //设置树形菜单的代码 $("#menu").html(htmlstr); element.render('menu');//渲染树形菜单 } }) }); </script> </body> </html>
页面展示效果
2.8 注意事项
- 记得打上渲染树形菜单那行代码,否则会出现下面这种现象。
- 创建编写了PermissionAction记得配置web.xml文件,否则无法运行项目
- PermissionAction类编写注意点
结束语
本期博客知识点分享就到这里了,小伙伴可关注我,后期会分享更多知识点与大家一起进步,诸君山顶见!
感谢各位大佬的肯定