1.前期准备
1.1数据库准备
1.2LayUI源码准备
来到LayUI官网找到类似于树形菜单的源码:
这是我们后续需要用到的copy源码。
2.原始数据的获取
首先我们需要新建一个jsp界面,然后根据实际情况建一个实体类Permission:
package com.zking.entity; 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 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; } public Permission() { super(); } @Override public String toString() { return "Permission [id=" + id + ", name=" + name + ", description=" + description + ", url=" + url + ", pid=" + pid + ", ismenu=" + ismenu + ", displayno=" + displayno + "]"; } }
接下来就是我们的Dao方法了:
package com.zking.dao; import java.util.List; import com.zking.entity.Permission; import com.zking.util.BaseDao; import com.zking.util.PageBean; 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); } }
还有PermissionDaoTest:
package com.zking.dao; import java.util.List; import org.junit.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.zking.entity.Permission; public class PermissionDaoTest { private PermissionDao permissionDao = new PermissionDao(); @Test public void testList() throws Exception { List<Permission> list = permissionDao.list(null, null); ObjectMapper om = new ObjectMapper(); System.out.println(om.writeValueAsString(list)); } }
以上操作完成后我们先看一下我们拿到的数据是什么样的:
用JUnit查看,效果如下:
但是我们可以清楚的发现,我们拿到的数据都是平级关系,并没有浏览器页面一二级菜单的父子关系,所以我们也需要把数据的评级关系给它调整成跟树形菜单的父子关系,才能够正确绑定数据不出错误。
接下来我们需要做的就是把拿到的数据从彼此的平级关系变成父子关系。
3.LayUI父子数据的转换
转换数据我们需要用到一个类,TreeVo类(专门用于展示一个树形结构的类):
TreeVo:
package com.zking.util; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 专门用于展示一个树形结构的类 * @author jj * * @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(); } }
在完成TreeVo类后,我们之前写的Permission类要转成TreeVo类,才能拿到我们想要的父子数据。即:我们如果想将平级数据转换成父子数据,就需要借助TreeVo类来完成。
将数据库查询出来的平级数据转换成父子数据,在Permission类添加的代码如下:
//将数据库查询出来的平级数据转换成父子数据 //把原本的平级关系对应的对象转成了能够构成父子TreeVo对象 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 lst; }
做完这一步我们测试一手:
大家这样看可能看不出来,我们把它copy到json在线格式化效验上去看一下:
在JUnit测试之后我们发现此时的数据还是平级关系,原因是我们先前只不过是换了个对象而已,所以数据还是平级关系。所以我们还需要做外层循环遍历跟内层循环遍历。如下:
新建一个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; } }
然后在原本的PermissionDao类中把原本的返回lst改成buildList(list,"-1")即可。
即:
return lst;——>return BuildTree.buildList(lst, "-1");
接下来我们再测试一手,看看数据是不是父子关系了:
到这里我们的数据转换就成功了。接下来就是LayUI其他功能的实现了。
4.LayUI功能的实现
PermissionAction类:
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 permisson = 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) { e.printStackTrace(); } } @Override public Permission getModel() { return permisson; } }
mvc.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.zking.web.UserAction"> </action> <action path="/permissionAction" type="com.zking.web.PermissionAction"> </action> <!-- <action path="/meetingInfo" type="com.zking.web.MeetingInfoAction"> --> <!-- <forward name="list" path="/meetingInfoList.jsp" redirect="false" /> --> <!-- <forward name="toList" path="/meetingInfo.action?methodName=list" --> <!-- redirect="true" /> --> <!-- <forward name="toEdit" path="/meetingInfoEdit.jsp" redirect="false" /> --> <!-- </action> --> <!-- <action path="/solrBlog" type="com.test.web.SolrBlogAction"> --> <!-- <forward name="list" path="/solrBlogList.jsp" redirect="false" /> --> <!-- <forward name="toList" path="/solrBlog.action?methodName=list" --> <!-- redirect="true" /> --> <!-- <forward name="toEdit" path="/solrBlogEdit.jsp" redirect="false" /> --> <!-- </action> --> <action path="/studentBlog" type="com.test.web.StudentBlogAction"> <forward name="list" path="/studentBlogList.jsp" redirect="false" /> <forward name="toList" path="/studentBlog.action?methodName=list" redirect="true" /> <forward name="toEdit" path="/studentBlogEdit.jsp" redirect="false" /> </action> <action path="/solrBlog" type="com.xiaoli.web.SolrBlogAction"> <forward name="list" path="/solrBlogList.jsp" redirect="false" /> <forward name="toList" path="/solrBlog.action?methodName=list" redirect="true" /> <forward name="toEdit" path="/solrBlogEdit.jsp" redirect="false" /> </action> </config>
main.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 = ''; htmlStr += '<dl class="layui-nav-child">'; htmlStr += '</dl>'; $.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; $.each(children,function(idx,node){ htmlStr += '<dd><a href="javascript:;">'+node.test+'</a></dd>'; }) } htmlStr += '</li>'; }) $("#menu").html(htmlStr); element.render("menu");//渲染 不加的话样式可能出不来 } }); }); </script> </body> </html>
最终效果图:
最后LayUI之动态树实现就到这里,祝大家在敲代码的路上一路通畅!