Flask+LayUI开发手记(十):构建统一的选项集合服务

       作为前端最主要的组件,无论是layui-table表格还是layui-form表单,其中都涉及到选项列的处理。如果是普通编程,一个任务对应一个程序,自然可以就事论事地单对单处理,前后端都配制好选项,手工保证两者的一致性,同时加强校验保证程序不出错。但是如果想做一个统一的库表编辑工具,这样处理显然是不行的。

       所以,对于选项域的处理,第一个要求就是前后端一体化,即由后端服务作为唯一的数据源提供方,而前端是根据后端的数据做展示,这样,可以省去诸多一致性的麻烦。毕竟,有一些通用选项在业务界面中无数次出现,如果是在前端每一个选项都要配置上选项目明细,那出现变动时的修改就是恶梦般的事件。

        当然,还有第二个要求,就是前端在表格和表单里处理选项域,对选项要能够完全参数化配置化,要通过DOM中的特殊定义的属性进行配置,通过属性要能指定选项集合名称、选项展示类型(列表、树以及其它)、显示格式(只显示名称、键值名称组合)以及选项的默认值等一系列信息,不能每一个选项都要对应JS处理程序,那样实现同样也是恶梦。

        按照上面的这些要求来设计,后端服务首先要满足前端展现的数据要求,如针对表格选项列显示要提供选项字典(映射表),对表单选项域要提供选项列表(包括选项树型列表)等,同时还要提供唯一索引的选项集合名称,以便前端请求可以指定集合下载。因此,首先应该构建一个选项集合的基础类,命名为OptionSet,内容如下:

#选项集合基础类
class OptionSet(object) :# 初始化方法def __init__(self,v_opt=None,n_opt=None,t_opt=None):self.opt_dict = v_optself.opt_name = n_optself.opt_title = t_opt#logging.debug('options %s' % str(v_opt))if n_opt :sysOptionPool.add_optset(n_opt,self)def get_dict(self):return self.opt_dictdef get_map(self) :return self.get_dict()def get_name(self,id):return self.get_dict().get(id) def get_list(self,**kwargs) :itemlist = []d_opt = self.get_dict()if d_opt == None :return Nonefor (k,v) in d_opt.items():itemlist.append([k,v])f_sort = kwargs.get('sort')if f_sort == None or f_sort == False:return itemlistreturn sorted(itemlist)def get_option(self,sort=None) :return self.get_list(sort=sort)

        首先是以字典方式定义好选项条目数据,之后再定义上选项集合的名称。同时类还提供多个对外接口函数,分别是取选项原始字典get_dict(),取选项映射字典get_map(),取选项名称get_name,取选项列表get_list(),取选项明细get_option()。 

        注意,此处有个坑,就是后来才发现python是有类变量和实例变量的区分的。实例变量必须在在__init__()初始化函数中进行定义,由于python学的时间不长,所以在定义变量时就想当然地以为和JAVA的写法一致,在类名后面就直接写出。在类名后面定义的变量称为类变量,类变量确实能在类里公用,但根据同一个类定义的不同变量实际也是共享的,所谓类变量实际类似JAVA中定义的静态类变量。而要每个实例有自己的变量,就必须以self.val的方式定义在__init__()初始化方法。

        当然光有这个基础类是不够了,大多数选项条目是存储在数据库表里的,复杂一些的比如机构、菜单选项更是以父子节点的树型存储的,这些复杂选项将以OptionSet的子类扩展出来,目前总结,包括列表选项、树型选项和类别树型选项三种,将在下一节列出来。本节主要介绍选项集合整体框架的实现,OptionSet是选项集合的基础类,所以上述的接口函数,有很多是重复的,当然,随着各种扩展子类的出现,这些函数都会有不同的实现了。

        有了OptionSet基础类,就可以定义最简单的选项集合了,比如下面列出的这几个最常用的通用选项,作为系统全局变量,可以在具体业务实现模块中被引用。

