使用本示例需通过docker容器,请先下拉jxTMS的docker镜像并按说明启动tms容器,并从helloWorld开始尝试。
jxTMS简易流程的查询
任何一种业务管理,起码都需要两点:
-
业务的办理
-
业务办理情况的查询
简易流程也同样如此。我们前面已经实现sfDemo流程,所以现在我们还需要为其添加查询功能。和之前的数据表一样,实现业务查询功能需要做如下工作:
-
定义数据源
-
定义列表显示与查询界面
-
设置查询条件
-
对查询出来的数据进行加工
-
添加查询入口
大家可以先回想一下前面我们在条件查询以及数据表等节所演示的内容,先自己做一下,这样再来看我们的演示会比较容易理解。
定义数据源
在sql文件中添加:
sql listDemoSF
from affair as ta
select ta.all
where ta.Purpose=='demo'
orderBy ta.CreateTime DESC;
由于我们没有在data文件定义自己的流程数据类,那么jxTMS会自动使用默认的affair类作为sfDemo流程的流程数据类。由于affair是提供默认流程数据类的,所以我们在sfDemo流程启动时【用户点击了发起申请中申请人填写部分的确认按钮,对应的处理函数是@myModule.request(‘sfDemo’, ‘demoApply’, ‘dual’)标记的sfDemoApply_dual函数】,将sfDemo的流程数据对象的Purpose设置为demo。所以数据源listDemoSF就是查询Purpose为demo的affair。
注:如果想定义自己的流程数据类,则除通常的ID、CreateDate之外,必须声明如下的三个属性:
field Info json
field Extant json
field FlowExec json
同时,还需要在capa.py文件中增加一个joType的成员函数:
def joType(self):
return '自己在data文件定义的流程数据类名'
重载了joType函数返回一个数据类名,jxTMS就不会再使用affair来记录流程信息,而是用joType所指定的类名创建一个流程数据对象,然后将流程信息记录到该对象的Info、Extant、FlowExec中。
定义列表显示与查询界面
在web文件中添加:
web listDemoSF type div;
web listDemoSFt0 parent listDemoSF type table title="查询条件",width=900;
with listDemoSFt0 row 0 col c0 web n type text text="类型:",width=120;
with listDemoSFt0 row 0 col c1 web n bind demoType type input width=120;
with listDemoSFt0 row 0 col c2 web n type text text="名字:",width=120;
with listDemoSFt0 row 0 col c3 web n bind demoName type input width=120;
with listDemoSFt0 row 1 col c0 web n type text text="查询时间段起点:",width=120;
with listDemoSFt0 row 1 col c1 web n bind startDate type dtpicker width=120,minuteStep=5,initDisp=false;
with listDemoSFt0 row 1 col c2 web n type text text="查询时间段终点:",width=120;
with listDemoSFt0 row 1 col c3 web n bind endDate type dtpicker width=120,time=false,initDisp=false;
with listDemoSFt0 row 2 col c0 web n type button width=80,motion=cmd,demand=reSearch,text='搜索',onlyOnce=false;
web listDemoSFt1 bind tableTotalCount parent listDemoSF type table title="简易流程demo列表",width=900,pagination=true,query=search,queryParam={'listTable':'listDemoSFt1'};
with listDemoSFt1 col demoID head demoID hide=true;
with listDemoSFt1 col demoCreateTime head 创建时间 width=80;
with listDemoSFt1 col demoType head 类型 width=80;
with listDemoSFt1 col demoName head 名字 width=80;
with listDemoSFt1 col creator head 创建人 width=80;
with listDemoSFt1 col demoState head 状态 width=80;
with listDemoSFt1 col op1 head 查看 width=60;
其中startDate、endDate是新出现的控件:日期时间选择器。出于演示的目的,startDate设置为带时间选择、5分钟为最小单位;endDate设置为不带时间。
类似的可查询的数据表,在前面讲解过,大家可回看一下,然后对照着最后显示出来的界面一一过一下就好了。
设置查询条件
在capa.py文件中修改setSearchCondition函数:
def setSearchCondition(self, db, ctx):
if self.dataSource == 'demo.listDemoData':
cn = self.getInputBoolean('demoNoUsed')
if cn:
self.sql.addContion('demoData', 'NoUsed', jxCompare.Equal,cn)
cn = self.getInputString('demoName')
if utils.valid(cn):
self.sql.addContion('demoData', 'Name', jxCompare.Match,cn)
elif self.dataSource == 'demo.listDemoSF':
cn = self.getInputString('demoType')
if utils.valid(cn):
self.sql.addContion('affair', 'Type', jxCompare.Like,cn)
cn = self.getInputString('demoName')
if utils.valid(cn):
self.sql.addContion('affair', 'Name', jxCompare.Like,cn)
ds = self.getInputDate('startDate')
if not utils.isNone(ds):
self.sql.addContion('affair', 'CreateTime',jxCompare.GreatEqual, ds)
de = self.getInputDate('endDate')
if not utils.isNone(de):
self.sql.addContion('affair', 'CreateTime', jxCompare.LessEqual, utils.getDateEndBefore(de))
startDate和endDate是日期,所以应用getInputDate读取,如果用getInput则读到的是一个字符串。日期时间选择器控件所选择的时间,是我们所选择的时间点,而由于endDate不带时间【即便带了也一样】,所以endDate其实是我们所选时间的0点0分0秒,而我们一般的概念中,选择的结束时间都是包括在内的,这就会导致用户理解的是所选日期一天都算在内,而日期时间选择器所给出的却是当天的开始,就会造成一天的误差。所以我们这里用utils.getDateEndBefore函数,来获取所给日期【不管什么时间】的最后一个毫秒所对应的时间点。这就保证了用户理解的准确执行。
我们之前在示例数据表的查询的时候,增加过listDemoData,现在又增加了listDemoSF,可setSearchCondition函数、dispAffairInfo函数等系统函数都只有一个,怎么办呢?如同上面的示例一样,通过对当前所加载的数据源来区分用户打开的到底是listDemoData还是listDemoSF,这个当前加载的数据源的全名就放到了对象变量中的self.dataSource中。
我们的流程数据对象的类名是affair,所以大家查询的就是affair的Type和Name,这里使用的Like算子。
对查询出来的数据进行加工
在capa.py文件中,修改dispAffairInfo函数:
def dispAffairInfo(self,db,ctx,json, jo):
if self.dataSource == 'demo.listDemoData':
json.set("demoID", jo.ID)
json.set("demoCreateTime", jo.CreateTime)
json.set("demoType", utils.getMsg('{}-type',jo.Type))
json.set("demoName", utils.getMsg('{}-name',jo.Name))
json.set("demoTypeName", utils.getMsg('{}-{}',jo.Type,jo.Name))
json.set("demoNoUsed", jo.NoUsed)
elif self.dataSource == 'demo.listDemoSF':
json.set("demoID", jo.ID)
json.set("demoCreateTime", jo.CreateTime)
json.set("demoType", utils.getMsg('{}-type',jo.Type))
json.set("demoName", utils.getMsg('{}-name',jo.Name))
json.set("creator", jo.Info.get('creator'))
state = '审批中'.decode('utf-8')
if jo.State == 1:
state = '审批结束'.decode('utf-8')
json.set("demoState",state)
json.set('op1',self.getViewA(self.getFullName(),'sfDemo',jo.Name,jo.ID))
dispAffairInfo函数同样用self.dataSource区分了用户打开的到底是那个界面。其中比较特殊的是对op1的设置。大家回看一下web文件中listDemoSF的定义,会看到op1的列头是查看,应该可以猜测出来,这里应该就是点击后查看相应的流程的入口。请大家到时点击看看。
注:getViewA就是获取依据本功能模块的相关设置来获取查看某数据对象的入口。其函数签名为:
public static infoBlock getViewA(String capaname,String viewWeb,String joname,Long joid);
其中的参数说明如下:
-
capaname:功能模块的名字,即:module().name()
-
viewWeb:显示界面,对于sfDemo流程来说,就是sfDemo
-
joname:要显示的数据对象名,jxTMS用来动态设置界面的title
-
joid:要显示的数据对象id
然后getViewA就会利用这些参数动态生成一个针对本数据对象的查看入口,供用户点击后查看。
添加查询入口
在op.py中添加:
@biz.Motion('demo.demo1','disp','listDemoSF')
@biz.OPDescr
def op1(json):
json.setShortcut('演示'.decode('utf-8'),'demo简易流程查询'.decode('utf-8'))
json.setParam('dispType','list').setParam('dataSource','demo.listDemoSF')
我们将sql、web、op.py、capa.py等文件按用sftp管理jxTMS的代码所述更新到/home/tms/codeDefine/demo/demo/demo1目录中。
然后执行一次热机刷新后,由于快捷栏中的入口有变化,所以先要退出登录,再次登录后点击快捷栏中的【演示->demo简易流程查询】,然后看看显示出来的界面是什么样的并执行下查询。
结语
大家看到现在的setSearchCondition函数和dispAffairInfo函数,是否觉得太不kiss了?!简单的很,拆成两个模块啊。大家可以自己尝试一下。
不拆成两个模块可以吗?!当然可以,笔者在开始就说一个功能模块是由data、sql、web、op.py、capa.py五个文件组成,其中data、sql、op.py这三个文件的内容都不会太多,如果太多那真的是应该拆分为其它模块了。而web、capa.py之外,jxTMS还会从.web后缀名的文件加载界面定义,从.py后缀名的文件加载功能代码,所以大家也可以将listDemoData和listDemoSF做一下拆解。