天天看点

jxTMS--web界面定义

web界面定义

jxTMS原生是动态web界面模式,即只有一个tms.html页面,除右上角的四个系统菜单外,其它所有用户交互都是动态从后台获取界面描述,然后根据这个界面描述来动态的生成所需的web控件。

动态web界面的工作流程是:

  • 开发者用文本一行一个的定义web界面的各个控件
  • jxTMS在启动或热机刷新时,解析开发者的定义生成对应的webObject保存到组织缓存中
  • 开发者定义disp类型的界面显示入口【在op.py文件中】
  • jxTMS在用户登录时,通过该用户的角色列表和各入口的role的定义是否匹配,来为其准备初始界面
  • jxTMS将所有模块中该用户有权限的入口全部打包发送到前端,浏览器用这些入口信息为该用户生成左上角的功能菜单、左侧的快捷功能树
  • 用户从菜单、快捷栏、工具条等点击显示型入口
  • 后台在确认该用户有访问该入口的权限后【动态生成的界面无权限则不显示该入口,但如果用户知道了该入口的调用参数,可自行编写或用我们的自动化脚本测试工具发起攻击,所以后台还是会进行权限核验的】,加载相应的capa,并将相应的界面描述发送到前端
  • 前端根据入口中的配置和这个界面描述来确定该如何显示【如显示到主界面、辅助界面还是弹出对话框等】,并创建相应的控件
  • 后台等待一下,以确保后台有足够的时间生成界面所描述的各控件,然后触发prepareDisp事件以对该界面进行初始化,并装定数据
  • 前端用接收到的数据对控件进行装定,并确定状态,如是否遮挡、使能、不显示、自动计算的初始化等等
  • 如果是主界面显示,后台还会发送该界面所对应的工具条,前端将其显示到该界面上方的工具栏处

数据交互

对于输入型控件,前端会监听其change事件,实时将数据更新收集后以上一节中的pollData机制发送到后台,也就是说,用户的输入和后台之间是准同步的,所有的更新都会被实时采集,然后看pollData的间隔发送到后台。

如果定义了自动计算,则当作为数据源的某输入控件发生变化时,其会触发其所有参与的自动计算公式,而这些自动计算公式的产出方,如果是其它公式的数据源,其也会触发相应的公式进行计算。也就是说,自动计算是一个自动化的链式反应,会将所有受影响的数据全部计算后更新。

注:自动计算就类似于excel中的公式计算,只不过是开发者预先定义好的,然后发送到前端在用户输入时自动执行,大家请先在demo组织中,以销售角色登录后点击创建订单增加几个产品,输入数量、列表价、报价来看一下。然后再阅读关于自动计算的讲解会比较容易理解

用户如果点击了某按钮,前端会在向后台请求相应的cmd入口前执行两个动作:

  • 执行数据校验,如果该按钮定义了需校验的数据名,则前端会调用绑定了这些数据名的控件的校验函数,对用户输入到该控件中的值用开发者所定义的数据合法性检查规则进行校验,如果校验不通过,则弹窗发出警告
  • 校验通过后,为避免pollData周期过长,有数据未提交,所以会先触发一次pollData

界面打开后,后台执行prepareDisp或cmd型事件之后,后台是以异步的方式通过pollData将相应的输出数据捎回前端的。这些数据包括:全局变量、上下文变量、控件数据、控件属性值【如对违反业务规则的数据标红,即控件加红框以做警示】、提示信息等等,前端会将相应的数据自动分发。

注:由于是异步模式,所以用户完全可以打开多个界面进行协作,如在主界面打开订单审批界面,然后再在辅助界面打开发起这个订单的销售的业绩表、回款等来做出是否批准的判断。所以一次pollData会捎带回多个capa的数据,前端会根据不同的capa分发到相应的控件,而不会混乱。而这个最普通的场景也是笔者一定要采取异步模式的根本原因,因为在这个场景下,同步模式需要开发者来自行管理多个界面的数据交互,直接导致对开发者的要求高上很多