#通用是否字段
optYesno = OptionSet({'Y':'是','N':'否',},'Yesno')#性别选项集合    
optGender = OptionSet({'0':'无','1':'男','2':'女',},'Gender')#通用状态选项集合
optStatus = OptionSet ({0:'正常',1:'停用',8:'临封',9:'封禁'},'Status')

        当然,以公用变量来定义选项明细确实可以用,并且通过名称的反射机制,也可以通过变量的字符串来查找到相应的选项,只是这并不是一个最佳的方案。更综合全面的方案还是将定义好的选项集合放到一个选项池里,这就是下面这个OptionPool类的实现机制。

        OptionPool的基本变量是一个包括选项集合名和集合实例的字典,通过一系列函数,可以把定义好的选项集合加入到选项池中,同样也可以对相应的选项集合进行删除、清空、列示以及重载。依托OptionPool类定义了唯一的实例sysOptionPool,相应的集合操作都将在此实例上进行。所以,在后端应用模块中想使用选项数据,直接引入该变量即可。

class OptionPool(object) :def __init__(self) :self.optpool = {}def add_optset(self,n_opt,optset) :self.optpool[n_opt]=optsetdef del_optset(self,n_opt) :self.optpool.pop[n_opt]#清理选项缓冲库def clr_optset(self):self.optpool.clear()#列出所有的选项集合def lst_optset(self) :return self.optpool.keys()#取指定的选项集对象def get_optset(self,n_opt) :return self.optpool.get(n_opt)#重新装入选项明细集,只对库表提取的选项集有效def reload_optset(self,n_opt) :rscode = self.optpool.get_optset(n_opt).reload()if rscode == 0 :udata =  f'重装选项集[{n_opt}]出错'else :udata = self.get_optset(n_opt).get_option()return (rscode,udata)#取多个选项集def get_options(self,nstr_opt) :nlst_opt = nstr_opt.split(';')udata = {}for v_opt in nlst_opt :n_opt,t_opt = v_opt.split('.',1) if '.' in v_opt else (v_opt,None)i_optset = self.get_optset(n_opt)if i_optset == None:return (0,f'获取选项集合时未发现选项集[{v_opt}]!!')optdata = i_optset.get_option(t_opt)if optdata == None :return (0,f'获取选项集合[{v_opt}]出错')udata[v_opt] = optdata#logging.debug('udata %s' % str(udata))#logging.debug('opt list: %s' % self.lst_optset())return (1,udata)#取多个选项集的字典映射def get_optmaps(self,nstr_opt) :nlst_opt = nstr_opt.split(';')udata = {}for v_opt in nlst_opt :n_opt,t_opt = v_opt.split('.',1) if '.' in v_opt else (v_opt,None)i_optset = self.get_optset(n_opt)if i_optset == None:return (0,f'获取选项映射MAP时未发现选项集[{v_opt}]!!')optdata=i_optset.get_map(t_opt)if optdata == None :return (0,f'获取选项映射[{v_opt}]出错')udata[v_opt] = optdatareturn (1,udata)#取多个选项集的列表def get_optlists(self,nstr_opt) :nlst_opt = nstr_opt.split(';')udata = {}for v_opt in nlst_opt :n_opt,t_opt = v_opt.split('.',1) if '.' in v_opt else (v_opt,None)i_optset = self.get_optset(n_opt)if i_optset == None:return (0,f'获取选项LIST时未发现选项集[{v_opt}]!!')optdata=i_optset.get_list(t_opt,sort=True)if optdata == None :return (0,f'获取选项列表LIST[{v_opt}]出错')udata[v_opt] = optdatareturn (1,udata)#重载多个选项集def reload_options(self,nstr_opt) :nlst_opt = nstr_opt.split(';')udata = {}for v_opt in nlst_opt :n_opt,t_opt = v_opt.split('.',1) if '.' in v_opt else (v_opt,None)i_optset = self.get_optset(n_opt)if i_optset == None:return (0,f'重装选项集[{v_opt}]未发现该集合!!')rscode = i_optset.reload()if rscode == 0 :return (rscode,f'重装选项集[{v_opt}]出错!!')optdata = self.get_optset(n_opt).get_option(t_opt)if optdata == None :return (0,f'重装选项集合[{v_opt}]获取数据出错!!') udata[v_opt] = optdatareturn (1,udata)sysOptionPool = OptionPool()

         定义完选项池后,即可依托选项池定义选项路由服务了。路由服务最好采用restful API编程模式,即由前端发送请求,后端以JSON格式提供数据,至于前端用数据怎么处理,由前端来自行定义。

       路由服务配合着选项池类的功能改进,即可完成我们想要的任何选项数据要求。当然,在这里要明确一下,后端的程序是提供基本数据的,前端展示的要求各种各样,如果前端能够依托基础数据实现,就由前端来实现吧。比如映射字典,虽然后端也提供数据服务,但是依托选项明细数据,前端也可以方便地将其变换为映射字典,基于减低服务端工作量的考虑,还是由前端JS完成变换更方便。

        路由服务提供如下的几个服务:

        无选项名称直接list:列出所有选项集合 map:

        选项名称后跟opr参数

        无参数:取选项集合 map:获取映射 list:获取列表 reload:重装         

