这篇博客主要讲述了layUI树形菜单的各种形态以及配合MySQL数据库中的数据动态加载节点!
这上面是一个比较全面的树形菜单,下面是有关它的详述图:
第一步肯定是要导入layUI的包啦!
没有的小伙伴们火速下载熬: https://www.layui.com/
下载完是这样的:
啊有一个注意事项:我在观看layUI的示例时,我只在其中看到了两个导入的路径:
<link rel="stylesheet" href="static/layui/css/layui.css" media="all"> <script src="static/layui/layui.js" charset="utf-8"></script>但是,如果你在下载的文件里面只把layui.css和layui.js放入项目当中,如下图:
不仅效果出不来还会报错哦!这里可以看到报的是一个404错误
点进去会看到这样子一个,我大胆猜测使用layui中的tree组件是需要一个tree.js,但是这个文件在我导进去的两个文件中没有,那么为什么官方示例却又ok呢? 因为爱情?不不不,这边我找到两种解决方法:
第一,像官方示例中一样导入layui.js,但是导文件的时候将整个layui导进去。
第二,导入layui.all.js这个js文件,看到中间那个all就知道它比layui.js要高大上啦嘿嘿嘿,它会比layui.js更加全一点,我也不知道为啥要这样,我看到效果有就完了!
具体实现代码:
通过 tree.render() 方法指定一个元素,便可快速创建一个 tree 实例,我这边是模拟了一串数据,后面讲讲动态加载树形菜单熬:
<%@ 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 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="static/layui/css/layui.css" media="all"> </head> <body> <script src="static/layui/layui.js" charset="utf-8"></script> <div id="test1"></div> <script> layui.use('tree', function(){ var tree = layui.tree; //模拟数据 var data = [{ title: '一级1' ,id: 1 ,field: 'name1' ,checked: true ,spread: true ,children: [{ title: '二级1-1 可允许跳转' ,id: 3 ,field: 'name11' ,href: 'https://www.layui.com/' ,children: [{ title: '三级1-1-3' ,id: 23 ,field: '' ,children: [{ title: '四级1-1-3-1' ,id: 24 ,field: '' ,children: [{ title: '五级1-1-3-1-1' ,id: 30 ,field: '' },{ title: '五级1-1-3-1-2' ,id: 31 ,field: '' }] }] },{ title: '三级1-1-1' ,id: 7 ,field: '' ,children: [{ title: '四级1-1-1-1 可允许跳转' ,id: 15 ,field: '' ,href: '#' }] },{ title: '三级1-1-2' ,id: 8 ,field: '' ,children: [{ title: '四级1-1-2-1' ,id: 32 ,field: '' }] }] },{ title: '二级1-2' ,id: 4 ,spread: true ,children: [{ title: '三级1-2-1' ,id: 9 ,field: '' ,disabled: true },{ title: '三级1-2-2' ,id: 10 ,field: '' }] },{ title: '二级1-3' ,id: 20 ,field: '' ,children: [{ title: '三级1-3-1' ,id: 21 ,field: '' },{ title: '三级1-3-2' ,id: 22 ,field: '' }] }] },{ title: '一级2' ,id: 2 ,field: '' ,spread: true ,children: [{ title: '二级2-1' ,id: 5 ,field: '' ,spread: true ,children: [{ title: '三级2-1-1' ,id: 11 ,field: '' },{ title: '三级2-1-2' ,id: 12 ,field: '' }] },{ title: '二级2-2' ,id: 6 ,field: '' ,children: [{ title: '三级2-2-1' ,id: 13 ,field: '' },{ title: '三级2-2-2' ,id: 14 ,field: '' ,disabled: true }] }] },{ title: '一级3' ,id: 16 ,field: '' ,children: [{ title: '二级3-1' ,id: 17 ,field: '' ,fixed: true ,children: [{ title: '三级3-1-1' ,id: 18 ,field: '' },{ title: '三级3-1-2' ,id: 19 ,field: '' }] },{ title: '二级3-2' ,id: 27 ,field: '' ,children: [{ title: '三级3-2-1' ,id: 28 ,field: '' },{ title: '三级3-2-2' ,id: 29 ,field: '' }] }] }] //基本演示 var inst1 = tree.render({ elem: '#test1' //绑定元素id ,data: data //数据 ,showCheckbox: true //是否显示复选框 ,id: 'demoId1' //索引 ,isJump: true //是否允许点击节点时弹出新窗口跳转 ,click: function(obj){ //点击事件 var data = obj.data; //获取当前点击的节点数据 layer.msg('状态:'+ obj.state + '<br>节点数据:' + JSON.stringify(data)); } }); }); </script> </body> </html>tree组件的基础参数,根据需要自个调熬
首先我们得知道layui需要的数据格式是什么 还真别说,我在这里出了一个bug——tree结构动态展示出来,标题却都是未命名
主要是因为根据数据源设计节点实体类时,我为了偷懒我复制了之前做easyUI的tree组件,但是easyUI和layui的节点标题在源码中是不一样的!导致找不到标题属性就默认为‘未命名’了,可以看到我能把json弹出来:
但当我点击确认后:
解决方法也有两个:
1、更改源码,我刚开始居然试图弄这个,但是我承认我菜鸡我失败了,不过我找了其他人的,貌似是有成功的!
2、老老实实去节点实体类更改标题的名字,改成title~我就是使用这一种方法。
如下节点实体类:
package com.zengjing.vo; import java.util.ArrayList; import java.util.List; import java.util.Map; public class TreeVo<T> { /** * 节点ID */ private String id; /** * 显示节点文本 */ private String title; /** * 节点状态,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 getTitle() { return title; } public void setTitle(String text) { this.title = 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.title = 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创建节点结构方法类:
package com.zengjing.vo; 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("-1"); root.setHasParent(false); root.setChildren(true); root.setChecked(true); root.setChildren(topNodes); root.setTitle("顶级节点"); 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; } }节点实体类创建后,就在dao层创建一个将数据库的数据通过BuildTree解析为json格式返回,,
public List<TreeVo<Menu>> toNode(Menu menu,PageBean pageBean) throws Exception { List<Menu> list = this.list(menu, pageBean); List<TreeVo<Menu>> nodes = new ArrayList<TreeVo<Menu>>(); TreeVo treeVo = null; for (Menu p : list) { treeVo = new TreeVo<>(); treeVo.setId(p.getMenuid()); treeVo.setTitle(p.getMenuname()); treeVo.setParentId(p.getParentid()); Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put("self", p); treeVo.setAttributes(attributes); nodes.add(treeVo); } return BuildTree.buildList(nodes,"-1"); }再在action中输出为json串到前端:
public String menuTree(HttpServletRequest req,HttpServletResponse resp) { try { ResponseUtil.writeJson(resp, this.md.toNode(null, null)); } catch (Exception e) { e.printStackTrace(); } return null; }writeJson方法内容:
public static void writeJson(HttpServletResponse response,Object o)throws Exception{ response.setContentType("text/html;charset=utf-8"); ObjectMapper om = new ObjectMapper(); String jsonstr = om.writeValueAsString(o); PrintWriter out=response.getWriter(); out.println(jsonstr.toString()); out.flush(); out.close(); }后台获取json数据结束,就到了重点的前端,我是通过一个函数使用了ajax获取后台数据,再进行返回,在tree的数据源后直接调用方法即可,我的jsp界面:
<%@ 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 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" type="text/css" href="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/themes/default/easyui.css"> <!-- 定义图标的样式 --> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/themes/icon.css"> <!--组件库源文件的js文件 --> <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/jquery.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/jquery.easyui.min.js"></script> <link rel="stylesheet" href="static/layui/css/layui.css" media="all"> </head> <body> <script src="static/layui/layui.js" charset="utf-8"></script> <div id="test1"></div> <input type="hidden" id="ctx" value="${pageContext.request.contextPath }" /> <script> function data(){ var data=[]; jQuery.ajax({ url:$('#ctx').val()+'/menu.action?methodName=menuTree', type:"post", async:false, success:function(result){ data=result; } }); alert(data); return data; } layui.use('tree', function(){ var tree = layui.tree; //基本演示 tree.render({ elem: '#test1' ,data: eval('('+data()+')') }); }); </script> </body> </html>最后效果图:
前端果然是一个考验人耐心的东西,呜呜呜,写前端样式有问题一定记得清缓存!!好几次因为这个卡好久了,还有记得不同前端框架的文档肯定有一点差异,不要盲目c代码,我今天就直接血的教训,有问题欢迎指教哦摆摆!