数据名

webObject是xTMS所有动态web控件的根对象,有三个最重要的属性:

  • 控件名【原生名】,控件的原生名只要在同一个空间中不重名即可。由于在前端,很多web控件都是由多个html元素组合而来,如数据表,包括表头、分页、增加新行按钮等等,所以就需要一个不重复的名字对这些内部的html元素标记供需要时能准确引用

注:为能在用户同时打开多个相同的界面时仍能确保准确定位,控件名更进一步的分为原生名和动态名,动态名是当前capaid+原生名而得来的,这样就确保了动态生成的控件的唯一性,而不会出现引用错误

  • 控件类型,目前jxTMS支持:文本、单行输入、多行输入、按钮、a【工具条】、tag【标记】、数据表、单选框、多选框、树型菜单、日期拾取器、下拉框、文件上传、图表【目前只支持bar型数据显示】、代码编辑器、div【应该是组容器,即用来打包其它控件的group以作为一个整体进行显示控制的,但一开始因为就是用div进行打包的,就一直沿用了下来】

注1:目前有两种组容器,一个是div主要用来整体加载或卸载以及作为一块工作区域进行显示控制的;一个是容器表【上面说的是数据表,用来分页显示数据的】,主要是为了简化控件行列对齐的。在早期,笔者用的是top、left等直接进行定位,但发现对齐实在是太浪费时间了,后来才采用了容器表以行列方式进行对齐,这就大大简化了前端的位置定义

注2:请开发者不要用top、left进行自主定位。因为笔者经过反复尝试后,认为jxTMS所定位的个人开发者实在是无法在美学和极少的工作量方面达到折中,所以后期增加的控件已经放弃了top、left的定位支持,所以无法保证这两个属性在各种控件中都能正常工作。关于美学问题,只能等jxTMS逐渐进化,加强在前端的美学设计后来解决,笔者也坚信,这才是根本的解决之道

注3:笔者也曾非常担心过这种表格行列对齐的界面能否为用户所接受,但实践后发现,对于目前的中小微企业来说,信息化管理是有无问题,是能否真正帮助其提高作业效率、降低运行成本的问题,就没有任何用户对界面提出过任何异议:)

  • 绑定的数据名,前端由于需要同时管理非常多的web控件,所以需要为这些动态web控件起一个全局唯一的动态名进行管理,但这个动态名太复杂,对开发者非常不友好,会因起名与使用而浪费大量的时间与精力,而一旦输错排查起来都非常的困难。而我们的编程模型是:用户点击一下,就执行一小段代码。所以必须为这些控件起一个局部的、简短的、最好有强烈业务特性的数据名。这个数据名只有需要和后台交互的web控件需要绑定,而这个数据名没有任何限制,只要在一个capa内没有重复即可

注:jxTMS中变量名的命名为java标准的变量命名方式,也并没有限制不得使用中文。但由于jxTMS的正确运行是由底层java、前端jQuery、开发者的python【基于jython,支持到python2.7,只使用到非常少的python特性,所以已经足够使用】三种代码环境共同协作完成的,所以建议不要用中文,以尽量减少不必要的转码风险

绑定了数据名的web控件,可以直接在python代码中用如下两个函数进行访问其值:

#读用户在控件中的输入
self.getInput(数据名)
#写到控件中
self.setOutput(数据名,值)
           

注1:文件上传控件是直接将文件保存到一个临时目录中,然后开发者可在self.importFilepath得到该文件的访问路径,例如上传一个excel文件来导入用户,则可:

#上传后,在类似【导入】按钮点击所对应的cmd事件中执行
with jxExcel(self.importFilepath,'people') as e:
	...已经打开了上传文件中名为people的sheet,可通过e来进行读写操作了
           

注2:上传文件为了避免重名等不必要的冲突,所以在上传时都重起了一个动态名,而且该文件保存在某临时目录中,jxTMS会定期清理该目录,所以上传文件的使用规则是:即传即用,用后失效