#选项集合统一服务路由
@bp.route('/option/<n_opt>',methods=['GET','POST'])
@login_required
#@admin_auth
def sys_get_optset(n_opt):logging.info('Get OptionSet %s.....' % n_opt)if n_opt == 'list' :optkeylst = sysOptionPool.lst_optset()logging.debug('optset name : %s' % str(optkeylst))rscode = 1umsg = "取选项列表成功"udata = list(optkeylst)else :opr = request.values.get('opr')if opr == None :rscode,udata = sysOptionPool.get_options(n_opt)umsg = "取选项明细数据成功!!" + n_optelif opr == 'map':logging.debug('Get OptionSet Map %s.....' % n_opt)rscode,udata = sysOptionPool.get_optmaps(n_opt)umsg = "取选项明细字典成功!!" + n_optelif opr == 'list':logging.debug('Get OptionSet List %s.....' % n_opt)rscode,udata = sysOptionPool.get_optlists(n_opt)umsg = "取选项明细列表成功!!" + n_optelif opr == 'reload':rscode,udata = sysOptionPool.reload_options(n_opt)umsg = "重载选项明细集合成功!!" + n_opt#logging.debug('optset %s' %str(udata))if rscode == 0:umsg = udataudata = {}rsdata = {"success": rscode,"code": 0,"msg": umsg,"data":udata}return json.dumps(rsdata)

         最后,是前端获取选项集合的示例 。在table的列定义中和form的输入域中均可通过属性进行定义,示例如下:

listCols: [[ { type: 'checkbox', fixed: 'left' }......,{field: 'sex', title: '性别', width:60, sort: true,optset:'Gender',optformat:'name'},{field: 'telephone', title: '电话', width:100, sort: true},{field: 'role_cd', title: '会员角色', width: 100,optset:'MemberRole'},{field: 'status', title: '状态', width: 30,optset:'Status'}......
]]
    <div class="layui-form-item"><div class="layui-inline" style="width:30%"><label class="layui-form-label">角色</label><div class="layui-input-block"><select name="role_cd"  opt-set="MemberRole"><option value="">---请选择---</option></select></div></div><div class="layui-inline" style="width:30%"><label class="layui-form-label">性别</label><div class="layui-input-block"><select name="sex" opt-set="Gender" opt-default="1"></select></div></div><div class="layui-inline" style="width:33%"><label class="layui-form-label">状态</label><div class="layui-input-block"><select name="status" opt-set="Status"><option value="">---请选择---</option></select></div></div></div>

        应该说,前端的选项集合处理是以一个集成模块单独存在的,此处只列出获取选项集合的两个函数。首先是遍历前端页面获取所有的选项集合列表,这包括table中列信息和form中的输入域信息两部分,table派生的toolbar筛选域中也有选项集合,不过都包含在前两部分里了,所以不做遍历。生成的选项名称列表进行去重处理后发送对服务的请求,获取的数据存在前端统一的options接口对象中,其它模块均可通options对象获取相应数据。

