一.效果图
Layui就一般都为这种水墨风,哈哈,大概就是图片的左侧的样子
二.具体步骤
2.1 数据库
需要准备的数据:首先准备好需要展示在树形菜单中的数据。这些数据应该包含节点的id、父节点id、节点名称等信息,以便构建树形结构。
2.2 树形导航栏
写一个java文件,里面包含了所有关于树形导航栏的方法和属性,把关于导航栏的单独用一个类写出,这样会更加清晰明了
第一个类: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(); } }
第二个类:BuildTree:
这个类里面定义了两个循环,一个循环可视为外层循环,即可定义为将所有的数据循环一遍,接着第二个循环可视为内层循环,将第二个循环的父id和第一个循环的id进行判断,如果相等就说明 他们是父子关系,并且在这里还指定了默认最高节点也就是父节点,这个可根据自己的数据库里的来进行变化
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 || "0".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("0"); 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; } }
2.3 Dao方法
2.3.1 basedao
package com.yinzi.utils; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import com.yinzi.utils.DBAccess; import com.yinzi.utils.PageBean; public class BaseDao<T> { /** * 带分页的模糊查询 * @param c * @param sql * @param pagebean * @return * @throws Exception */ public List<T> executeQuery(Class c ,String sql,PageBean pagebean) throws Exception{ //创建集合保存数据 List<T> list = new ArrayList<>(); //获取连接 Connection conn =null; //执行SQL语句 PreparedStatement ps = null; //结果集对象 ResultSet rs =null; //判断是否需要分页 if(pagebean!=null && pagebean.isPagination()) {//如果需要分页,就拼接SQL语句 String Countsql=getCountsql(sql);//获取总数量 conn = DBAccess.getConnection(); ps = conn.prepareStatement(Countsql);//执行改变后的SQL语句 rs = ps.executeQuery(); if(rs.next()) { pagebean.setTotal(rs.getObject("n").toString()); } String pagesql=getPagesql(sql,pagebean); conn = DBAccess.getConnection(); ps = conn.prepareStatement(pagesql);//执行改变后的SQL语句 rs = ps.executeQuery(); }else {//否则不需要,就按照原sql语句执行 conn = DBAccess.getConnection(); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); } //循环遍历数据 while(rs.next()) { T t = (T) c.newInstance(); //拿到所有的属性 Field[] fields = c.getDeclaredFields(); for (Field field : fields) { //打开访问权限 field.setAccessible(true); field.set(t,rs.getObject(field.getName())); } list.add(t); } return list; } /** * 最终SQL语句 * @param sql * @param pagebean * @return */ private String getPagesql(String sql, PageBean pagebean) { return sql+" limit "+pagebean.getStartIndex()+","+pagebean.getRows(); } /** * 总记录数 * @param sql * @return */ private String getCountsql(String sql) { return "select count(1) as n from ("+sql+") t"; } /** * 增删改的方法 * @param sql sql语句 * @param t 实体类 * @param attr 实体类对应的属性 * @return * @throws Exception */ public int excuteUpdate(String sql ,T t,String[] attr) throws Exception { Connection conn=DBAccess.getConnection();//连接数据库 PreparedStatement ps=conn.prepareStatement(sql);//执行sql语句 //循环拿到属性 for (int i = 0; i < attr.length; i++) { //一切反射从获取类类开始 Field f = t.getClass().getDeclaredField(attr[i]); //打开访问权限 f.setAccessible(true); ps.setObject(i+1, f.get(t)); } return ps.executeUpdate(); } }
2.3.2 Dao类
这个类里面首先做了一个查询所有获取所有的数据,接着 写一个方法将这些平级的数据转换成父子关系的数据,还调用了BuildTree里面的方法
package com.yinzi.dao; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.yinzi.entity.Permission; import com.yinzi.utils.BaseDao; import com.yinzi.utils.PageBean; import com.zking.util.BuildTree; import com.zking.util.TreeVo; 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_easyui_permission"; return super.executeQuery(Permission.class, sql, pagebean); } //将这些平级数据转换成父子关系的数据 public List<TreeVo<Permission>> menus(Permission permission,PageBean pagebean) throws Exception{ //创建一个父子关系的集合 TreeVo List<TreeVo<Permission>> tvList = new ArrayList<TreeVo<Permission>>(); //获取平级数据 List<Permission> list = this.list(permission, pagebean); for (Permission per : list) { TreeVo<Permission> tv=new TreeVo<>(); //将per对象转成tv对象,因为tv对象才有children tv.setId(per.getId()+""); tv.setText(per.getName()); tv.setParentId(per.getPid()+""); tvList.add(tv); } return BuildTree.buildList(tvList, "0");//这个地方填写一级菜单的id } }
2.4 后台Servlet
package com.yinzi.Servlet; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.yinzi.dao.PermissionDao; import com.yinzi.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 pd=new PermissionDao(); public void menus(HttpServletRequest req, HttpServletResponse resp) throws Exception { //调用方法 List<TreeVo<Permission>> menus = pd.menus(null, null); //回显给前台 ResponseUtil.writeJson(resp, menus); } @Override public Permission getModel() { return permission; } }
2.5 前台代码
首先样式小编是复制的Layui的模式,然后在ajax里面进行了拼接循环,最后输出即可
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html > <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"></link> <script type="text/javascript" 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> //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,node){ //这个需要循环多次 htmlStr+='<dd><a href="javascript:;">'+node.text+'</a></dd>'; }) htmlStr+='</dl>'; } htmlStr+='</li>'; }) $("#menu").html(htmlStr); //渲染 element.render('menu'); } }); }); </script> </body> </html>