使用本示例需通過docker容器,請先下拉jxTMS的docker鏡像并按說明啟動tms容器,并從helloWorld開始嘗試。
jxTMS的簡易流程這一節我們做個複雜點的:一個申請審批流程。
一件事如果需要多人協作完成,業務管理系統一般是将其組織為一個流程來把各作業環節串聯起來。但一般意義上的流程既較為複雜又門檻頗高,對于jxTMS的所期望的低成本開發來說,對開發者的要求有點高。所以jxTMS對一般意義上的流程進行了簡化,稱之為簡易流程。
簡易流程是面向企業中大量的常見流程,對一般性的流程做了些簡化:
注:如果大家對一般性的流程是什麼樣的不太理解,那就先搞清楚jxTMS的簡易流程,等熟悉了之後再針對性的添加上面所說的這兩個被簡化掉的内容,就容易理解一般的流程了
也就是說,簡易流程就是針對大多數的線性作業,将作業環節按序依次推進,每節點就隻有兩個動作:
所有的申請審批都是此種模式的流程。
制作一個流程所需的工作内容jxTMS已經為簡易流程做了大量的支持工作,所以在jxTMS中制作一個簡易流程的工作得到了大大的簡化。
首先,需要協助業務部門進行流程的基本設計,包括:
然後需要做的隻有:
但是呢,一個流程創建了,其執行情況如何我們還要提供列表查詢的功能,所以我們還需要制作該流程列表查詢的相關内容。
下面我們以一個最簡單的三環節申請審批流程為例來演示如何制作該流程。該流程簡述如下:
注:由于是演示,所有作業過程全部由manager來完成。以後我們會講解如何将各作業環節分配給不同的崗位
制作流程頁面在web文件中添加:
web sfDemo type div;
web sfDemoApply parent sfDemo type div cover=true;
web sfDemoApplyt1 parent sfDemoApply type table title='申請人填寫',width=900,alone=true;
with sfDemoApplyt1 row 0 col c0 web n type text text='類型:',width=100,extra=[{'color':'red','text':'*'}];
with sfDemoApplyt1 row 0 col c1 web n bind demoType type input verify=[len>0] width=200,logChangedDisp='類型';
with sfDemoApplyt1 row 0 col c2 web n type text text='名稱:',width=100,extra=[{'color':'red','text':'*'}];
with sfDemoApplyt1 row 0 col c3 web n bind demoName type input verify=[len>0] width=200,logChangedDisp='名稱';
web sfDemoApplyt2 parent sfDemoApply type table title='申請人說明',width=900,forDisp=false,alone=true;
with sfDemoApplyt2 row 0 col c0 web n type text text='意見:',width=100;
with sfDemoApplyt2 row 0 col c1 web n bind applySuggestion type textarea width=800,logChangedDisp='意見';
with sfDemoApplyt2 row 1 col c0 web n type text text='簽發時間:',width=100;
with sfDemoApplyt2 row 1 col c1 web n bind applyDate type text width=200;
with sfDemoApplyt2 row 1 col c2 web n type text text='簽發人:',width=100;
with sfDemoApplyt2 row 1 col c3 web n bind applyBy type text width=200;
with sfDemoApplyt2 row 2 col c0 web n type button width=80,text='确認',needVerify=['demoType','demoName'],motion=cmd,
demand=simpleFlowDual,onlyOnce=false,delay=2000,
params={'flowName':'sfDemo','nodeName':'demoApply','active':'accept','signBy':'applyBy','signDate':'applyDate'};
web sfDemoConfirm parent sfDemo type div cover=true;
web sfDemoConfirmt1 parent sfDemoConfirm type table title='審核',width=900,alone=true;
with sfDemoConfirmt1 row 0 col c0 web n type text text='意見:',width=100;
with sfDemoConfirmt1 row 0 col c1 web n bind confirmSuggestion type textarea width=800,logChangedDisp='意見';
with sfDemoConfirmt1 row 1 col c0 web n type text text='簽發時間:',width=100;
with sfDemoConfirmt1 row 1 col c1 web n bind confirmDate type text width=200;
with sfDemoConfirmt1 row 1 col c2 web n type text text='簽發人:',width=100;
with sfDemoConfirmt1 row 1 col c3 web n bind confirmBy type text width=200;
with sfDemoConfirmt1 row 2 col c0 web n type button width=80,text='同意',motion=cmd,
demand=simpleFlowDual,params={'flowName':'sfDemo','nodeName':'demoConfirm','active':'accept','signBy':'confirmBy','signDate':'confirmDate'};
with sfDemoConfirmt1 row 2 col c2 web n type button width=80,text='拒絕',motion=cmd,
demand=simpleFlowDual,params={'flowName':'sfDemo','nodeName':'demoConfirm','active':'reject','signBy':'ConfirmBy','signDate':'confirmDate'};
web sfDemoApprove parent sfDemo type div cover=true;
web sfDemoApprovet1 parent sfDemoApprove type table title='審批',width=900,alone=true;
with sfDemoApprovet1 row 0 col c0 web n type text text='意見:',width=100;
with sfDemoApprovet1 row 0 col c1 web n bind approveSuggestion type textarea width=800,logChangedDisp='意見';
with sfDemoApprovet1 row 1 col c0 web n type text text='簽發時間:',width=100;
with sfDemoApprovet1 row 1 col c1 web n bind approveDate type text width=200;
with sfDemoApprovet1 row 1 col c2 web n type text text='簽發人:',width=100;
with sfDemoApprovet1 row 1 col c3 web n bind approveBy type text width=200;
with sfDemoApprovet1 row 2 col c0 web n type button width=80,text='同意',motion=cmd,demand=simpleFlowDual,
params={'flowName':'sfDemo','nodeName':'demoApprove','active':'accept','signBy':'approveBy','signDate':'approveDate'};
with sfDemoApprovet1 row 2 col c1 web n type button width=80,text='拒絕',motion=cmd,demand=simpleFlowDual,
params={'flowName':'sfDemo','nodeName':'demoApprove','active':'reject','signBy':'approveBy','signDate':'approveDate'};
with sfDemoApprovet1 row 2 col c3 web n bind exportAdditional type combobox width=80,values=[{'value':'demoApply','text':'退回申請人'},{'value':'demoConfirm','text':'退回審核'}],notExtant=true;
制作好的界面顯示出來是如下的模樣:
注:先不要管【日志、數據變動等】這三個工具條,以後我們會演示的
我們所定義的這個界面:sfDemo。其包括了三個子div【組容器】:
大家先不要管這個界面的細節,等完成了整個流程後,再對照着web界面的參考:web界面定義、web控件的一般定義、web界面中各控件的定義來搞明白界面的定義。
對各節點的邏輯處理分别編程然後我們打開capa.py,在demo1類的定義中添加:
#申請節點的處理
#request在這裡的參數表是:流程名、節點名、dual【指定為相應節點的邏輯處理】
@myModule.request('sfDemo', 'demoApply', 'dual')
def sfDemoApply_dual(self, db, ctx,ca,active):
#ca是記錄當前流程各種狀态與數據的對象事件,也是
#active是當前用戶的操作,點擊同意按鈕是accept,點擊否決按鈕是reject
ca.Type=self.getInput('demoType')
ca.Name=self.getInput('demoName')
ca.Purpose='demo'
ca.State=0
ca.CreatorID=ctx.getCaller().id()
ca.Info.set('creator',ctx.getCaller().abbreviation())
#提示并關閉界面
return True
#流程結束後的處理
#虛拟節點end指示是整個流程結束後的邏輯處理
@myModule.request('sfDemo', 'end', 'dual')
def sprjend_dual(self, db, ctx,ca,active):
#結束
ca.State=1
db.update(ca,'State')
#提示并關閉界面
return True
注:jxTMS在每個節點都執行了大量的默認操作,而審核與審批兩節點并沒有需要執行的額外處理,所以省略
編寫流程jxTMS中的簡易流程是用文本進行定義的,大家在capa.py,在demo1類的定義中添加:
#指定本功能模塊的默認顯示界面【jxTMS在自動指派任務時需要此默認界面】
def viewWebInterface(self):
return 'sfDemo'
#定義一個名為sfDemo的簡易流程
@myModule.simpleFlow('sfDemo')
def sfDemo():
'''
web sfDemo;
node demoApply 申請 web sfDemoApply ;
node demoConfirm 審核 web sfDemoConfirm needRole manager notJump;
node demoApprove 審批 web sfDemoApprove needRole manager notJump;
'''
pass
簡易流程的定義,是用myModule.simpleFlow(流程名)修飾的一個空函數的doc中進行定義的,大家可參考簡易流程中的說明來理解。概要的說,就是定義了一個sfDemo的流程,該流程的web界面也叫sfDemo,其包括了三個節點:申請、審核、審批,指定了每個節點可操作的子界面,後兩個節點都指派給manager角色的用戶來執行【而且不可跳過】。
增加流程創建的入口在op.py中添加:
@biz.Motion('demo.demo1','disp','sfDemo')
@biz.OPDescr
def op1(json):
json.setShortcut('演示'.decode('utf-8'),'發起申請'.decode('utf-8'))
json.setParam('notCover','sfDemoApply')
就是一個顯示流程界面的sfDemo,唯一不同的是有一個notCover的參數被設置為了申請節點所對應的界面:sfDemoApply。意思就是不要遮擋sfDemoApply界面,什麼意思呢?!等流程建好,大家多執行幾次就會理解了的,所以現在先不管了。
我們将web、op.py、capa.py等文件按用sftp管理jxTMS的代碼所述更新到/home/tms/codeDefine/demo/demo/demo1目錄中。
然後執行一次熱機刷新,由于快捷欄中的入口有變化,所以先要退出登錄,再次登錄後點擊快捷欄中的【演示->發起申請】,然後看看顯示出來的界面是什麼樣的。
遮罩大家會看到【發起申請】這個界面,自上而下由四張表組成:申請人填寫、申請人說明、審核、審批。在過了一小會之後,審核、審批兩表會突然變灰了,而且裡面的控件也無法再被點擊到。
大家請回過頭來,看一下sfDemo界面的定義,會發現sfDemoApply【參考simpleFlow('sfDemo')的定義,可以觀察到其是demoApply即申請節點所對應的web操作界面】、sfDemoConfirm【參考simpleFlow('sfDemo')的定義,可以觀察到其是demoConfirm即審核節點所對應的web操作界面】、sfDemoApprove【參考simpleFlow('sfDemo')的定義,可以觀察到其是demoApprove即審批節點所對應的web操作界面】都有一個之前從沒看到過的屬性:
cover=true
大家看一下組控件中對cover屬性的講解。當一個組容器設置了cover=true後,其在界面初始化與數據裝定之後,就會被用一個罩子遮擋起來,從而使得用戶能看到其内容但無法對其包含的控件進行操作。
大家再看一下op.py文件中【發起申請】這個入口,我們設置了一個參數:
json.setParam('notCover','sfDemoApply')
意思也很明顯,就是在打開通過【發起申請】這個入口打開sfDemo界面後,取消sfDemoApply界面的遮擋,也就是将sfDemoApply的遮罩關閉掉,從而使得用戶可以填寫申請信息以及進行說明,并點擊【确認】按鈕。
注:大家有沒有想到一種攻擊方式:在某種情況下【如請款被退回重新填寫】,通過按【tab】鍵将焦點移動到總經理的意見那一欄後,輸入同意支付一百萬,然後再按【tab】鍵将焦點移動到總經理對應的同意按鈕上,敲擊【回車】,如果财務人員未注意簽發人或簽發人正好同名【這就是jxTMS為什麼要用簡稱的原因】,就完成了一次非法的申請審批。針對此種情況,jxTMS中禁用了【tab】鍵,用戶是無法通過按動【tab】鍵來切換焦點的
取消【tab】鍵、加遮罩、取消遮罩,這豈不是很麻煩啊,jxTMS為什麼要這麼做呢?!大家看一下sfDemo界面的web定義,是不是覺得的很麻煩,可sfDemo流程隻有簡單的三個步驟,而且每個步驟都非常簡單。那麼大家想一下,如果是一個複雜的流程,其界面是不是就會非常的麻煩?!
那麼,如果不使用遮罩的話,任何一個節點就必須同時設計一個寫界面、一個隻顯示的讀界面,然後在流轉到自己的那個節點的時候,其它節點的界面都是讀界面、自己的這個節點的界面是寫界面。而這樣一來,界面開發的工作量就大了一倍,管控隻會更複雜。因為,如果是其他人查看呢?如果是自己已經做完了想再看看呢?
注:另一種實現方案是将該節點所對應的組容器中的所有子控件禁用掉,但jxTMS的控件都是動态生成的、界面管理本身就非常複雜,如果采用本方法,會導緻界面管理進一步複雜化,這對于隻有一個開發者的jxTMS來說,筆者實在不想出現該禁用又被錯誤的使能,或是該使能的時候卻沒有正确的使能
開發過程可能需要修改流程界面啊,那麼如果有修改,則還必須保持兩個界面的同步。所以呢,思來想去,筆者認為簡單才是硬道理,jxTMS的實現再複雜,隻要測試完備就不怕,換來的是開發者成本、風險的雙雙下降,還是一半以上的下降,最終還是采用了遮擋的方案。
結語簡易流程比較複雜,而本節内容就已經較多了,所以就不再繼續,大家對照顯示出來的【發起申請】界面,仔細回顧一下本節内容,這樣可以加深對簡易流程的理解。我們下一節開始講解簡易流程的流轉。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!