var options={......}
//获取选项
function optset_load(options,callback) {let optlst = optnames(options);if (optlst.length==0) {if (callback) callback(options);return ;}let optstr= optlst.join(';');//console.log('optstr:',optstr,'optlst:',optlst);$.post(options.optUrl + '/' + optstr,{},function(rs){if(rs.success == 0 ){layer.msg(rs.msg,function(){});return false;}//layer.msg(rs.msg,function(){});options.optSet = rs.data;callback(options);},'json');
},//获取options中所有的选项集合的名称
function optnames(options) {let listmaps = [];options.listCols[0].forEach(function(icol) {let n_opt = icol.optmap || icol.optset;if (!n_opt) return ;listmaps.push(n_opt);});let optlst = $('[opt-set]').map(function() {n_opt = $(this).attr('opt-set');if (n_opt == '') {layer.msg('取opt-set名字时出现空值!!详细信息请查看控制台');console.log('取opt-set名字时出现空值!!',this);return ;}return n_opt;});optlst = [...optlst,...listmaps];if (optlst==null || optlst.length === 0) {return [];}return [...new Set(optlst)];
},

        通过上面这些程序,一个基本的选项集合集成框架初步成型。下面,就是以现在的框架为基础,继续细化,加入面向数据库存储的列表、树型以及多类别树型类了。下面是个简单的示例 。

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

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

相关文章

redis的数据初始化或增量更新的方法

做系统开发的时候&#xff0c;经常需要切换环境&#xff0c;做一些数据的初始化的工作&#xff0c;而redis的初始化&#xff0c;假如通过命令来执行&#xff0c;又太复杂&#xff0c;因为redis有很多种数据类型&#xff0c;全部通过敲击命令来初始化的话&#xff0c;打的命令实…

【PaddleOCR】OCR表格识别数据集介绍,包含PubTabNet、好未来表格识别、WTW中文场景表格等数据,持续更新中......

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

sparkjar任务运行

mainclass&#xff1a; test.sparkjar.SparkJarTest

Web攻防-文件下载文件读取文件删除目录遍历路径穿越

知识点&#xff1a; 1、WEB攻防-文件下载&读取&删除-功能点&URL 2、WEB攻防-目录遍历&穿越-功能点&URL 黑盒分析&#xff1a; 1、功能点 文件上传&#xff0c;文件下载&#xff0c;文件删除&#xff0c;文件管理器等地方 2、URL特征 文件名&#xff1a; d…

使用LIMIT + OFFSET 分页时,数据重复的风险

在使用 LIMIT OFFSET 分页时&#xff0c;数据重复的风险不仅与排序字段的唯一性有关&#xff0c;还与数据变动&#xff08;插入、删除、更新&#xff09;密切相关。以下是详细分析&#xff1a; 一、数据变动如何导致分页异常 1. 插入新数据 场景&#xff1a;用户在浏览第 1 页…

Excel 数据透视表不够用时,如何处理来自多个数据源的数据?

当数据透视表感到“吃力”时&#xff0c;我们该怎么办&#xff1a; 数据量巨大&#xff1a;Excel工作表有104万行的限制&#xff0c;当有几十万行数据时&#xff0c;透视表和公式就会变得非常卡顿。数据来源多样&#xff1a;数据分散在多个Excel文件、CSV文件、数据库甚至网页…

cf(1034)Div3(补题A B C D E F)

哈&#xff0c;这个比赛在开了不久之后&#xff0c;不知道为啥卡了差不多20来分钟&#xff0c;后面卡着卡着就想睡觉了。实在是太困了.... 题目意思&#xff1a; Alice做一次操作&#xff0c;删除任意数字a,而Bob做一次操作删除b使得ab对4取余是3。 获胜条件&#xff0c;有人…

浏览器与服务器的交互

浏览器地址栏输入URL&#xff08;网址​​&#xff09; ​​​​(1) 服务器进行URL解析​​&#xff1a;验证URL格式&#xff0c;提取协议、域名等 ​​​​(2) 服务器进行DNS查询​​&#xff1a;将域名转换为IP地址&#xff08;可能涉及缓存或DNS预取&#xff09; ​​​​…

Spring Boot中POST请求参数校验的实战指南

在现代的Web开发中&#xff0c;数据校验是确保应用程序稳定性和安全性的关键环节。Spring Boot提供了强大而灵活的校验机制&#xff0c;能够帮助开发者轻松地对POST请求参数进行校验。本文将详细介绍如何在Spring Boot中实现POST请求参数的校验&#xff0c;并通过具体的代码示例…

Spring Boot + MyBatis/MyBatis Plus:XML中循环处理List参数的终极指南

