[前端] LayUI树形表格treetable使用及说明

1882 0
黑夜隐士 2022-11-8 17:36:42 | 显示全部楼层 |阅读模式
目录

    LayUI是现在比较流行的一款前端框架实体类TbMenu代码TbMenuForm接收类返回数据格式TreeTableModel类监听工具栏的新增修改删除和刷新按钮方法后台保存方法


LayUI是现在比较流行的一款前端框架

也有很多人基于LayUI开发了很多不错的组件,比如treetable树形表格。
因为treetable是第三方基于LayUI开发的,所以需要先用Layui引入一下文件。


layui.config({
    base : 'static/layui/'
}).extend({
    treetable : 'treetable-lay/treetable'
});之后先看一下显示的效果。


之后页面只需要引入LayUI的CSS和JS就可以了。
页面给一个table标签,用于显示treetable中的数据样式。
<table class="layui-hide" id = "menu" lay-filter="menu"></table>表格左上方的工具栏按钮组件代码。
<script type="text/html" id="toolbarDemo">
    <div class="layui-btn-group">
        <button class="layui-btn layui-btn-sm" lay-event="add"><i class="layui-icon"></i>新增</button>
       <button class="layui-btn layui-btn-sm" lay-event="updata"><i class="layui-icon"></i>修改</button>
        <button class="layui-btn layui-btn-sm" lay-event="delete"><i class="layui-icon"></i>删除</button>
        <button class="layui-btn layui-btn-sm" lay-event="refresh"><i class="layui-icon"></i>刷新</button>
    </div>
</script>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<TbMenu> spec = buildSpec(form);    //查询条件
        List<TbMenu> list = menuService.findAll(spec, sort);
        return new TreeTableModel(list);
}

public Sort bulidSort() {
        return Sort.by("idx");    //按idx字段排序
}

public Specification<TbMenu> 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<TbMenu> 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<TbMenu> getChildren() {
        return children;
    }

    public void setChildren(List<TbMenu> children) {
        this.children = children;
    }

    public TbMenu(Integer id, String name, String url, String icon, double idx, TbMenu parent, List<TbMenu> 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 chckStatus = 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的使用。
<style type="text/css">
    .myData .layui-form-item{
        margin: 20px 100px 10px 45px;
    }
    .myData .layui-form-label{
        width: 80px;
    }
</style>
<form class="layui-form myData" action="save" method="post" lay-filter="stuform">
    <input type="hidden" name="id" data-th-value="${model.id}" />

        <div class="layui-form-item">
        <label class="layui-form-label">上级菜单:</label>
        <div class="layui-input-block">
            <input type="text" name="parentId" id="tree" lay-filter="tree" class="layui-input" />
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">菜单名称:</label>
        <div class="layui-input-block">
            <input type="text" name="name" lay-verify="required" th:value="${model.name}" class="layui-input" />
        </div>
    </div>
    <div class="layui-form-item" >
        <label class="layui-form-label">菜单地址:</label>
        <div class="layui-input-block">
            <input type="text" name="url" th:value="${model.url}" class="layui-input" />
        </div>
    </div>
    <div class="layui-form-item" >
        <label class="layui-form-label">图标:</label>
        <div class="layui-input-block">
            <input type="text" name="icon" th:value="${model.icon}" class="layui-input" />
        </div>
    </div>
    <div class="layui-form-item" >
        <label class="layui-form-label">排序:</label>
        <div class="layui-input-block">
            <input type="text" name="idx" th:value="${model.idx}" class="layui-input" />
        </div>
    </div>
</form>

<script th:inline="javascript">
        layui.use(["treeSelect", "form"], function () {
                var form = layui.form;
        form.render('select');
            var treeSelect = layui.treeSelect;
            treeSelect.render({
                // 选择器
                elem: '#tree',
                // 数据
                data: 'menu/treeSelect?id='+[[${model.id==null ? 0 : model.id}]],
                // 异步加载方式:get/post,默认get
                type: 'post',
                // 占位符
                placeholder: '上级菜单',
                // 是否开启搜索功能:true/false,默认false
                search: true,
                // 一些可定制的样式
                style: {
                    folder: {
                        enable: true
                    },
                    line: {
                        enable: true
                    }
                },
                // 加载完成后的回调函数
                success: function (d) {
                                // 选中节点,根据id筛选
                    treeSelect.checkNode('tree', [[${model.parent == null? parentId: model.parent.id}]]);
                                // 刷新树结构
                           treeSelect.refresh('tree');
                }
            });
        });
</script>menu/treeSelect加载TreeSelect数据。对于TreeSelect的用法,大家可以访问LayUI下拉树TreeSelect的使用。
@RequestMapping(value="/treeSelect")
@ResponseBody
public Object treeSelect(Integer id) {
    Sort sort = Sort.by("idx");    //排序
    Specification<TbMenu> spec = buildSpec1();    //查询条件,可以自行添加,对应的buildSpec1方法
    List<TbMenu> list = menuService.findAll(spec,sort);
    return buildTree(list, id);
}

private Object buildTree(List<TbMenu> list, Integer id) {
    List<HashMap<String, Object>> result=new ArrayList<>();
    for (TbMenu dept : list) {
        if(dept.getId() != id) {
            HashMap<String, Object> 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<TbMenu> buildSpec1() {
    Specification<TbMenu> specification = new Specification<TbMenu>() {

        private static final long serialVersionUID = 1L;

        @Override
        public Predicate toPredicate(Root<TbMenu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            HashSet<Predicate> 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<String, Object> bulidPageResult(Page page) {
                HashMap<String, Object> 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格式返回就可以了。后台方法代码上面基本的有,前台页面代码有些零散,下面是显示页面完整代码。
<script type="text/html" id="toolbarDemo">
    <div class="layui-btn-group">
        <button class="layui-btn layui-btn-sm" lay-event="add"><i class="layui-icon"></i>新增</button>
        <button class="layui-btn layui-btn-sm" lay-event="updata"><i class="layui-icon"></i>修改</button>
        <button class="layui-btn layui-btn-sm" lay-event="delete"><i class="layui-icon"></i>删除</button>
        <button class="layui-btn layui-btn-sm" lay-event="refresh"><i class="layui-icon"></i>刷新</button>
    </div>
</script>

<table class="layui-hide" id = "menu" lay-filter="menu"></table>

<script type="text/javascript">
    layui.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',        //pid字段的名称,父级菜单id
                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();

        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();
            }
        })
    })
</script>推荐文章,LayUI树形结构tree的使用
以上为个人经验,希望能给大家一个参考,也希望大家多多支持中国红客联盟。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行