Flask+LayUI开发手记(一):LayUI表格的前端数据分页展现

        用数据表格table展示系统数据,是LayUI的基本功能,编码十分简单,就是通过table.render()渲染,把属性配置好就OK了,十分方便,功能也十分强大。

       不过,在实现时,把table的有个功能却理解错了,就是分页。

        table.render()的分页属性,主要包括三个,page、limit和limits,page是逻辑值,设置为true是分页,limit是每页显示行数,limits是设置面显示行数的多条选项。应该说,这几个属性都十分清晰,按说明配好后,果然分页控制栏就显示出来了,真不错。

       原来以为只要在服务端把需要的数据生成好传到前端来,分页就算完成了,但做了一段时间就发现不对了。开始做无外乎就是用户、角色、权限编辑,记录都很少,用不上分页,等做日志显示时一下就看出错误了,分页设的16条,怎么系统在一个页面把全部100多条数据都展示了。

       然后仔细研究才发现,这三个分页属性设置好,只是相当于打了了前端控制的开关,界面上显示出分页流程控件,可以进行分页切换操作,但数据展示内容,前端不管,还是要后端做处理的。table会在每次点击一次页面切换时,就向后端提交一次数据请求,请求中含有page和limit属性,后端按这两个参数把当前页的数据记录生成传下来。

       我是一直不喜欢这种每换一次页就要向后端提数据请求的实现逻辑的。一页才展示10几条记录,每次换页都要去取数据,来来回回的,性能不好,对后端压力有些太多了。而且,大部分数据展示功能,总共也就是几百条数据,完全可以一次把数据取到前端来,只在前端做处理就可以了。这样无疑大大减轻后端服务器的压力,而且控制上也比较简单是吧。

       也因此,我把系统需要进行分页展示的功能分成两种模式实现,一是前端数据分页,二是后端数据分页 。前端数据分页,即一次性提取后台数据到前端,由前端实现全部的分页数据处理控制,后端数据分页,即前端展示分页操作栏,所有的分页操作均提交到后端进行数据请求,由后端生成分页数据传给前端。

       下面这些程序,就是前端数据分页功能的实现,程序主要分为三个部分,前端html、JS实现、后端数据处理服务。

<!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="admin_log" lay-filter="admin_log" style="margin-top:-15px;"></table><script type="text/html" id="toolBar"><div class="layui-btn-container"><div class="layui-inline"  style="display:inline;margin-right:10px;"><label class="layui-btn-sm">日期范围</label><div class="layui-input-inline"><input type="text" id="bdate" placeholder="开始日期" autocomplete="off" class="layui-input layui-btn-sm"></div><label class="layui-btn-sm" style="display:inline;">-</label><div class="layui-input-inline"><input type="text" id="edate" placeholder="结束日期" autocomplete="off" class="layui-input layui-btn-sm"></div><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></div></div></div>
</script>
<script type="text/html" id="linetoolBar"><a lay-event="detail" title="查看细节" ><i class="layui-icon layui-icon-form"></i></a>
</script></body>
</html>

      前端 HTML页面相当简单,包括三个部分,table、toolBar、linetoolBar。table部分定义了一个总的表格容器,其具体内容由table.render进行渲染。toolBar是数据表格的头部工具栏,linetoolBar是数据表格的行工具栏内容。这三部分有了,基本的展示框架也就出来了。

       具体的还要看JavaScript中的处理,程序如下:

<script src="/static/layui/layui.js"></script>
<script>
layui.use(['jquery','layer','table','laydate'], function(){var $=layui.jquery,layer=layui.layer,table=layui.table,laydate = layui.laydate;var recData = null;var recCount = null;initTableData(0);// op 操作标志 0:渲染 1:重载function initTableData(op) {$.post('{{url_for("sysadm.admin_log")}}',{bdate:$('#bdate').val(),edate:$('#edate').val()},function(rs){if(rs.code == 0){recData = rs.data;recCount = rs.count;if (op ==0) table_render();else table_reload(1);layer.msg(rs.msg,function(){});}else{layer.msg(rs.msg,function(){});return false;}},'json');}function table_render() {table.render({elem: '#admin_log',height: 'full',data: recData,toolbar: '#toolBar',method: 'POST',page: true //开启分页,limits: [16, 20, 30, 40, 50]           ,limit : 16 ,even : true,size : 'sm' ,cols: [[ { type: 'checkbox', fixed: 'left' },{field: 'id', title: 'ID', width:40, sort: true, fixed: 'left'},{field: 'opr_cd', title: '操作', width:40, fixed: 'left'},{field: 'operate', title: '内容', width:260},{field: 'username', title: '操作员', width:80, sort: true},{field: 'ip', title: '客户端IP', width:80},{field: 'add_time', title: '操作时间', width: 220},{fixed: 'right', width:200, align:'center', toolbar: '#linetoolBar'}]]});b_date = laydate.render({elem: '#bdate'});e_date = laydate.render({elem: '#edate'});//表头工具栏事件table.on('toolbar(admin_log)', function (obj) {switch (obj.event) {case 'search':initTableData(1);break;};});//table行内工具栏事件table.on('tool(admin_log)', function (obj) {    //obj是指这张表中的数据row = obj.data;                             //将这张表中的数据赋给row这个变量rid = row.id;switch(obj.event) {case 'detail':adminlog_detail("查看细节",rid);break;}});}function adminlog_detail(title,rid){url = '{{url_for("sysadm.admin_log_detail",id="")}}' + rid;layer.open({type: 2,  //layer提供了5种层类型。可传入的值有:0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)title:title,area: ['660px', '460px'],   //宽高skin: 'layui-layer-rim',    //样式类名content:  url, //查看细节页面btn:['关闭'],yes: function(index, layero){layer.closeAll();},});}function table_reload(cpage) {table.reload('admin_log', {data : recData,page: { curr: cpage },},true);}});</script>

        在数据分页展示中,最主要的思路是先把数据一次性传到前端,之后,前端进行分页展示处理。所以,处理包括三个阶段,第一、数据获取,第二、渲染数据表格,第三、数据检索并重载。

       第一步数据获取,是向后端发post请求获取数据下传,这个服务器的python程序不用改动,继续按规定格式下发全量数据。

       第二步渲染数据表格,这块与原来最大的区别是数据源配置发生了变化。LayUI数据表格的数据源有两种模式,一是配url属性,二是配data属性。一般实现都是配url属性,从后端路由或者是JSON文件中取数据。data方式,我只用过一次,就是调试render时,模拟生成了几条数据,配在前端变量里作为数据源。这样调试可以避免前后端通讯的干扰因素,先熟悉table的各种技术细节。好在已经用过了,要做前端分页控制,就得用data数据源模式(主要还是借鉴网上别人的经验),将后端传下来的数据结构中的data赋给前端变量,然后配置成数据源,一切OK。

       这一步还出现了个小问题。开始,我把第一步数据获取initTableData()和第二步表格渲染table_render(),以串行模式编排的,也就是先取数赋本地变量再渲染表格。但运行时界面却不显示表格内容,调试发现recData变量在initTableData中有数,但在table_render渲染时居然是空值,按照程序流程不该如此呀。上网查了一下,原来是post异步通讯闹的怪,也就是说执行post后流程并不会等待结果返回,而是接着往下执行后面的程序。

       先获取数据再表格渲染,这一流程必须是串行的,所以post异步通讯的处理方法必须改,必须能串行。改可以有两种方法,第一种是将post改为同步通讯,也就是程序流程阻塞在这个点,等接收到数据返回后再往下执行,但post没有设置同步的属性,要改就要改为ajax提交,这个倒也不难。不过,还有更好的办法,还是把表格渲染的程序放到post的成功后回调函数中来执行,这一样可以实行同步顺序的功能。应该说,第二种方法更简单更方便。

       表格渲染,除了表格渲染render外,还要对头部及行内工具栏的按钮动作进行功能设置,这两个设置必须在表格渲染之后完成,同样属于表格渲染的一部分。合并在一起形成table_render(),都在获取数据后一起执行。

       把这些都配置完成,执行一下,数据展示出来了。而且,data数据源模式下,table会自动完成分页展示控制,不需要再象url数据源模式下,还需要各种特殊处理了。这是真方便。

       第三步是表格检查重载。头部工具栏中有一个检索功能,就是输入启始日期和终止日期,作为登录日志的查询条件。检索按钮实际上相当于表格数据初始化的再次重入,所以,执行了与第一次数据初始化同样的函数,只是入口属性设置为1。这时,需要从后端重新获取数据,但前端不必重新渲染,只要调用table_reload()进行重载就可以。

       前端的功能完成了,下面就是后端程序了。

@bp.route('/admin_log/',methods=['GET','POST'])
@login_required
#@admin_auth
def admin_log():if request.method == 'GET':return render_template('admin/admin_log.html.j2')else :logging.debug('Admin Log POST....')bdate = request.values.get('bdate');edate = request.values.get('edate')filtstr = '1=1 'if bdate :filtstr += ' and add_time >= "' + bdate +'"'if edate :filtstr += ' and add_time <= "' + edate + '"'logging.debug('filter : ' + filtstr)adminlog = db.session.query(Admin_Log).filter(text(filtstr)).order_by(Admin_Log.add_time.desc()).all()rnum = len(adminlog)alist = []for ilog in adminlog:rdata = dict(id=ilog.id,opr_cd=ilog.opr_cd,admin_id=ilog.admin_id,username=ilog.username,operate = ilog.operate,ip=ilog.ip,add_time=ilog.add_time.strftime('%Y-%m-%d %H:%M:%S'))alist.append(rdata)rsdata = {"code": 0,"msg": "查询登录日志数据成功","count": rnum,"data":alist}return jsonify(rsdata)@bp.route('/admin_log_detail/',methods=['GET'])
def admin_log_detail():if request.method == 'GET':rid= request.values.get('id')if rid == None:return render_template('admin/admin_log_dtl.html.j2')else:ilog = db.session.query(Admin_Log).filter_by(id=rid).first()rdata = dict(id=ilog.id,opr_cd=ilog.opr_cd,admin_id=ilog.admin_id,username=ilog.username,operate = ilog.operate,ip=ilog.ip,add_time=ilog.add_time.strftime('%Y-%m-%d %H:%M:%S'))rsdata = {"success": 1,"msg": "取登录日志数据成功" + rid,"data":rdata}logging.debug(str(rsdata))return render_template('admin/admin_log_dtl.html.j2',rsdata=rsdata)

        后端数据处理就是遵循flask的标准规范了,在get请求时,调用admin_log.html进行渲染,然后页面的JS程序会向后端发出post请求获取数据。后端在POST分支里取数并生成回传数据文件。回传数据文件的按规定组成,包括四部分code、count、msg和data。这个处理比较标准,不再细述了。

       最后的展示样式如下

       后端一次获取数据传到前端,由前端独立完成数据处理,其实只适合数据查询的应用场景。因为,一次把数据全传到前端的模式,意味着系统内出现两套数据,一套前端缓冲数据,一套后端原本数据,如果功能中需要增删改数据,如何保持前后端数据的一致性,就成了控制上的大问题。当然这种场景也是应用很广泛,专门做个前端数据分页模式也有很大用处。

       但后端数据分页模式也是有很多场景应用的。比如增删改查功能,与其却考虑前后端数据同步,还不如每次切换页就向服务端请求数据简单直接,系统所有处理都只去管理后端数据即可。应该说后端数据分页的模式,应用范围更为广泛,而且程序操作流程及逻辑都比前端分页更为清晰,也是layUI table主推的模式。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ue4.27 C++ 解析内容为json的字符串

json字符串为 R"({"x": -1870.0, "y": -11400.0})"&#xff0c;里面内容是个json对象。 const FString& Message R"({"x": -1870.0, "y": -11400.0})"; TSharedRef<TJsonReader<>> Reader TJs…

Java Nacos与Gateway的使用

Java系列文章目录 IDEA使用指南 Java泛型总结&#xff08;快速上手详解&#xff09; Java Lambda表达式总结&#xff08;快速上手详解&#xff09; Java Optional容器总结&#xff08;快速上手图解&#xff09; Java 自定义注解笔记总结&#xff08;油管&#xff09; Jav…

Python:jupyter 模型可视化(VS)

step1:打开vs安装扩展 安装后重新启动vs 建立可视化模型 import pandas as pd from sklearn.tree import DecisionTreeClassifier from sklearn import treemusic_data pd.read_csv(music.csv)Xmusic_data.drop(columns[genre]) Ymusic_data[genre]modelDecisionTreeClassifie…

IDEA构建SpringBoot多模块项目

前言 最近一直在思考一个问题&#xff0c;springboot的多模块项目到底是怎么运行和运作的&#xff1f; 一般我们大部分的springboot项目都是单模块的项目&#xff0c;但是如果后续有要求开发多模块的项目应该怎么处理&#xff1f;于是基于这点进行了研究。 本次文章将会带大…

java语言特点

Java语言是一种广泛使用的编程语言&#xff0c;它具有以下几个显著的特点&#xff1a; 面向对象&#xff1a;Java是一种纯面向对象的语言&#xff0c;它支持类的封装、继承和多态等特性。面向对象的设计使得Java程序更加模块化&#xff0c;易于维护和扩展。 平台无关性&#xf…

Linux驱动开发基础(Hello驱动)

所学内容来自百问网 目录 1. 文件在内核中的表示 2. 打开字符设备节点时&#xff0c;内核中也有对应的struct file 3. 编写驱动程序步骤 4. 相关知识点 4.1 涉及函数解析 4.2 module_init/module_exit的实现 4.3 register_chrdev的内部实现 4.4 class_destroy/device_…

K8s知识内容总结

1. K8s是个什么东西&#xff1f;解决了什么核心问题&#xff1f;相比docker有什么核心优势&#xff1f; k8s源于google内部的一个集群管理系统&#xff0c;它是用来管理集群的。比例&#xff0c;一个大型的电商系统&#xff0c;在微服务架构模式下&#xff0c;一个集群中可能有…

冒泡,选择,插入,希尔,快速,归并

冒泡&#xff0c;选择&#xff0c;插入&#xff0c;希尔&#xff0c;快速&#xff0c;归并 选择类的排序&#xff1a;选择排序&#xff0c;堆排序 交换类的排序&#xff1a;冒泡&#xff0c;快排 #include <stdio.h> #include<stdbool.h> #include<stdlib.h&…

10个html+css+js 绚丽按钮合集(1)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享10个htmlcssjs 绚丽按钮合集(1) 创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文章目录 10个htmlcssjs 绚丽按钮第1个&#xff1a;效果&#xff…

Selenium + Python 自动化测试06(frame操作方法)

上一篇我们讲述了特殊元素的操作方法&#xff0c;本篇接着讲一些可能遇到的其它操作方法。 如frame操作。 Frame 标签有Frameset、Frame、Iframe 3种&#xff0c;Frameset可以直接照常进行元素定位。Frame、Iframe需要驱动切换到对应的frame才可以定位到。否则 &#xff0c;会…

【人工智能】AI浪潮下,程序员何去何从?

人工智能时代&#xff0c;程序员何去何从&#xff1f; 随着AIGC&#xff08;如chatgpt、midjourney、claude等&#xff09;大语言模型接二连三的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工作&#x…

[第五空间 2021]WebFTP

打开题目所给的环境&#xff0c;发现要求我们输入账号密码&#xff0c;尝试输入了一些随意的账号密码&#xff0c;显示账号密码错误。这里我先用bp拦截&#xff0c;但是并没有找到什么有用的东西&#xff0c;然后这里用dirsearch扫一下后台&#xff1a; 发现这里是git泄露&…

SQL Zoo 8+.NSS Tutorial

以下数据来自SQL Zoo 1.at Edinburgh Napier University&#xff0c;studying (8) Computer Science&#xff0c;Show the the percentage who STRONGLY AGREE.&#xff08;在爱丁堡纳皮尔大学&#xff0c;学习“计算机科学”&#xff0c;显示STRONGLY AGREE的百分比&#xff0…

MySQL 实战 45 讲(01-05)

本文为笔者学习林晓斌老师《MySQL 实战 45 讲》课程的学习笔记&#xff0c;并进行了一定的知识扩充。 sql 查询语句的执行流程 大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层两部分。 Server 层包括连接器、查询缓存、分析器、优化器和执行器。 连接器负责接收客…

Debezium日常分享系列之:Debezium UI 的状态

Debezium日常分享系列之&#xff1a;Debezium UI 的状态 一、下一阶段工作二、设计新的UI三、目前阶段四、更多内容 虽然Debezium的UI是我们愿景的重要组成部分&#xff0c;但开发与Kafka Connect紧密绑定的UI并不是正确的方向。因此&#xff0c;决定冻结当前Web UI项目的开发。…

【UE 网络】Network Role and Authority、Actors Owner、Actor Role and RemoteRole

目录 0 引言1 Network Role and Authority&#xff08;网络角色和授权&#xff09;1.1 Authority (权威角色 / 权威端)1.2 Simulated Proxy (模拟代理 / 模拟端)1.3 Autonomous Proxy (自主代理 / 主动端)1.4 示例&#xff1a;多人塔防游戏中的 NetRole 2 Actors and their Own…

QT 网络聊天室简易版

视频:qt开发网络聊天w室软件3.4界面开发_哔哩哔哩_bilibili 目录 UI部分 设计稿图 放置控件 界面美化 拖动窗体 设置界面 网络部分 配置对话框 多项目结果和服务器端设计 客户端框架开发 UI部分 设计稿图 放置控件 界面美化 现在我们把窗体自带的标题栏给去了,用我们自…

[论文泛读]zkLLM: Zero Knowledge Proofs for Large Language models

文章目录 介绍实验数据实验数据1实验数据2实验数据3 介绍 这篇文章发在CCS2024&#xff0c;CCS是密码学领域的顶会。作者是来自加拿大的University of Waterloo。文章对大语言模型像GPT和LLM等大语言模型实现了零知识可验证执行&#xff0c;但不涉及零知识可验证训练。个人觉得…

WUP-CH34X ch34x系列芯片USB转串口通信uniapp插件使用说明

插件地址&#xff1a;WUP-CH34X 系列芯片USB转串口通信安卓库 简介 本文档是针对 CH340/CH341/CH342/CH343/CH344/CH347/CH9101/CH9102/CH9103/CH9104/CH9143的 USB 转串口安卓库的开发说明文档。 主要介绍如何使用芯片的 USB 转异步串口功能&#xff08;以下简称 CH34XUART…

循环神经网络三

一.介绍 在普通的神经网络中&#xff0c;信息的传递是单向的&#xff0c;这种限制虽然使得网络变得更容易学习&#xff0c;单在一定程度上也减弱了神经网络模型的能力。特别是在现实生活中&#xff0c;网络的输出不仅和当前时刻的输入相关&#xff0c;也过去一段时间的输出相关…