一,什么是树形菜单?
一种以树状结构表示的菜单形式。通过父子关系组织菜单项,使用户能够方便地浏览和选择菜单项。树形菜单通常包含一个根节点和多个子节点,每个节点都可以包含子节点,从而形成层级结构。通常使用列表或嵌套列表的形式来实现树形菜单,如HTML中的
<ul>
和<li>
标签。树形菜单的优点之一是能够清晰地展现菜单间的层次和关系,使用户在浏览和选择菜单项时更加直观
1.1 用于什么地方
1 网站导航:
树形菜单可用于构建网站的导航菜单,使用户能够快速浏览和访问网站的不同部分和功能。
2 文件系统浏览:
树形菜单可用于显示文件夹和文件的层级结构,方便用户浏览和管理文件系统中的内容。
3 组织架构展示:
树形菜单可用于展示组织机构的层级结构,包括公司部门、团队成员等,帮助用户了解和导航组织的架构。
4 内容分类:
树形菜单可用于分类和组织大量的内容,例如新闻分类、商品分类等,使用户能够方便地浏览和筛选感兴趣的内容。
5 选项菜单:
树形菜单可用于构建复杂的选项菜单,其中每个选项可能有多个子选项,帮助用户进行复杂的选择和操作。
6 配置管理:
树形菜单可用于管理复杂的配置选项,将配置项按照层级组织,方便用户浏览和修改配置设置
二,为什么要使用树形菜单?
1 层级结构清晰:
树形菜单将菜单项组织成层级结构,使用户能够清晰地了解菜单项之间的关系和层次
2 空间利用高效:
树形菜单通过折叠和展开子节点的方式,可以节约页面空间,菜单项较多时,树形结构可以有限地展示主要菜单项,并提供折叠功能以展开子菜单项,从而更有效地利用页面空间
3 快速导航:
树形菜单提供了直观的导航功能,用户可以通过点击父节点或选择子节点来迅速导航到目标页面或功能。
4 可扩展性和灵活性:
树形菜单可以灵活扩展,适应不同层级和数量的菜单项,通过添加或删除节点,可以轻松地调整菜单的层级结构和内容,提供更好的用户体验和管理效果
5 可视化展示:
树形菜单以树状结构的形式呈现,通过节点的缩进和连接线等视觉元素,使菜单的层级结构一目了然
总之,使用树形菜单能够提供清晰的层级结构、高效的空间利用、快速的导航功能、灵活的扩展性和可视化的展示效果,从而提升用户的导航体验和操作效率。
三,实现树形菜单
3.1 引入相对应的layui中的css和js(如下代码引入封装公共的),并需要了解layui该官网菜单模块介绍(需要的菜单在layui官网copy即可)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <%@include file="common/header.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"> <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> </body> </html>
2 编写dao方法,一个查询方法,一个平级数据转父子关系数据方法,想要获取二级菜单就需要编写该方法进行遍历用来比较子菜单父级id与一级菜单id比较,如下代码中的类TreeVo是封装的一个类进行判断一级菜单与二级菜单根据id是否有父子关系,因为子菜单有个父级去找一级菜单(如下下代码块中)
package com.zking.dao; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; import com.zking.entity.Permission; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.BaseDao; import com.zking.util.BuildTree; import com.zking.util.PageBean; import com.zking.util.TreeVo; public class PermissionDao extends BaseDao<Permission> { public List<Permission> list(Permission permission, PageBean pageBean) throws Exception { String sql = "select * from t_oa_permission"; return super.executeQuery(sql, Permission.class, pageBean); } // 将平级数据转换父子关系的数据 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<>();// 实例TreeVo 方便将平级数据转成父子关系的数据 vo.setId(p.getId() + "");// 转编号 vo.setText(p.getName());// 转名称 vo.setParentId(p.getPid() + "");// 父级id lst.add(vo); } return BuildTree.buildList(lst, "-1"); } //测试是否有数据 public static void main(String[] args) throws Exception { PermissionDao pd = new PermissionDao(); List<TreeVo<Permission>> menus = pd.menus(null, null); ObjectMapper om = new ObjectMapper(); System.out.println(om.writeValueAsString(menus)); } }
TreeVo封装实体类:
package com.zking.util; import java.util.ArrayList; import java.util.List; import java.util.Map; 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(); } }
3,编写后端servlet,方便将数据回显到前端
注意:该方法没有返回值,如果有返回值运行时会一直调用自己
package com.zking.web; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.zking.dao.PermissionDao; import com.zking.entity.Permission; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.ResponseUtil; import com.zking.util.TreeVo; public class PermissionAction extends ActionSupport implements ModelDriver<Permission> { 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);//回显menus数据 System.out.println(menus); } catch (Exception e) { e.printStackTrace(); } } @Override public Permission getModel() { return permission; } }
4,在前端jsp页面获取后端回显的数据,做相对应的拼接
<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(index,nodel){//遍历子标签 htmlStr += '<dd><a href="javascript:;">'+nodel.text+'</a></dd>' }) htmlStr +='</dl>';//拼接子标签 结束标签 } htmlStr +='</li>'; //拼接一级菜单 结束标签 }) $("#menu").html(htmlStr); element.render('menu');//渲染 } }) }); </script>
最后jsp运行结果:
好啦!今日分享到这里咯!觉得不错的记得三连o!😀