python + layui.tree 生成树菜单,实现节点的增、删、改

1、渲染tree

表结构

class Class(db.Model):
    __tablename__ = 'class'  # 表名
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 主键
    class_name = db.Column(db.String(200), nullable=False)      # 分类名称、不能为空
    parent_id = db.Column(db.Integer, nullable=False)     # 父级id、不能为空

先写好获取tree数据的方法

function get_class() {  //获取节点数据
   var treeData = [];
   layui.$.ajax({
       url:'/get_class/',  //接口
       type:'get',     //请求方式
       async:false,  //同步请求
       success: function(resut){
           treeData = resut.data;
       }
   });
   console.log('节点数据:',treeData);
   return treeData;
}

这里需要用同步请求async:false,不然会获取不到数据。

渲染tree

layui.use(['tree','layer'],function () {
    var tree=layui.tree;
    tree.render({ //加载tree
        elem:'#class_tree'
        ,data: get_class()
        ,id:'treeId'
        ,showCheckbox: true //开启复选框
    })
})

tree容器:

<div id="class_tree"></div>

tree.render参数选项:

参数 参数说明
elem 指向容器选择器,我这里是指向容器的id
data 要渲染的数据
id 设定实例唯一索引,用于基础方法传参使用
showCheckbox 是开启复选框,开启时为true
edit 开启节点的操作图标,默认 false,目前支持['add', 'update', 'del']
accordion 是否开启手风琴模式,false
onlyIconControl 是否仅允许节点左侧图标控制展开收缩。默认 false(即点击节点本身也可控制)。若为 true,则只能通过节点左侧图标来展开收缩
isJump 是否允许点击节点时弹出新窗口跳转。默认 false,若开启,需在节点数据中设定 link 参数(值为 url 格式)
showLine 是否开启连接线。默认 true,若设为 false,则节点左侧出现三角图标。
text 要自定义各类默认文本 ,text:

后端接口

因为layui.tree接收的数据格式为

[
    {'id': 1,'parent_id': 0, 'title': '一级分类1', 'children': [
        {'id': 2, 'parent_id': 1, 'title': '二级分类1', 'children': [
            {'id': 3, 'parent_id': 2, 'title': '三级分类1', 'children': [
                {'id': 5, 'parent_id': 3, 'title': '四级分类1', 'children': []}, 
                {'id': 6, 'parent_id': 3, 'title': '四级分类2', 'children': []}
            ]}
        ]}, 
        {'id': 9, 'parent_id': 1, 'title': '二级分类2', 'children': []}
    ]}, 
    {'id': 21, 'parent_id': 0, 'title': '一级分类2', 'children': []}, 
]

所以后端代码:

@app.route('/tree/')
def tree():
    return render_template('tree.html')

def build_tree(data,parent_id):
    '''生成树菜单的数据
    :param data: 数据
    :param parent_id: 父级id
    :return:
    '''
    tree = []
    for row in data:
        if row['parent_id'] == parent_id:
            child = build_tree(data, row['id'])
            row['children'] = []
            if child:
                row['children'] += child
            tree.append(row)
    return tree
# 获取节点数据
@app.route('/get_class/',methods=['get'])
def get_class():
    categorys = Class.query.all()
    list = []
    for cla in categorys:
        data = dict(id=cla.id, parent_id=cla.parent_id, title=cla.class_name)
        list.append(data)
    print('list:',list)
    data = build_tree(list, 0)
    print('data:', data)
    return jsonify({'code': 0, 'msg': '获取节点数据成功', 'data': data})

查看页面效果image.png

2、开启操作图标(添加、编辑、删除)

添加顶级节点

因为tree.render操作图标的添加,只针对一级节点以下的节点添加,所以要先单独写一个添加一级节点
在容器上面增加按钮:

<button class="layui-btn layui-btn-sm layui-btn-radius" lay-demo="add_project" style="margin-left:10px"><i class="layui-icon">&#xe654;</i>添加项目</button>

image.png
增加顶级节点表单代码:

<!--添加顶级节点-->
<div id="add_project" style="display: none;margin-top: 20px;margin-left: 20px">
    <ul id="add_project_form" class="layui-form layui-form-pane" style="margin-right: 15px">
        <li class="layui-form-item">
            <label class="layui-form-label">项目名称</label>
            <div class="layui-input-block">
                <input name="project_name" placeholder="请输入项目名称" autocomplete="off" class="layui-input" lay-verify="required">
            </div>
        </li>
        <li class="layui-form-item" style="text-align: center">
            <div class="layui-input-block">
                <button type="submit" class="layui-btn" lay-submit lay-filter="add_project">立即提交</button>
                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
            </div>
        </li>
    </ul>
</div>

修改js:

layui.use(['tree','layer','util'],function () {
    var $ = layui.$,form=layui.form,tree=layui.tree,util=layui.util;
    tree.render({ //加载tree
        elem:'#class_tree'
        ,data: get_class()
        ,id:'treeId'
        ,showCheckbox: true //开启复选框
        ,edit:['add', 'update', 'del']
    })
    util.event('lay-demo', {  //添加顶级节点
        add_project:function () {
            layer.open({
                type: 1
                , title: '添加项目'
                , area: '400px'
                , shade: 0.4
                , content: $('#add_project')
                , success: function (layero, index) {
                    layero.find('.layui-layer-content').css('overflow', 'visible');
                    form.render().on('submit(add_project)', function(data) {
                        var form_data = {form_data: JSON.stringify(data.field)};
                        console.log('添加项目form_data:', form_data);
                        $.ajax({
                            data: form_data,
                            type: "post",
                            dataType: "JSON",
                            url: '/add_project/',
                            success:function (result) {
                                if (result.code===0){
                                    layer.close(index);
                                    layer.msg(result.msg,{icon: 6});
                                    tree.reload('treeId', {data: get_class()});  //添加成功时,需要重载tree
                                } else {
                                    layer.alert(result.msg,{icon: 5});
                                }
                            }
                        })
                    })
                }
            })
        }
    })
})

后端代码:

# 添加顶级节点
@app.route('/add_project/',methods=['post'])
def add_project():
    data = json.loads(request.form.get('form_data'))
    project_name = data['project_name']
    add_obj = Class(class_name=project_name, parent_id=0)  # 增加的内容
    try:
        db.session.add(add_obj)
        db.session.commit()
        return jsonify({'code': 0, 'msg': '添加顶级节点成功'})
    except Exception as e:
        return jsonify({'code': e, 'msg': '添加失败,请重试'})

开启操作图标(添加、编辑、删除)

tree.render增加代码:

,edit: ['add', 'update', 'del'] //操作节点的图标
,operate: function(obj){
    var type = obj.type;
    var data = obj.data; //得到当前节点的数据
    var elem = obj.elem; //得到当前节点元素
    console.log('elem:',elem,'data:',data)

    if(type === 'add'){  //添加子节点
        $.post('/add_class/',{parent_id: data.id, title: "未命名"},function (result) {
            if (result.code===0){
                layer.msg(result.msg,{icon: 6});
                tree.reload('treeId', {data: get_class()});
            }else if(result.code===1){
               layer.alert(result.msg,{icon: 5});
                tree.reload('treeId', {data: get_class()});
            }else {
                layer.alert(result.msg,{icon: 5})
            }

        })
    }else if(type === 'update'){  //修改节点
        $.post('/edit_class/',{class_id: data.id, title: data.title},function (result) {
            if (result.code===0){
                layer.msg(result.msg,{icon: 6});
                tree.reload('treeId', {data: get_class()});
            }else {
                layer.alert(result.msg,{icon: 5})
            }
        })
    }else if(type === 'del'){  //删除节点
        if(data.children.length!==0){  //该节点有子节点时不允许删除
            layer.alert('请先删除该节点的子节点!!', {icon: 5});
            tree.reload('treeId', {data: get_class()});
        }else {
            $.post('/del_class/',{class_id: data.id},function (result) {
                if (result.code===0){
                    layer.msg(result.msg,{icon: 6});
                    tree.reload('treeId', {data: get_class()});
                }else if(result.code===1) {
                    layer.alert(result.msg,{icon: 5});
                    tree.reload('treeId', {data: get_class()});
                }else {
                    layer.alert(result.msg,{icon: 5})
                }
            })
        }
    }
}

我这里添加节点默认命名为“未命名”,删除节点的时候做了判断,只能删除没有子节点的数据。

后端代码:

# 添加节点
@app.route('/add_class/',methods=['post'])
def add_class():
    parent_id = request.values.get('parent_id')
    title = request.values.get('title')
    class_name = Class.query.with_entities(Class.class_name)

    title_d = []
    for class_n in class_name:
        class_name = class_n.class_name
        title_d.append(class_name)
    if title in title_d:  # 判断“未命名”是否已存在
        class_obj = Class.query.filter_by(class_name=title).all()
        class_id = class_obj[0].id
        return jsonify({'code': 1, 'msg': '你添加的节点 "未命名" 还未修改名称','id':class_id})
    add_obj = Class(class_name=title, parent_id=parent_id)  # 增加的内容
    try:
        db.session.add(add_obj)
        db.session.commit()
        return jsonify({'code': 0, 'msg': '添加节点 “未命名” 成功,请修改节点名称'})
    except Exception as e:
        return jsonify({'code': e,'msg':'添加失败,请重试'})

# 修改节点
@app.route('/edit_class/',methods=['post'])
def edit_class():
    class_id = request.values.get('class_id')
    title = request.values.get('title')
    edit_obj = Class.query.get(class_id)  # 要修改的数据
    try:
        edit_obj.class_name = title
        db.session.commit()
        return jsonify({'code': 0, 'msg': '修改节点成功'})
    except Exception as e:
        return jsonify({'code': e, 'msg': '修改失败,请重试'})

# 删除节点
@app.route('/del_class/',methods=['post'])
def del_class():
    class_id = request.values.get('class_id')
    del_obj = Class.query.get(class_id)  # 要删除的数据
    try:
        db.session.delete(del_obj)
        db.session.commit()
        return jsonify({'code': 0, 'msg': '删除节点成功'})
    except Exception as e:
        return jsonify({'code': e, 'msg': '删除失败,请重试'})

添加节点的时候如果已经存在“未命名”,则会给出提示'你添加的节点 "未命名" 还未修改名称',需要将“未命名”修改名称后才能成功添加节点。

前端js和css都是基于layui-v2.6.8

posted @ 2021-09-02 17:23  、阿红吖  阅读(1162)  评论(0编辑  收藏  举报