对于web控件,除了显示给用户看的值,有时开发者也需要动态修改控件的属性,如demo中所演示的:销售所给折扣低于其权限,则业务规则会将其标红以提示后继的审批人员这里不合规。针对这种情况:

#以原生名来获取本capa中的控件
wo=self.getWO(控件的原生名)
#设置该控件的属性,如set('boder','3px solid red')即将该控件标红--用一个3像素的红框框起来
wo.set(属性,值)
           

注1:大家在看demo所演示的源码的时候,会发现绝大多数的控件的原生名都是n。这是由于笔者在使用过程中,发现由于有同一个空间原生名不得重复的限制,导致笔者在给控件起名时太过痛苦,尤其是在修改web界面的定义时,一不注意就会导致系统报错。所以最后笔者利用容器表的表名+行列信息给所有容器表中名为n的控件自动生成一个原生名,这就大大降低了开发时的工作量和风险

注2:由于名为n的控件会自动生成一个原生名,所以开发者必须记得如果要修改控件属性,必须为其指定一个本空间内不重复的原生名

数据校验

数据校验的实现分为两步,首先在定义一个需要用户输入的web控件时,开发者可以指定用户输入的数据需要符合什么样的要求:

  • 值比较,如销售输入折扣率时应有一个最低限额
  • 长度限制,主要针对字符串,其长度应满足什么样的条件,如密码长度大于6
  • 日期比较,主要针对字符串转成的日期,应满足什么样的条件
  • 类型判断,应是数字、整数、日期格式等
  • 正则表达式,对于更复杂的规则,如判断是mail、手机号等,可以用javascript的正则表达式定义一个规则,必须满足该规则才接受

注:具体的校验规则请参考编程手册中的相关定义

其次,开发者还需要在用户输入完,所要点击的按钮的定义中,设置needVerify属性:

needVerify=['customName','customFax','customType','payType']
           

则当用户点击该按钮后,前端即检查这几个数据名的控件值是否符合其各自的数据准入规则。

注1:由于javascript属于弱类型语言,一般的控件在输入时都是作为字符串类型的,所以需要注意校验条件的比较是否成立。jxTMS在进行比较时是做了类型转换的,但开发者仍然需要反复测试用户各种奇怪输入情况下的校验条件是否成立

