LayUI—tree树形结构的使用 树形结构在实际开发中很长用到比如部门管理权限菜单等。因为用树形结构来展示会显的很清晰明了。最近写了一个个人博客小项目中用到了LayUI的树形结构之后写了一个小案例整理一下。官网地址https://www.layui.com/v1/doc/modules/tree.html先看一下显示的效果图。点击节点右面会显示对应部门的详情信息可以修改。可以自定义按钮添加部门也可以直接用自带的方法对部门进行新增修改和删除。可以获取选中的节点根据项目需求有的需要选中保存。先需要引入LayUI的样式文件JS和CSS。案例对应的实体类Dept。Entity public class Dept { private Integer id; private String name; //部门名称 private String deptName; //部门负责人 private String phone; //电话号 private String number; //编号 private double idx; //排序 JsonIgnore private Dept parent; JsonIgnore private ListDept children new ArrayList(); Id GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName deptName; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone phone; } public String getNumber() { return number; } public void setNumber(String number) { this.number number; } public double getIdx() { return idx; } public void setIdx(double idx) { this.idx idx; } ManyToOne CreatedBy public Dept getParent() { return parent; } public void setParent(Dept parent) { this.parent parent; } OneToMany(cascadeCascadeType.ALL,mappedByparent) OrderBy(valueidx) public ListDept getChildren() { return children; } public void setChildren(ListDept children) { this.children children; } public Dept(Integer id, String name, String deptName, String phone, String number, double idx, Dept parent, ListDept children) { this.id id; this.name name; this.deptName deptName; this.phone phone; this.number number; this.idx idx; this.parent parent; this.children children; } public Dept(Integer id) { this.id id; } public Dept() { } }显示LayUI树形菜单只需要一个标签容器即可。div iddept_tree /div在案例中还有一些其他样式比如右边的详情信息新增按钮等。完整代码如下。style typetext/css #dept_main, #dept_particulars{ width: 48.5%; display: inline-block; vertical-align: top; padding: 20px; background: white; box-sizing: border-box; } #dept_tree{ margin-top: 20px; } /style div iddept_main stylemargin-right: 2%; fieldset classlayui-elem-field layui-field-title legend所有部门/legend /fieldset button classlayui-btn layui-btn-sm layui-btn-radius layui-btn-normal lay-demoaddDepti classlayui-icon#xe654;/i添加部门/button button classlayui-btn layui-btn-sm layui-btn-radius layui-btn-normal lay-demogain获取选中节点/button div iddept_tree /div /div div iddept_particulars fieldset classlayui-elem-field layui-field-title legend部门详情/legend /fieldset div iddept_home div classlayui-tree-emptyText无数据/div /div /divJS请求数据渲染页面代码data为请求数据源当时直接放入的请求链接好像不行所以之后才写了一个方法去请求数据源。layui.use([tree, util], function() { var tree layui.tree; var util layui.util; tree.render({ elem: #dept_tree, data: getData(), id: treeId, showCheckbox: true, //是否显示复选框 onlyIconControl: true }); }); function getData(){ var data []; $.ajax({ url: dept/treeload, //后台数据请求地址 type: post, async:false, success: function(resut){ data resut; } }); return data; }tree 组件提供的有以下基础参数可根据需要进行相应的设置。参数选项说明类型示例值elem指向容器选择器String/Object-data数据源Array-id设定实例唯一索引用于基础方法传参使用。String-showCheckbox是否显示复选框Booleanfalseedit是否开启节点的操作图标。默认 false。若为 true则默认显示“改删”图标若为 数组则可自由配置操作图标的显示状态和顺序目前支持的操作图标有add、update、del如edit: [add, update, del]Boolean/Array[update, del]accordion是否开启手风琴模式默认 falseBooleanfalseonlyIconControl是否仅允许节点左侧图标控制展开收缩。默认 false即点击节点本身也可控制。若为 true则只能通过节点左侧图标来展开收缩BooleanfalseisJump是否允许点击节点时弹出新窗口跳转。默认 false若开启需在节点数据中设定 link 参数值为 url 格式BooleanfalseshowLine是否开启连接线。默认 true若设为 false则节点左侧出现三角图标。Booleantruetext自定义各类默认文本目前支持以下设定text: {defaultNodeName: 未命名 //节点默认名称,none: 无数据 //数据为空时的提示文本}Object-因为tree指定了json数据的键名称所以后台传递过来的数据对应的键名不一样时需要做一下处理或者实体类中的属性名就和tree的JSON数据的键名称一样。键名属性选项说明类型示例值title节点标题String未命名id节点唯一索引用于对指定节点进行各类操作String/Number任意唯一的字符或数字children子节点。支持设定选项同父节点Array[{title: 子节点1, id: 111}]href点击节点弹出新窗口对应的 url。需开启 isJump 参数String任意 URLspread节点是否初始展开默认 falseBooleantruechecked节点是否初始为选中状态如果开启复选框的话默认 falseBooleantruedisabled节点是否为禁用状态。默认 falseBooleanfalse后台请求数据的方法。RequestMapping(value /treeload) ResponseBody public Object treeload(){ Sort sort Sort.by(idx); ListDept dpet deptService.findByParentIsNull(sort); //查找所有菜单 ListHashMapString, Object result new ArrayList(); //定义一个map处理json键名问题 return fun(dpet, result); } private Object fun(ListDept dpet, ListHashMapString, Object result) { for(Dept d : dpet){ HashMapString, Object map new HashMap(); map.put(id, d.getId()); map.put(title, d.getName()); map.put(spread, true); //设置是否展开 ListHashMapString, Object result1 new ArrayList(); ListDept children d.getChildren(); //下级菜单 //这里可以根据自己需求判断节点默认选中 /*if(m.getParent() ! null || m.getChildren().size() 0){ map.put(checked, true); //设置为选中状态 }*/ map.put(children, fun(children, result1)); result.add(map); } return result; }因为这里新建的实体类字段名和tree指定了json数据的键名称不一样所以这里用了一个fun递归方法处理的。中间可以根据项目需求根据条件判断是否需要选中该节点。返回的JSON数据格式[ { children: [ //子节点 { children: [ { children: [], id: 30, title: 测试, spread: true }, { children: [], id: 31, title: 开发, spread: true }, { children: [ { children: [], id: 36, title: 测试节点, spread: true } ], id: 32, title: 测试, spread: true } ], id: 2, title: 技术部, spread: true }, { children: [], id: 19, title: 财务部, spread: true } ], id: 1, //节点id title: 某某公司, //节点名称 spread: true }, { children: [], id: 33, title: 测试, spread: true } ]设置节点点击回调方法在加载数据方法tree.render中添加以下代码。click: function (obj) { var id obj.data.id; $(#dept_home).load(dept/show?idid); }把请求过来的详情页面load到右边的div中显示。后台请求方法RequestMapping(value /show) public void show(DeptForm form, ModelMap map) throws InstantiationException, IllegalAccessException { Dept model new Dept(); Integer id form.getId(); Integer parentId 0; if(id!null) { model deptService.findById(id); parentId model.getParent()null?0:model.getParent().getId(); } map.put(parentId, parentId); map.put(model, model); }DeptForm类为一个接收类其中字段和实体类中一样。根据请求传递过来的id查询这条数据的详细信息之后把查询的当前部门详情数据及父级节点id用于下拉树TreeSelect传递给详情页面。show.html详情页面代码。meta charsetUTF-8 / style typetext/css .myData .layui-form-item{ margin: 20px 100px 10px 45px; } .myData .layui-form-label{ width: 85px; } .layui-input-block { margin-left: 120px; } /style form classlayui-form myData actionsave methodpost lay-filterstuform input typehidden nameid>RequestMapping(value/treeSelect) ResponseBody public Object treeSelect(Integer id) { Sort sort Sort.by(idx); SpecificationDept spec buildSpec1(); ListDept list deptService.findAll(spec,sort); return buildTree(list, id); } private Object buildTree(ListDept list, Integer id) { ListHashMapString, Object resultnew ArrayList(); for (Dept dept : list) { if(dept.getId() ! id) { HashMapString, Object nodenew HashMap(); node.put(id, dept.getId()); node.put(name,dept.getName()); node.put(open, false); node.put(checked, false); if(dept.getChildren().size() ! 0) { node.put(children,buildTree(dept.getChildren(), id)); } result.add(node); } } return result; } public SpecificationDept buildSpec1() { SpecificationDept specification new SpecificationDept() { private static final long serialVersionUID 1L; Override public Predicate toPredicate(RootDept root, CriteriaQuery? query, CriteriaBuilder cb) { HashSetPredicate rulesnew HashSet(); Predicate parent cb.isNull(root.get(parent)); rules.add(parent); return cb.and(rules.toArray(new Predicate[rules.size()])); } }; return specification; }显示的效果。上面修改并保存后台方法因为修改和新增共用的一个方法用id区分的。Override public Object save(DeptForm form) { try { Dept model new Dept(); Integer id form.getId(); if(id ! null) { model deptService.findById(id); } //父级菜单id Integer parentId form.getParentId(); if(parentId null) { model.setParent(null); }else { model.setParent(new Dept(parentId)); } BeanUtils.copyProperties(form, model,id, parent); deptService.save(model); return new AjaxResult(数据保存成功); } catch (Exception e) { return new AjaxResult(false,数据保存失败); } }设置节点操作在加载数据方法tree.render中添加以下代码。edit: [add, update, del], //操作节点的图标 operate: function(obj){ var type obj.type; //得到操作类型add、edit、del var data obj.data; //得到当前节点的数据 var elem obj.elem; //得到当前节点元素 var id data.id; var name data.title; if(type add){ //增加节点 $.post(dept/save, {parentId: id, name: 未命名}, function (result) { tree.reload(treeId, {data: getData()}); }) //返回 key 值 return ; } else if(type update){ //修改节点 $.post(dept/update, {id: id, name: name}, function () { tree.reload(treeId, {data: getData()}); }) } else if(type del){ //删除节点 $.post(dept/delete, {id: id}, function () { tree.reload(treeId, {data: getData()}); }); }; }其中operate为操作节点回调方法。obj.type为操作类型add为新增update为修改edl为删除。obj.data为操作节点后的数据。新增节点后向后台发送请求添加节点save方法和上面修改方法一样id为新建节点的父级节点id。修改节点同样向后台发送修改请求并传递对象的id和修改后的数据作为参数。后台响应方法。RequestMapping(value /update) ResponseBody public Object update(DeptForm form) { try { Dept model deptService.findById(form.getId()); model.setName(form.getName()); deptService.save(model); return new AjaxResult(数据保存成功); } catch (Exception e) { return new AjaxResult(false,数据保存失败); } }删除节点同理传递删除节点的id。删除请求方法。RequestMapping(value/delete) ResponseBody public Object delete(Integer id) { try { deptService.deleteById(id); return new AjaxResult(数据删除成功); } catch (Exception e) { return new AjaxResult(false,数据删除失败); } }使用按钮操作树形菜单。现在页面中定义两个按钮给按钮添加lay-demo属性并设置属性值JS通过这个属性值绑定点击事件。button classlayui-btn layui-btn-sm layui-btn-radius layui-btn-normal lay-demoaddDepti classlayui-icon#xe654;/i添加部门/button button classlayui-btn layui-btn-sm layui-btn-radius layui-btn-normal lay-demogain获取选中节点/button绑定添加部门和获取选中节点按钮的点击事件的JS代码。util.event(lay-demo, { addDept: function(othis){ $.get(dept/edit, function(data) { layer.open({ type: 1, title: 新增, area: [530px], content: data, btn: [提交, 退出], yes: function () { }, success: function (layero, index) { layui.use(form, function () { var form layui.form; layero.addClass(layui-form); var submitBtn layero.find(.layui-layer-btn0); submitBtn.attr(lay-filter, formVerify).attr(lay-submit, ); layero.keydown(function (e) { if (e.keyCode 13) { submitBtn.click(); } }); form.on(submit(formVerify), function (data) { $.post(dept/save, data.field, function (result) { if (result.success) { layer.close(index); tree.reload(treeId, {data: getData()}); } layer.msg(result.msg, {offset: rb}); }); return false; }); }); } }) }) }, gain: function () { var checkData tree.getChecked(treeId); var str JSON.stringify(checkData); $.post(dept/checkedGain, {data: str}, function () { }); layer.alert(JSON.stringify(checkData), {shade:0}); } });添加部门按钮点击事件先发送请求到后台跳转到eidt新增页面edit.html新增页面代码和上面的show.html显示部门详情页面差不多。上级部门同样使用的LayUI下拉树显示的下拉树数据请求方法和上面的详情页面下拉树请求方法一致。LayUI下拉树TreeSelect的使用。新增后的保存方法也和上面的保存方法一致。后台请求方法代码跳转到edit页面。RequestMapping(value /edit) public void edit(){ }edit.html页面完整代码如下。meta charsetUTF-8 / style typetext/css .myData .layui-form-item{ margin: 20px 100px 10px 45px; } .myData .layui-form-label{ width: 85px; } .layui-input-block { margin-left: 120px; } /style form classlayui-form myData actionsave methodpost lay-filterstuform div classlayui-form-item label classlayui-form-label上级部门/label div classlayui-input-block input typetext nameparentId idtree2 lay-filtertree2 classlayui-input / /div /div div classlayui-form-item label classlayui-form-label部门名称/label div classlayui-input-block input typetext namename lay-verifyrequired classlayui-input / /div /div div classlayui-form-item label classlayui-form-label部门负责人/label div classlayui-input-block input typetext namedeptName classlayui-input / /div /div div classlayui-form-item label classlayui-form-label电话/label div classlayui-input-block input typetext namephone classlayui-input / /div /div div classlayui-form-item label classlayui-form-label编号/label div classlayui-input-block input typetext namenumber classlayui-input / /div /div div classlayui-form-item label classlayui-form-label排序/label div classlayui-input-block input typetext nameidx value0 classlayui-input / /div /div /form script th:inlinejavascript layui.use([treeSelect, form], function () { var form layui.form; form.render(select); var treeSelect layui.treeSelect; treeSelect.render({ // 选择器 elem: #tree2, // 数据 data: dept/treeSelect, // 异步加载方式get/post默认get type: post, // 占位符 placeholder: 上级菜单, // 是否开启搜索功能true/false默认false search: true, // 一些可定制的样式 style: { folder: { enable: true }, line: { enable: true } }, // 加载完成后的回调函数 success: function (d) { } }); }); /script页面效果。获取选中节点按钮点击事件。如果项目需要保存数据时就需要获取到选中节点的数据了。这里可以获取到选中节点的数据之后当参数传递到后台。传递到后台是一个JSON数据的字符串需要转换一下这里给推荐大家两个很好用的JSON转换工具net.sf.json.JSONObject和Alibaba Fastjson。这里用的是Alibaba Fastjson需要引入以下依赖。dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.47/version /dependency这里用于输出新建了一个和tree的json数据的键名称一样的工具类DeptTree代码如下。import java.util.ArrayList; import java.util.List; public class DeptTree { private Integer id; private String title; private boolean checked; private boolean spread; private ListDeptTree children new ArrayList(); public Integer getId() { return id; } public void setId(Integer id) { this.id id; } public String getTitle() { return title; } public void setTitle(String title) { this.title title; } public ListDeptTree getChildren() { return children; } public void setChildren(ListDeptTree children) { this.children children; } public boolean isChecked() { return checked; } public void setChecked(boolean checked) { this.checked checked; } public boolean isSpread() { return spread; } public void setSpread(boolean spread) { this.spread spread; } }后台接收到传递过来的JSON数据字符串转换后输出方法。RequestMapping(value /checkedGain) ResponseBody public void checkedGain(String data){ ListDeptTree array2 JSONArray.parseArray(data, DeptTree.class); treeData(array2); } //递归输出选中数据 private void treeData(ListDeptTree array2) { for (DeptTree tree: array2){ System.out.println(tree.getTitle()tree.getId()); if(tree.getChildren() ! null){ treeData(tree.getChildren()); } } }选中节点点击获取选中节点数据。后台对应方法接收到数据转换后输出结果。数据拿到了之后保存方法就简单了。后台方法代码基本都在上面了页面全部代码。style typetext/css #dept_main, #dept_particulars{ width: 48.5%; display: inline-block; vertical-align: top; padding: 20px; background: white; box-sizing: border-box; } #dept_tree{ margin-top: 20px; } /style div iddept_main stylemargin-right: 2%; fieldset classlayui-elem-field layui-field-title legend所有部门/legend /fieldset button classlayui-btn layui-btn-sm layui-btn-radius layui-btn-normal lay-demoaddDepti classlayui-icon#xe654;/i添加部门/button button classlayui-btn layui-btn-sm layui-btn-radius layui-btn-normal lay-demogain获取选中节点/button div iddept_tree /div /div div iddept_particulars fieldset classlayui-elem-field layui-field-title legend部门详情/legend /fieldset div iddept_home div classlayui-tree-emptyText无数据/div /div /div script typetext/javascript layui.use([tree, util, layer], function() { var tree layui.tree; var util layui.util; var layer layui.layer; tree.render({ elem: #dept_tree, data: getData(), id: treeId, showCheckbox: true, //时候显示复选框 onlyIconControl: true, edit: [add, update, del], //操作节点的图标 click: function (obj) { var id obj.data.id; $(#dept_home).load(dept/show?idid); }, operate: function(obj){ var type obj.type; //得到操作类型add、edit、del var data obj.data; //得到当前节点的数据 var elem obj.elem; //得到当前节点元素 var id data.id; var name data.title; if(type add){ //增加节点 $.post(dept/save, {parentId: id, name: 未命名}, function (result) { tree.reload(treeId, {data: getData()}); }) //返回 key 值 return ; } else if(type update){ //修改节点 $.post(dept/update, {id: id, name: name}, function () { tree.reload(treeId, {data: getData()}); }) } else if(type del){ //删除节点 $.post(dept/delete, {id: id}, function () { tree.reload(treeId, {data: getData()}); }); }; } }); util.event(lay-demo, { addDept: function(othis){ $.get(dept/edit, function(data) { layer.open({ type: 1, title: 新增, area: [530px], content: data, btn: [提交, 退出], yes: function () { }, success: function (layero, index) { layui.use(form, function () { var form layui.form; layero.addClass(layui-form); var submitBtn layero.find(.layui-layer-btn0); submitBtn.attr(lay-filter, formVerify).attr(lay-submit, ); layero.keydown(function (e) { if (e.keyCode 13) { submitBtn.click(); } }); form.on(submit(formVerify), function (data) { $.post(dept/save, data.field, function (result) { if (result.success) { layer.close(index); tree.reload(treeId, {data: getData()}); } layer.msg(result.msg, {offset: rb}); }); return false; }); }); } }) }) }, gain: function () { var checkData tree.getChecked(treeId); var str JSON.stringify(checkData); $.post(dept/checkedGain, {data: str}, function () { }); layer.alert(JSON.stringify(checkData), {shade:0}); } }); }); function getData(){ var data []; $.ajax({ url: dept/treeload, //后台数据请求地址 type: post, async:false, success: function(resut){ data resut; } }); return data; } /script如果对你有帮助点赞关注一下呗^_^留下你的足迹。