Flask+LayUI开发手记(五):树型表格实现数据展示与编辑

news2024/9/22 3:45:41

       说起来,树型表格treeTable绝对是后端程序员的巨大福利。要知道,在系统编程中,有父子节点关系的数据真是太多了,随便想一下就可以举出很多例子,比如权限、栏目、机构、产品、科目、货币、行业诸般等等,其实只要是稍微复杂一点的参数数据都会以树型方式来进行组织。以前做这种要专门找个树型组件来做,很麻烦的。

        Layui提供的树型表格TreeTable,实际是对表格的一个扩展类,把树和表格合在一起,前端 配置上完全和表格一样,数据组织也变化不大。树型表格最经典的实现应该是机构维护的程序,这次就拿它来做例子,本部分还是先做数据查询列表的功能,下一部分介绍树型表格的编辑。   

     组织机构数据列表的程序包括如下几部分,后端包括机构model和机构列表数据准备以及机构树型数据的组织三部分,前端是树型表格的展示实现。

       第一部分是机构的模型表,字段主要包括ID、机构编码、名称、简称、父级机构以及邮箱、地址、电话和机构类型等字段,如下:

class Branchs(db.Model):
    __tablename__ = 'cm_branch'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)  #机构ID
    branch_cd = db.Column(db.String(20),nullable=False,unique=True) #机构编码
    branch_name = db.Column(db.String(50))                          #机构名称
    short_name = db.Column(db.String(30))                           #机构简称
    parent_id=db.Column(db.Integer,default=0)                       #父级机构ID,不能为空
    email = db.Column(db.String(50))                                #联系邮箱
    phone = db.Column(db.String(20))                                #联系电话
    address = db.Column(db.String(120))                             #办公地址
    branch_cat = db.Column(db.String(4))                              #机构业务条线体系
                                        # 00:分支机构 (包括总部、区域分公司、办事处等)
                                        # 10:管理条线 (包括计划、财务、市场、审计合规)
                                        # 20:行政条线(包括人力、行政、党办等) 
                                        # 30:营业条线(包括销售、市场、专项产品部门)
    branch_type = db.Column(db.String(4))                           #机构类型 
                                        #00:管理机构 10:营业机构 20:管理部门 90:统计机构 
    status = db.Column(db.SmallInteger)                             #状态 0:正常 1:停用 9:废弃
    regtime = db.Column(db.DateTime,default=datetime.now)           #注册时间

        第二个程序是前端列表展示程序,branch_list.hmtl.j2,整个程序结构和前面的数据表格展示几乎完全一致。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>CMS系统-机构管理</title>
    <link rel="stylesheet" href="/static/layui/css/layui.css"  media="all">
</head>
<body>

<table id="table_tree" lay-filter="table_tree" style="margin-top:-15px;"></table>

<script type="text/html" id="toolBar">
    <div class="layui-btn-container">
        <div class="layui-inline">
            <label class="layui-form-label layui-btn-sm" style="width:90px;">栏目名称:</label>
            <div class="layui-input-inline">
                <input type="text" id="cat_name" placeholder="请输入机构名称" autocomplete="off" class="layui-input layui-btn-sm">
            </div>
        </div>
        <div class="layui-inline">
            <div class="layui-input-inline" style="padding-left:10px;padding-top:8px">
                <button id="btn_search" type="button" class="layui-btn layui-btn-normal layui-btn-sm" lay-event="search">
                    <i class="layui-icon layui-icon-search"></i>查询
                </button>
                <button id="btn_add" type="button" class="layui-btn layui-btn-sm" lay-event="add">
                    <i class="layui-icon layui-icon-add-1"></i>新增
                </button>
                <button id="btn_mdel" type="button" class="layui-btn layui-btn-sm" lay-event="mdel">
                    <i class="layui-icon layui-icon-delete"></i>批量删除
                </button>
           </div>
        </div>
    </div>
</script>

<script type="text/html" id="linetoolBar">
    <a class="layui-btn layui-btn-sm" lay-event="edit" title="编辑"><i class="layui-icon layui-icon-edit"></i></a>
    <a class="layui-btn layui-btn-sm" lay-event="adds" title="加子结点"><i class="layui-icon layui-icon-add-circle-fine"></i></a>
    <a class="layui-btn layui-btn-danger layui-btn-sm" lay-event="del" title="删除"><i class="layui-icon layui-icon-delete"></i></a>
