实现步骤:
1. 分析数据库
如图所示,聪明的你可能发现了!有的数据的属性pid有多个相同的值并且对应了id的值。由此发现它们是有某种关系的,我也不打哑谜了,数据库设计呢,就是把所有一级菜单(最大的菜单)的某一个值设置为一致,这样后来我们只要查询这个值就可以获取所有一级菜单。
这些都是父级内容
pid为10就是id为10下的子菜单!
pid:“所以我们现在是什么关系?”
id:“I'm your father🫰”
2. 构建数据源
2.1 编写实体类
package com.ycxw.entity; /** * 会议系统实体类 * * @author 云村小威 * * @2023年7月11日 下午9:41:18 */ 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; } 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; } @Override public String toString() { return "Permission [id=" + id + ", name=" + name + ", description=" + description + ", url=" + url + ", pid=" + pid + ", ismenu=" + ismenu + ", displayno=" + displayno + "]"; } }
2.2 编写节点实体类
package com.zking.util; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * * @author 云村小威 * * @2023年7月12日 上午11:13:05 */ 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.3 构建BuildTree节点结构方法类
package com.zking.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author 云村小威 * * @2023年7月12日 上午11:15:30 */ public class BuildTree { /** * 指定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) { //获取父级id String pid = children.getParentId(); //如果跟传过来的id相同 if (pid == null || idParam.equals(pid)) { //就判定这个父级菜单 topNodes.add(children); //继续执行下操作 continue; } for (TreeVo<T> parent : nodes) { //获取id String id = parent.getId(); //判断父级id是否等于pid if (id != null && id.equals(pid)) { //等于就判定它是谁的儿子 parent.getChildren().add(children); //设置是否有父节点和子节点 children.setHasParent(true); parent.setChildren(true); continue; } } } return topNodes; } }
2.4 编写dao类
后台方法构建好后,只需将数据源进行Json解析后,进行后台获取输出到页面即可:
package com.ycxw.dao; import java.util.ArrayList; import java.util.List; import com.ycxw.entity.Permission; import com.zking.util.BaseDao; import com.zking.util.BuildTree; import com.zking.util.PageBean; import com.zking.util.TreeVo; /** * @author 云村小威 * * @2023年7月12日 上午11:11:54 */ public class PermissionDao extends BaseDao<Permission>{ /** * 获取数据库数据 * @param p * @param pageBean * @return * @throws Exception */ public List<Permission> list(Permission p, PageBean pageBean) throws Exception { String sql = "select * from t_oa_permission where 1=1"; 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>> list = new ArrayList<TreeVo<Permission>>(); //获取的数据库集合 List<Permission> per = this.list(permission, pageBean); for (Permission p : per) { TreeVo<Permission> vo = new TreeVo<>(); //设置节点类内容并添加到节点集合 vo.setId(p.getId()+"");//设置节点id vo.setText(p.getName()); //设置节点文本 vo.setParentId(p.getPid()+"");//设置父节点 list.add(vo); } //返回节点构建好的数据 return BuildTree.buildList(list, "-1"); } }
2.5 编写数据Acntion控制类
对传入节点进行Json解析,并输出到页面
package demo.web; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ycxw.dao.PermissionDao; import com.ycxw.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月12日 上午11:31:20 */ public class PermissionAction extends ActionSupport implements ModelDriver<Permission> { private Permission permission = new Permission(); private PermissionDao dao = new PermissionDao(); public void menus(HttpServletRequest req, HttpServletResponse resp) { try { List<TreeVo<Permission>> menus = dao.menus(null, null); //调用封装好的解析json类 ResponseUtil.writeJson(resp, menus); } catch (Exception e) { e.printStackTrace(); } } @Override public Permission getModel() { return permission; } }
将数据解析成json方法
public static void writeJson(HttpServletResponse response, Object o) throws Exception { ObjectMapper om = new ObjectMapper(); // om.writeValueAsString(o)代表了json串 write(response, om.writeValueAsString(o)); }
3. 前台准备
3.1 配置mvc.xml文件
<action path="/permission" type="demo.web.PermissionAction"></action>
3.2 页面编写
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>主界面</title> <link rel="stylesheet" href="${pageContext.request.contextPath}/static/js/layui/css/layui.css"> <script src="${pageContext.request.contextPath}/static/js/layui/layui.js"></script> </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> layui.use(['element', 'layer', 'util'], function(){ var element = layui.element ,layer = layui.layer ,util = layui.util ,$ = layui.$; $.post("${pageContext.request.contextPath}/permission.action?methodName=menus", function(data){ //将json串转换成数组对象 var per = $.parseJSON(data); //拼接左侧导航栏 var htmlStr = ""; $.each(per,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(i,n){ htmlStr+="<dd><a href=\"javascript:;\">"+n.text+"</a></dd>"; }) htmlStr+="</dl>"; } htmlStr+="</li>"; }) //替换原来标签内容 $("#menu").html(htmlStr); //因为要修改之前元素的内容,防止数据出不来需要进行渲染结构传入目标容器的选择器和数据。 element.render("menu"); }) }); </script> </body> </html>
注意:
防止数据出不来需要进行渲染结构传入目标容器的选择器和数据。
element.render("menu");
3.3 运行效果