LayUI是现在比较流行的一款前端框架,也有很多人基于LayUI开发了很多不错的组件,比如treetable树形表格。因为treetable是第三方基于LayUI开发的,所以需要先用Layui引入一下文件。
layui.config({
base : 'static/layui/'
}).extend({
treetable : 'treetable-lay/treetable'
});
之后先看一下显示的效果。
之后页面只需要引入LayUI的CSS和JS就可以了。
页面给一个table标签,用于显示treetable中的数据样式。
表格左上方的工具栏按钮组件代码。
JS请求加载数据及设置表格样式。
yui.use(['treetable', 'table', 'layer'], function () {
var table = layui.table;
var layer = layui.layer;
var treetable = layui.treetable;
//渲染表格
var renderTable = function(){
layer.load(2); //加载层
treetable.render({
height: 'full-160',
id:'menu',
treeColIndex: 1, //树形图标显示在第几列
treeSpid: '0', //最上级的父级id
treeIdName: 'id', //id字段的名称
treePidName: 'parentId', //父级节点字段
treeDefaultClose: false, //是否默认折叠
treeLinkage: false, //父级展开时是否自动展开所有子级
elem: '#menu', //表格id
url: 'menu/treedata',
toolbar: '#toolbarDemo',
page: false,
cols: [ [
{type:'radio'},
{field: 'name', title: '菜单名称'},
{field: 'url' , title: '地址'},
{field: 'icon' , hide : true, title: '图标'},
{field: 'idx', title: '排序'}
] ],
//数据渲染完的回调
done: function () {
//关闭加载
layer.closeAll('loading');
}
})
};
renderTable();
});
其中URL地址为请求数据地址。后台对应的方法。
@RequestMapping(value="/treedata")
@ResponseBody
public Object list(TbMenuForm form){
Sort sort = bulidSort(); //排序
Specification spec = buildSpec(form); //查询条件
List list = menuService.findAll(spec, sort);
return new TreeTableModel(list);
}
public Sort bulidSort() {
return Sort.by("idx"); //按idx字段排序
}
public Specification buildSpec(TbMenuForm form){
return null;
}
list方法中的TbMenuForm接收类中的字段和实体类字段差不多。其中TreeTableModel返回类为返回数据格式的工具类。
实体类TbMenu代码。
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.data.annotation.CreatedBy;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class TbMenu {
private Integer id;
private String name; //菜单名称
private String url; //路径
private String icon; //图标
private double idx; //排序
@JsonIgnore
private TbMenu parent;
@JsonIgnore
private List 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 getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public double getIdx() {
return idx;
}
public void setIdx(double idx) {
this.idx = idx;
}
@ManyToOne
@CreatedBy
public TbMenu getParent() {
return parent;
}
public void setParent(TbMenu parent) {
this.parent = parent;
}
@OneToMany(cascade=CascadeType.ALL,mappedBy="parent")
@OrderBy(value="idx")
public List getChildren() {
return children;
}
public void setChildren(List children) {
this.children = children;
}
public TbMenu(Integer id, String name, String url, String icon, double idx, TbMenu parent, List children) {
this.id = id;
this.name = name;
this.url = url;
this.icon = icon;
this.idx = idx;
this.parent = parent;
this.children = children;
}
public TbMenu(Integer id) {
this.id = id;
}
public TbMenu() {
}
@Transient
public Integer getParentId() {
return parent==null? 0 : parent.getId();
}
}
TbMenuForm接收类。
public class TbMenuForm {
private Integer id;
private String name; //菜单名称
private String url; //路径
private String icon; //图标
private double idx; //排序
private Integer parentId; //父节点id
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 getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public double getIdx() {
return idx;
}
public void setIdx(double idx) {
this.idx = idx;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
}
返回数据格式TreeTableModel类。
public class TreeTableModel {
private Integer code=0;
private String msg="ok";
private Integer count;
private List data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public List getData() {
return data;
}
public void setData(List data) {
this.data = data;
}
public TreeTableModel() {
super();
// TODO Auto-generated constructor stub
}
public TreeTableModel(Integer code, String msg, Integer count, List data) {
super();
this.code = code;
this.msg = msg;
this.count = count;
this.data = data;
}
public TreeTableModel(List data) {
super();
this.count=data.size();
this.data = data;
}
}
返回的JSON数据格式,这里需要注意的是parentId为父节点,需要和前面的JS中设置的属性值一样,没有父级节点parentId需要为0,不能为null。
{
"code": 0,
"msg": "ok",
"count": 6,
"data": [
{
"id": 1,
"name": "系统设置",
"url": "",
"icon": "",
"idx": 1.0,
"parentId": 0 //最上级节点,父节点为0
}, {
"id": 2,
"name": "角色管理",
"url": "",
"icon": "",
"idx": 1.0,
"parentId": 1 //上级节点
}, {
"id": 6,
"name": "数据表格",
"url": "",
"icon": "",
"idx": 1.0,
"parentId": 5
}, {
"id": 3,
"name": "部门管理",
"url": "",
"icon": "",
"idx": 2.0,
"parentId": 1
}, {
"id": 5,
"name": "表格案例",
"url": "",
"icon": "",
"idx": 2.0,
"parentId": 0
}, {
"id": 7,
"name": "树形表格",
"url": "",
"icon": "",
"idx": 2.0,
"parentId": 5
}
]
}
数据加载完成后,页面中就可以显示出数据了,效果如下。
使用table.on('toolbar(menu)', function(obj){})监听表格上面的工具类按钮点击事件。
监听工具栏的新增修改删除和刷新按钮方法。
table.on('toolbar(menu)', function(obj){
var checkStatus = table.checkStatus('menu');
var data = checkStatus.data;
if(obj.event === 'add'){
var parentId = data.length==0? 0 : data[0].id;
$.get('menu/edit', {parentId: parentId}, 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('menu/save',data.field,function(result){
if(result.success){
layer.close(index);
//刷新,重新渲染表格
renderTable();
}
layer.msg(result.msg,{offset:'rb'});
});
return false;
});
});
}
})
})
}else if(obj.event === 'updata'){
if(data.length != 1){
layer.msg("请选择一行进行编辑",{offset:'rb'});
}else{
var id = data[0].id;
$.get('menu/edit', {id: id}, 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('menu/save',data.field,function(result){
if(result.success){
layer.close(index);
//刷新,重新渲染表格
renderTable();
}
layer.msg(result.msg,{offset:'rb'});
});
return false;
});
});
}
})
})
}
}else if(obj.event === "delete"){
if(data.length != 1){
layer.msg("请选择一行进行删除",{offset:'rb'});
}else{
var id = data[0].id;
layer.confirm('确定删除选定行的数据吗?', function(index){
$.post('menu/delete',{id:id},function(result){
if(result.success){
layer.close(index);
renderTable();
}
layer.msg(result.msg,{offset:'rb'});
});
});
}
}else if(obj.event === "refresh"){
renderTable();
}
})
其中obj.event值为点击工具栏按钮的lay-event属性值。
新增和修改方法,先请求后台menu/edit获取到新增修改的页面,把页面用LayUI的layer弹框显示出来。这里新增和修改用的是一个方法和一个页面。修改时传递了一个id参数,用于查询修改的数据和区别新增还是修改。新增时如果选中了一行,会把当前行的id作为参数,传递到后台,相当于默认的父节点id。
跳转到新增和修改页面的edit后台方法。如果修改就把当前修改的数据传递到前台,新增时,如果有选中的节点,就把选中节点的id作为父节点id传递到前台。
@Override
public void edit(TbMenuForm form, ModelMap map) throws InstantiationException, IllegalAccessException {
TbMenu model = new TbMenu();
Integer id = form.getId();
if(id != null) {
model = menuService.findById(id);
}
map.put("model", model); //修改的对象,如果新增model就为null
map.put("parentId", form.getParentId()); //父节点id
}
edit页面代码,上级菜单是用LayUI的TreeSelect做的,对于TreeSelect的用法,大家可以访问LayUI下拉树TreeSelect的使用。
menu/treeSelect加载TreeSelect数据。对于TreeSelect的用法,大家可以访问LayUI下拉树TreeSelect的使用。
@RequestMapping(value="/treeSelect")
@ResponseBody
public Object treeSelect(Integer id) {
Sort sort = Sort.by("idx"); //排序
Specification spec = buildSpec1(); //查询条件,可以自行添加,对应的buildSpec1方法
List list = menuService.findAll(spec,sort);
return buildTree(list, id);
}
private Object buildTree(List list, Integer id) {
List> result=new ArrayList<>();
for (TbMenu dept : list) {
if(dept.getId() != id) {
HashMap node=new 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 Specification buildSpec1() {
Specification specification = new Specification() {
private static final long serialVersionUID = 1L;
@Override
public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
HashSet rules=new HashSet<>();
Predicate parent = cb.isNull(root.get("parent")); //添加父节点为空的条件,即查询最上级数据
rules.add(parent);
return cb.and(rules.toArray(new Predicate[rules.size()]));
}
};
return specification;
}
页面显示效果。
后台保存方法。
@Override
public Object save(TbMenuForm form) {
try {
TbMenu model = new TbMenu();
Integer id = form.getId();
if(id != null) {
model = menuService.findById(id);
}
//父级菜单id
Integer parentId = form.getParentId();
if(parentId == null) {
model.setParent(null);
}else {
model.setParent(new TbMenu(parentId));
}
BeanUtils.copyProperties(form, model,"id", "parent");
menuService.save(model);
return new AjaxResult("数据保存成功!");
} catch (Exception e) {
return new AjaxResult(false,"数据保存失败");
}
}
AjaxResult类是一个请求完成返回的一个工具类。
import java.util.HashMap;
import org.springframework.data.domain.Page;
public class AjaxResult {
private Boolean success;
private String msg;
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public AjaxResult(String msg) {
super();
this.success=true;
this.msg = msg;
}
public AjaxResult(Boolean success, String msg) {
super();
this.success = success;
this.msg = msg;
}
public AjaxResult(boolean success) {
this.success=success;
}
@SuppressWarnings("rawtypes")
public static HashMap bulidPageResult(Page page) {
HashMap result=new HashMap<>();
result.put("total", page.getTotalElements());
result.put("rows", page.getContent());
return result;
}
}
新增和修改就完了,下面就是删除数据。删除需要先判断是否选中了一行。然后把选中行的id作为参数,传递到后台,根据id删除数据就可以了。
if(data.length != 1){
layer.msg("请选择一行进行删除",{offset:'rb'});
}else{
var id = data[0].id;
layer.confirm('确定删除选定行的数据吗?', function(index){
$.post('menu/delete',{id:id},function(result){
if(result.success){
layer.close(index);
renderTable();
}
layer.msg(result.msg,{offset:'rb'});
});
});
}
最后一个就是刷新了,刷新只需要把表格刷新一下就可以了,调用一下表格刷新方法。
renderTable();
这里持久层框架用的Spring-Data-Jpa,但只要数据传递到后台了,怎么处理都差不多,请求的数据只要按照规定的JSON格式返回就可以了。后台方法代码上面基本的有,前台页面代码有些零散,下面是显示页面完整代码。
推荐文章,LayUI树形结构tree的使用
如果对你有帮助,点赞关注一下呗^_^,留下你的足迹。