</script>
<script src="/static/layui/layui.js"></script>
<script>
layui.use(['jquery','layer','treeTable'], function(){
	var $=layui.jquery
        ,layer=layui.layer
        ,table=layui.treeTable;
    var cur_row = null;
    var url_tree = '{{url_for("sysadm.branch_list")}}';
    var url_edit = '{{url_for("sysadm.branch_edit")}}';

    table.render({
            elem: '#table_tree'
            ,height: 'full'
            ,url: url_tree
            ,toolbar: '#toolBar'
            ,method: 'POST'
            ,page: false
            ,limits: [13, 26, 39, 52, 65]           
            ,limit : 13 
            ,even : true
            ,size : 'sm' 
            ,cols: [[ 
            { type: 'checkbox', fixed: 'left' }
            ,{field: 'id', title: 'ID', width:40, sort: true, fixed: 'left'}
            ,{field: 'name', title: '机构名称', width:140}
            ,{field: 'parent_id', title: 'PID', width:40, sort: true}
            ,{field: 'short_name', title: '机构简称', width:80}
            ,{field: 'branch_cd', title: '机构编码', width:60}
            ,{field: 'status_name', title: '状态', width:60}
            ,{field: 'category_name', title: '业务条线', width: 100}
            ,{field: 'type_name', title: '机构类型', width: 100}
            ,{field: 'regtime', title: '注册时间', width: 140}
            ,{fixed: 'right', width:200, align:'center', toolbar: '#linetoolBar'}
            ]]
            ,done: function () {
                table.expandAll('table_tree', true);
            }
    });

    //表头工具栏事件
    table.on('toolbar(table_tree)', function (obj) {
        switch (obj.event) {
            case 'search':
                tree_refresh(1);
                break;
            case 'add':
                cur_row=null;
		        tree_edit('add','新增机构节点',0);
                break;
            case 'mdel':
                tree_mdelete(1)
                break;
        };
    });

    //table行内工具栏事件
    table.on('tool(table_tree)', function (obj) {    
        //obj是指这张表中的数据,将表中选中的行数据赋给cur_row变量        
        cur_row = obj.data;                             
        var rid = cur_row.id;

        switch(obj.event) {
            case 'edit':
                tree_edit('upd',"修改机构节点",rid);
                break;
            case 'adds':
                tree_edit('adds',"新增下级机构",rid);
                break;
            case 'del':
                break;
        }
    });

    function tree_refresh(cpage) {
        table.reload('table_tree', {
            where: {                           
                'searchtext':$('#searchtext').val()
            },  
            page: { curr: cpage },
        },true);
    }

});

</script>
</body>
</html>

        和数据表格的展现程序对比一下,唯一的区别是引入的类是treeTable类。table.render的参数也完全一致,表头工具栏和行内工具栏的编写也是完全一样的。行内工具栏内增加了一个加子节点的功能,就是在编辑界面限定了父节点不能选择,因此增加了一块特殊处理,这个到后面的编辑功能时再细说。

       这里有一个问题要说明一下,就是分页。treeTable是支持分页的,但是我始终觉得树型分页是不单没意义而且还会造成逻辑混乱。因为后端取数据时,树型结构的顺序与ID顺序无关,这样取出的数据可能构不成完整的树型。同样,检索功能也造成了树型的混乱,不过至少检索功能可以标定想找的记录。

        第三个程序是后端的树型数据服务程序,程序如下:

################机构管理模块#################
@bp.route('/branch_list/',methods=['GET','POST'])
@login_required
#@admin_auth
def branch_list():
    if request.method == 'GET':
        return render_template('admin/branch_list.html.j2')
    else :
        logging.debug('Branch list POST....')
        br_name = request.values.get('searchtext')
        filtstr = ' 1=1 '
        if br_name :
            filtstr += ' and branch_name like "' + br_name + r'%"'
        logging.debug('filter : ' + filtstr)

        rows = db.session.query(Branchs).filter(text(filtstr)).order_by(Branchs.id).all()
        rnum = len(rows)
        alist = []
        logging.debug('rnum: %s' % rnum)
        brhCategory = Branch_Category()
        brhType = Branch_Type()
        brhStatus = Branch_Status()
        for irow in rows:
            rdata = dict(id=irow.id,name=irow.branch_name,parent_id=irow.parent_id,
                        short_name= irow.short_name,branch_cd=irow.branch_cd,
                        email=irow.email,phone=irow.phone,address=irow.address,
                        branch_cat=irow.branch_cat,category_name = brhCategory.id_format(irow.branch_cat), 
                        branch_type=irow.branch_type,type_name = brhType.id_format(irow.branch_type),
                        status=irow.status,status_name = brhStatus.id_format(irow.status),
                        regtime=irow.regtime.strftime('%Y-%m-%d %H:%M:%S'))
            alist.append(rdata)
        adata = dim_build_tree(alist,0,0)
        logging.debug('adata:' + str(adata))
        rsdata = {
            "code": 0,
            "msg": "获取机构数据成功",
            "count": rnum,
            "data":adata
        }
        return json.dumps(rsdata)

       这个程序也和数据表格的后端数据服务程序没有区别,只是去掉了分页逻辑处理部分。回传数据的最外层结构是一样的,但是回传的明细数据结构是整理成了一个树型结构数据,调用了通用工具函数dim_build_tree()来实现这个功能。程序如下:

#由父子节点的列表生成树型结构的通用程序。
#要求节点为id,父节点为parent_id,节点名称为name,子结构为children。
def dim_build_tree(dlist,p_id,level):
    tree = []
    for row in dlist:
        if row['parent_id'] ==p_id:
            row['level'] = level
            child = dim_build_tree(dlist, row['id'], level+1)
            row['children'] = []
            if child:
                row['children'] += child
                row['isParent'] = True
            else :
                row['isParent'] = False
            tree.append(row)
    return tree

        注意,想让树型能够成功展示出来,下面的每个环节都很重要。

        第一,回传的数据结构是一个嵌套的数组,必须要有id、parent_id和name三个字段,否则树型无法显示。特别是name字段,因为大多数数据的名称都可能有自定义的字段名,在上面的dict生成时,必须映射成’name'。

       第二,isParent必须要设置上(虽然我觉得这个字段其实是冗余的),而且必须是boolean值。并且前端treeTable要求的是‘true/false',而不是python的‘True/False'。这个我前面提过,flask自带的jsonify()函数没有做这个转换,所以前端树型展示有错误,必须换成json.dumps()来转换,就没有问题了。

       最后是功能界面展示,如下图所示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2085216.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国密起步4:GmSSL3生成证书并使用tls(SM2、SSL)

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 此文介绍如何使用gmssl的tls示…

RCS plot 包内置数据集使用时报错,如何解决?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【爬虫软件】YouTube评论采集工具

我用Python开发的油管评论批量采集软件。 软件界面&#xff1a; 采集结果&#xff1a; 演示视频&#xff1a; https://www.bilibili.com/video/BV1Ny411z7Wk 完整讲解&#xff1a; https://www.bilibili.com/read/cv35334276

Python进阶01-面向对象基础

零、文章目录 Python进阶01-面向对象基础 1、面向对象编程思想 &#xff08;1&#xff09;编程思想是什么 所谓的编程思想&#xff0c;就是人们利用计算机来解决实际问题的一种思维方式。常见的编程思想有面向过程和面向对象&#xff0c;很多计算机语言的语法各不相同&…

OpenHarmony南向开发:分布式软总线-softbus_tool使用规范

oftbus_tool 是 OpenHarmony 分布式软总线 API 调用测试工具&#xff0c;文件结构如下图所示。 softbus_tool 能够将软总线 interfaces 目录下的一些常用接口集中起来&#xff0c;供设备间搭建一些场景时使用&#xff08;比如设备绑定、BR 组网&#xff0c;BLE 组网&#xff0…

oracle的BITAND()函数用法

BITAND函数是计算两个数据的按位与 如下&#xff1a; 0010 0101 的按位与结果是0000 转为2进制就是0 0010 0011 的按位与结果是0010 转为2进制就是2

linux本地库迁移到阿里云云redis

背景 领导要我把另外一个不同账号的云redis进行同步 想法 阿里云不能直接备份还原redis&#xff0c;需要把备份文件先给搞到本地redis,然后本地redis进行同步到云redis 大概的逻辑是这样 实操 我这里下载的版本是4.0.2,文件如下: redis-shake-linux-amd64.tar.gz github下…

独立站除了Shopify还有什么?

国外电商平台仅占40-50%的市场份额。独立网站具有巨大的潜力。通过建立独立的跨境电商网站&#xff0c;您可以拥有自己的品牌销售体系。然而&#xff0c;对于很多平台卖家来说&#xff0c;建站可能并不是一件容易的事。在构建网站之前需要考虑几个问题&#xff0c;以及如何选择…

如何用wireshark分析找出url接口和param参数???

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

Java学习第四天