重要提醒&#xff1a;使用Param注解时&#xff0c;务必导入正确的包&#xff01; import org.apache.ibatis.annotations.Param; 很多开发者容易错误导入Spring的Param&#xff0c;导致参数绑定失败&#xff01; 一、为什么需要传递List参数&#xff1f; 最常见的场景是动态构…

Design Compiler:自适应重定时(Adaptive Retiming)

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 简介 重定时是DC Ultra引入的一种时序优化技术&#xff0c;可以将时序单元&#xff08;触发器和锁存器&#xff09;穿越组合逻辑前后移动&#xff0c;以优化设…

解决kali Linux在VMware中的全局缩放问题

在每次启动kali时&#xff0c;因为屏幕分辨率过高&#xff0c;系统整体特别小&#xff0c;该怎么操作调整合适呢 在搜索中搜索kali HiDPI Mode 选择yes 然后就会自动调整合适了

Python关键字梳理

在 Python 中&#xff0c;关键字&#xff08;Keywords&#xff09;是具有特殊含义的保留字&#xff0c;它们用于定义语法和结构。async 是 Python 3.5 引入的关键字&#xff0c;用于支持异步编程&#xff08;Asynchronous Programming&#xff09;。下面我将详细讲解 async 及其…

结构体实战:用Rust编写矩形面积计算器

文章目录结构体实战&#xff1a;用Rust编写矩形面积计算器&#x1f4d0; 问题描述1️⃣ 基础版&#xff1a;独立变量&#xff08;混乱版&#xff09;2️⃣ 进阶版&#xff1a;使用元组3️⃣ 终极版&#xff1a;使用结构体&#xff08;优雅版&#xff09;&#x1f3af; 运行结果…

基于开源链动2+1模式AI智能名片S2B2C商城小程序的场景零售创新研究

摘要&#xff1a;本文聚焦场景消费逻辑&#xff0c;探讨开源链动21模式AI智能名片S2B2C商城小程序在场景零售中的应用。通过分析场景消费中消费者体验的关键作用&#xff0c;结合该技术组合的特性&#xff0c;阐述其如何优化场景内容、增强场景美感&#xff0c;为消费者创造超乎…

新发布:26考研院校和专业大纲

复习方向错了&#xff0c;努力可能白费 近日&#xff0c;多所高校陆续发布2026年硕士研究生招生考试自命题科目大纲&#xff0c;为备考的学子们指明了复习方向。今年的考纲有哪些重要变化&#xff1f;又该如何应对&#xff1f;本文为你全面梳理&#xff01; 院校和专业发布详情…

matlab/Simulink-全套50个汽车性能建模与仿真源码模型9

50个simulink模型&#xff08;所有模型罗列如下&#xff0c;没罗列就是没有&#xff0c;包含子模块总共50个。&#xff09; 基于汽车驱动力-行驶阻力平衡图的汽车动力性仿真模型 基于汽车动力特性图的汽车动力性仿真模型 基于汽车功率平衡图的汽车动力性仿真模型 电动汽车动力…

为什么星敏感器(Star Tracker)需要时间同步?—— 从原理到应用的全解析

为什么星敏感器&#xff08;Star Tracker&#xff09;需要时间同步&#xff1f;—— 从原理到应用的全解析 引言 在卫星姿态控制系统中&#xff0c;星敏感器&#xff08;Star Tracker, 简称“星敏”&#xff09; 是最精确的姿态测量设备之一&#xff0c;其精度可达角秒级&…

【Cocos TypeScript 零基础 24.1】

目录 首次实战开发心得实战项目<修仙录游戏> 首次实战开发心得 遇到的技术问题也多 发表问题也不少 收入问题 本人都将会写篇专栏总结一下 实战项目<修仙录游戏> 上图是已上线的实战项目二维码 耗费的时间太久了 下次将跟新开发遇到的各种奇奇怪怪的问题 各位看…

Linux关机指令详解:shutdown命令的使用指南

掌握shutdown命令的正确使用对于Linux系统管理员至关重要&#xff0c;它不仅能确保系统安全关闭&#xff0c;还能避免数据丢失和用户工作中断。 目录 一、基本语法 二、常用选项 三、使用示例 立即关机 10分钟后关机 指定时间关机&#xff08;如23:00&#xff09; 重启系…