注2:前端输入的数据,在传递到后台后,因为java是强类型语言,所以底层java代码会将其转换为合适的类型,以确保java对象以及数据对象、数据库中的列这些数据的数据类型正确而一致。但,开发者用的是python,依然是一种弱类型语言,所以开发者不要使用过于奇怪或会造成歧义的数据格式,最佳的办法就是只使用:字符串、整数、浮点数、日期、布尔量这几种数据类型,其它数据类型,在这么复杂的javascript【弱类型】、java【强类型】、python【弱类型】、mysql数据库【已经准确的和java中的类型进行了匹配】集成环境中反复进行数据类型的准确转换,jxTMS已经很难了哦:(

注3:jxTMS在前后台传送数据使用的是json,而如果文本中混合有回车、tab、单引号、双引号等各种稀奇古怪的字符,javascript是无法正确转换的,所以在涉及到多行输入的控件,jxTMS使用base64编码后传送,但java和javascript的base64编码格式还不一致,需要前后台协作进行复杂的变换。所以,建议这种涉及多行输入的控件只用于人-人交互的用户意见等文本信息,其它需要计算机处理的数据用各种选择框、下拉框进行采集,万不得已也要使用单行输入框【单行输入框已经限制了一些奇怪字符的输入】,而将用户意见之类不加处理的直接保存到数据库中,在需要时也直接显示给用户看,不要让计算机来处理这些多行文本

多人协作场景下的数据隔离

业务系统中常用的流程就是一个多人协作的典型场景。这样的协作场景有几个要求:

  • 数据的共享与隔离,某环节的工作人员,只能操作指定的数据,但可能需要查看前面环节其它人员的处理情况,如demo所演示的订单审批,销售经理总得查看一下销售所输入的订单信息吧?但销售却不允许在总经理意见那输入任何文字,更不用说其能点击总经理的同意按钮了
  • 数据隐藏,如某订单非常独特,可能在通常的订单成本核算之外,进行单独的成本核算,但成本一般来说都不希望销售能看到,但总经理进行审批的时候又希望在一个界面中就能看到所有的信息,而不要看到了订单再打开另外的成本核算表之类的,这样的工作效率就太低了,尤其还是时间精力都是最值钱的核心员工
  • 一般情况下,因为有不同人员的输入,为了减少差错、容易控制,一般都是把输入和显示单独做成不同的界面,在需要录入的时候使用录入的界面,其它时候则使用显示的界面。但如果一个流程有七个处理环节【这还是一般的简易流程啊】,那么如果每个环节显示做一个、录入做一个,这就需要做十四个独立的控件组,然后根据不同情况动态决定到底显示哪些控件组,还需要平衡分别对应同一个数据的输入控件和显示控件的名字冲突问题,这就到底开发者的工作量加大了一倍,而作为通用系统的jxTMS如何协调、控制这个过程的复杂度更是上升了n倍

针对这些问题,jxTMS最终采取了一个最简单的办法来解决:遮罩。即不区分某环节你是只看还是需要录入,开发者每个环节只需要制作一个录入的控件组【开发量少了一半】,然后将所有控件组用半透明的遮罩进行遮挡,就实现了用户可看而无法录入,然后检查当前查看的用户是否需要录入,如果需要则打开其需要录入的那个控件组的遮罩即可。

perfect!完美,但有个问题,虽然你用遮罩挡住了让我点击然后输入的可能,但销售完全可以不停的按tab键,移动到总经理的意见那输入【同意,另给一万作为奖励】,然后再按tab移动到总经理的同意按钮那,回车!

所以,jxTMS关闭了tab键,通过牺牲用户键盘录入的平均效率来大幅度降低开发者的开发工作量和系统的复杂度,而这两者笔者经过权衡,认为是值得的:开发者工作量的下降会直接降低开发成本,系统复杂度的下降会大大提高系统的可靠性、坚固性、也极大的降低了开发者的潜在bug率,这两者都直接提升了整个系统的性价比。

而性价比的大幅度提升,将鼓励用户在更多业务环节、业务处理中使用jxTMS,而使用的越多,则需要用户录入的就越少、作业环节就越少、需要审批的环节也越少【因为业务越来越规范】,用户在单点损失的效率最终将从整个业务作业效率的巨大提升中得到超额的回报。

注:笔者在用jxTMS开发一个销售合同审批流程时,原本的折扣率计算等都需要审核人员在拿到审批单后重新手工计算一遍,而利用jxTMS的自动计算、业务规则审查,该环节只要看看折扣率那里系统有没有显示红框,没有直接点击确定按钮放行就好了

但是,遮罩引出了一个问题,jxTMS的web控件是先创建控件,然后后台刷数据过来进行装定的。对于其它控件都没问题,但数据表就不成了,因为数据表在创建的时候,接收到的只是列信息,具体的每行数据是后续刷过来的,而每有一行数据,数据表就会自动伸长一行,整个页面也会跟着伸长一行。所以如果遮罩一开始就设置,那么随着数据表的不断伸长,其数据行以及数据表下面的控件都可能会逃出遮罩区,而如果其中有输入控件和按钮,那这个遮罩有和没有没多大的区别。

因此,遮罩就必须在数据装定完成后才能进行遮挡,但jxTMS的数据又是异步的,虽然一般情况下,用户操作完毕,在下一个pollData中就会把所有数据全部捎带回来,但如果网络延迟比较大呢?!所以呢,遮罩是在显示完毕5秒后才进行遮挡的。

但是,又来但是了,我当时真的是都快疯了的啦:( 对于常用的操作界面,哪个按钮在哪,用户一定是非常熟悉的,在这5秒内,销售直接下拉点击总经理同意按钮,咋办?!

所以呢,jxTMS默认所有的操作按钮都是失能的,只有遮罩遮挡完毕才会使能。而且这个使能操作是和遮罩去除动作关联的,如果没有去除遮罩,其不会使能。

终于有好消息了,这个默认失能竟然带来了一个巨大的好处,web操作,因为要从本机到互联网服务器一来一回,所以系统的反应是比较迟钝的,用户如果性子急,看着还没反应,再点一下肯定会出问题的,所以这个默认失能就顺理成章的引出了一个附带功能:点击后失能。

但,令我崩溃的是:如果用户录入错误、数据校验未通过,那用户改好了却发现:失能后就无法再点击了啊!难道就因为一个数没输对,就让用户重新打开再次录入一遍?!所以,配套的就有一个延时n毫秒后重新使能的功能:

#按钮默认是先失能以避免在数据未装定完用户就有操作,然后再使能,并设置为点击后失能
#onlyOnce则是用来控制是否允许再次使能的,如果为false则先失能delay一段时间,然后再使能,以避免用户双击
#delay则是给出了失能后到再次使能的毫秒计数的间隔
#
#为什么会这么设计呢?!
#说起来都是泪:某个用户手太快,竟然在瞬间完成了双击,导致系统执行了两次,自然触发了稀奇古怪的故障:(
#
onlyOnce=false,delay=2000;
           

上述说明,虽然不需要开发者介入,但还是请开发者务必仔细阅读,然后根据用户业务的具体操作来选择功能按钮的参数配置,如无特殊情况,强烈建议默认使用上面所给的配置:onlyOnce=false,delay=2000

界面生命周期

所有的capa界面都是显式生存的:即在需要时后台检查需要加载新的界面,会从系统获取相应的capa,然后执行:

  • 根据这个capa创建一个新的capa
  • 为这个新的capa准备上下文,并将前端ui的代理和其进行绑定
  • 执行相应的disp函数,从系统中读取相应的控件组描述
  • 对读到的控件组将前述的从控件的原生名结合新创建的capa为其生成动态名
  • 将控件组中的所有数据名绑定到上下文中,为后继的输入输出关联做好准备
  • 对控件组所涉及到的按钮、工具条等和调用人就其调用权限进行预处理

所有这些处理工作做完,才能将这个绑定后的界面描述发送到前端。然后等待一小段时间,确保前端已经正确创建界面了:

  • 创建数据库连接和数据库事务,然后调用该界面所对应的prepareDisp事件
  • 查看该界面是否关联了工具条,如果有则为其调用权限进行预处理,然后将工具条发送到前端

最后,如果该界面用户用完了,点击关闭的×符号,前端将发出注销请求,后台将其卸载。

此外,前端有自己的界面管理策略,主界面是tab式样的,最大允许5个tab同时存在,辅助界面是平行弹窗式样的,最大允许10个界面同时存在,当超过各自限值后,两者都是按最近使用的策略来淘汰最久没有使用的那个界面的。

而当界面被淘汰掉的时候,前端也会向后台进行注销,如果同一个capa同时在主界面和辅助界面都有打开的界面,则只有当所有界面都被淘汰或关闭后才会注销。这一过程,开发者不需关注。

注:由于前述的原生名和capaid进行关联生成动态名原则,前端不支持在主界面同时打开同一个capa中的两个界面,如果在某个主界面中的界面点击后又打开了一个主界面中的界面,这个新的界面其实是在一个新的capa中加载的。开发者请务必了解这一点,以避免后者未能正确初始化【新的capa自然没有老capa中已经有的数据】

目前,jxTMS已经打包为云服务器镜像,开发者开箱即用:

jxTMS-腾讯云市场​