程序流程控制 顺序结构&#xff1a;按顺序从上往下执行&#xff1b; 分支结构&#xff1a; if分支结构&#xff08;区间匹配&#xff09; switch分支&#xff08;值匹配&#xff09; 循环结构&#xff1a; for循环 while循环 do-while循环 死循环 continue和break区别&…

鸿道Intewell实时操作系统MIPS架构生态:龙芯2K1000

在当今快速发展的工业互联时代&#xff0c;实时操作系统&#xff08;RTOS&#xff09;扮演着至关重要的角色。实时操作系统为工业制造、能源电力、轨道交通、汽车电子等领域提供了高实时、高可靠和高效率的技术支持。实时操作系统的设计注重实时性、并行性、可靠性和功耗&#…

基于分布式计算的电商系统设计与实现【系统设计、模型预测、大屏设计、海量数据、Hadoop集群】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目展示项目介绍 目录摘要Abstract1 引言1.1 研究背景1.2 国内外研究现状1.3 研究目的1.4 研究意义 2 关键技术理论介绍2.1 Hadoop相关组件介绍2.2 分布式集群介绍2.3 Pyecharts介绍2.4 Fl…

Keil The selected deivce“xxx“is unknown。。。识别到芯片依然烧录不进去程序解决或者未识别

之前一直用DAP烧录&#xff0c;用Jlink后烧录发现不行 在网上找了很多教程&#xff0c;版本等问题都一一排查依然不行 最后通过修改Port解决。。。。 将JTAG改成SW后就可识别芯片并且可以烧录。。。。

Vocechat本地部署结合内网穿透实现远程聊天交互实战方案(1)

文章目录 前言1. 拉取Vocechat2. 运行Vocechat3. 本地局域网访问4. 群晖安装Cpolar5. 配置公网地址6. 公网访问小结 7. 固定公网地址 前言 本文主要介绍如何在本地群晖NAS搭建一个自己的聊天服务Vocechat&#xff0c;并结合内网穿透工具实现使用任意浏览器远程访问进行智能聊天…

蚂蚁数科隐私计算技术平台FAIR入选2024数博会十大领先科技成果

8月28日&#xff0c;2024数博会领先科技成果发布会在贵阳国际生态会议中心举行&#xff0c;蚂蚁数科自主研发的隐私计算技术平台&#xff08;FAIR&#xff09;凭借领先的技术及广泛的应用场景脱颖而出&#xff0c;成功入选本届大会的“十大领先科技成果”奖。 据悉&#xff0c;…

HDMI、USB、WIFI的名称魔性更改

HDMI 授权组织把原来 HDMI 2.0 和 2.1 接口统称为 HDMI 2.1 的做法&#xff0c;等于是让电子设备上的 HDMI 2.1 标示丧失了区分作用&#xff0c;这操作就很离谱。 说起来类似这样的事情已经其实不是第一次发生了。 就拿现在最普及的 USB 接口来说&#xff0c;在 USB 3.2 标准…

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino 案例分析电路设计1. **基本音频检测电路设计**电路结构:2. **灵敏度调节原理**方法:3. **非 MCU 控制的 LED 触发**设计步骤:4. **电路示例**5. **示意图(文本描述)**总结实验方法案例分析 一个硅胶娃娃,挤压或拍打…

(第三十八天)

1 、 harbor 软件包下载 https://github.com/search?qharbor&typerepositories 2 、出现拒绝连接错误&#xff0c;可能是由于容器没开 # 问题解决&#xff1a; [rootdocker ~] # curl localhost:5000/v2/_catalog curl : (7) Failed connect to localhost:5000; 拒绝…

零基础国产GD32单片机编程入门(一)GPIO输出Keil5工程创建含源码

文章目录 一.概要二.GD32单片机GPIO内部结构图三.GD32单片机GPIO输入输出信号流向四.GD32单片机GPIO引脚的复用以及重映射五.从零开始创建一个GD32F103C8T6单片机GPIO输出驱动LED灯例程六.工程源代码下载七.小结 一.概要 GPIO(general porpose intput output):单片机通用输入输…

如何禁止电脑访问网站

一、修改Hosts文件 找到Hosts文件&#xff1a;在Windows系统中&#xff0c;Hosts文件通常位于C:\Windows\System32\drivers\etc\目录下。 编辑Hosts文件&#xff1a;以管理员身份打开记事本或任意文本编辑器&#xff0c;然后找到并打开Hosts文件。 添加禁止访问的域名&#…