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: {undefined defaultNodeName: '未命名',none: '无数据' //数据为空时的提示文本} |
后端接口
因为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
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"></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