工作流在oa和erp中十分常見,現有成熟的工作流通常是在客戶端實現的,web實現工作流的案例十分稀少。要實現web工作流必須要有強大的流程設計器,這里為大家介紹一款基於angular的流程控件,其功能十分強大,可直接開發在線流程設計工具。
流程設計效果如下:
該流程工具使用的是syncfusion旗下的angular組件開發,需要先安裝組件: @syncfusion/ej2-angular-diagrams
angular項目中添加demo-flow組件,在demo-flow目錄中添加script文件夾,script中粘貼diagram-common.js
准備就緒后開始布局流程頁面,布局代碼如下:
<script src="script/diagram-common.ts"></script> <div class="m-portlet m-portlet--mobile"> <div class="m-portlet__head"> <div class="m-portlet__head-caption"> <div class="m-portlet__head-title"> <h3 class="m-portlet__head-text"> 流程設計測試 </h3> </div> </div> </div> <div class="diagram-serialization" style="width: 100%;height: 10%"> <ejs-toolbar width="100%" (clicked)="onClicked($event)"> <e-items> <e-item text='New' tooltipText='New' prefixIcon='e-ddb-icons e-new'></e-item> <e-item type='Separator'></e-item> <e-item text='Save' tooltipText='Save' prefixIcon='e-ddb-icons e-save'></e-item> <e-item type='Separator'></e-item> <e-item text='Load' tooltipText='Load' prefixIcon='e-ddb-icons e-open'></e-item> <e-item type='Separator'></e-item> </e-items> </ejs-toolbar> </div> <div style="width:100%;height: 80%"> <div id="palette-space" class="sb-mobile-palette"> <ejs-symbolpalette id="symbolpalette" [enableAnimation]='enableAnimation' [expandMode]='expandMode' [palettes]='palettes' (created)='create($event)' width="100%" height="700px" [symbolHeight]=60 [symbolWidth]=60 [symbolMargin]='symbolMargin' [getSymbolInfo]='getSymbolInfo' [getNodeDefaults]='getSymbolDefaults'> </ejs-symbolpalette> </div> <div id="diagram-space" class="sb-mobile-diagram"> <div class="content-wrapper"> <ejs-diagram #diagram id="diagram" width="100%" height="700px" [snapSettings]='snapSettings' [getConnectorDefaults]='getConnectorDefaults' (doubleClick)="doubleClick($event)"> <e-nodes> <e-node id='Start' [height]=50 [width]=100 [shape]='terminator' [offsetX]=250 [offsetY]=80 [style]='terminatorStyle'> <e-node-annotations> <e-node-annotation content='Start'> </e-node-annotation> </e-node-annotations> </e-node> <e-node id='Alarm' [height]=50 [width]=100 [shape]='process' [offsetX]=250 [offsetY]=160 [style]='processStyle'> <e-node-annotations> <e-node-annotation content='Alarm Rings'> </e-node-annotation> </e-node-annotations> </e-node> <e-node id='Ready' [height]=50 [width]=100 [shape]='decision' [offsetX]=250 [offsetY]=240 [style]='decisionStyle'> <e-node-annotations> <e-node-annotation content='Ready to Get Up?'> </e-node-annotation> </e-node-annotations> </e-node> <e-node id='Climb' [height]=50 [width]=100 [shape]='process' [offsetX]=250 [offsetY]=330 [style]='processStyle'> <e-node-annotations> <e-node-annotation content='Climb Out of Bed'> </e-node-annotation> </e-node-annotations> </e-node> <e-node id='End' [height]=50 [width]=100 [shape]='terminator' [offsetX]=250 [offsetY]=430 [style]='terminatorStyle'> <e-node-annotations> <e-node-annotation content='End'> </e-node-annotation> </e-node-annotations> </e-node> <e-node id='Relay' [height]=50 [width]=100 [shape]='delay' [offsetX]=450 [offsetY]=160 [style]='delayStyle'> <e-node-annotations> <e-node-annotation content='Relay'> </e-node-annotation> </e-node-annotations> </e-node> <e-node id='Hit' [height]=50 [width]=100 [shape]='process' [offsetX]=450 [offsetY]=240 [style]='processStyle'> <e-node-annotations> <e-node-annotation content='Hit Snooze Button'> </e-node-annotation> </e-node-annotations> </e-node> </e-nodes> <e-connectors> <e-connector id='connector1' sourceID='Start' targetID='Alarm'> </e-connector> <e-connector id='connector2' sourceID='Alarm' targetID='Ready'> </e-connector> <e-connector id='connector3' sourceID='Ready' targetID='Climb'> <e-connector-annotations> <e-connector-annotation content='Yes' [style]='connectorTextStyle'> </e-connector-annotation> </e-connector-annotations> </e-connector> <e-connector id='connector4' sourceID='Climb' targetID='End'> </e-connector> <e-connector id='connector5' sourceID='Ready' targetID='Hit'> <e-connector-annotations> <e-connector-annotation content='No' [style]='connectorTextStyle'> </e-connector-annotation> </e-connector-annotations> </e-connector> <e-connector id='connector6' sourceID='Hit' targetID='Relay'> </e-connector> <e-connector id='connector7' sourceID='Relay' targetID='Alarm'> </e-connector> </e-connectors> </ejs-diagram> </div> <ejs-uploader #defaultupload id='fileupload' (success)='onUploadSuccess($event)' [asyncSettings]='asyncSettings'></ejs-uploader> </div> </div> <div bsModal #createOrEditModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="createOrEditModal" aria-hidden="true" [config]="{backdrop:'static'}"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title"> <span>修改節點</span> </h4> <button type="button" class="close" (click)="close()" attr.aria-label="關閉"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="form-group"> <label>Id</label> <input type="text" class="form-control" [(ngModel)]="selectItem.id" readonly> </div> <div class="form-group" [hidden]="selectItem.type!='node'"> <label>名稱*</label> <input type="text" class="form-control" [(ngModel)]="selectItem.name"> </div> <div class="form-group" [hidden]="selectItem.type!='node'"> <label>執行權限</label> <select class="form-control" [formControl]="selectedState"> <option value="">選擇執行權限</option> <option value="1">所有人</option> <option value="2">指定角色</option> </select> </div> <div class="form-group" [hidden]="selectItem.type!='node' || selectedState.value!='2'"> <label class="m-checkbox"> <input type="checkbox"> 部門主管 <span></span> </label> <label class="m-checkbox"> <input type="checkbox"> 部門經理 <span></span> </label> </div> <div class="form-group" [hidden]="selectItem.type!='connector'"> <label>執行條件</label><br> <p-radioButton name="radGroup" value="yes" label="通過" [(ngModel)]="condition"></p-radioButton> <p-radioButton name="radGroup" value="no" label="不通過" [(ngModel)]="condition"></p-radioButton> <p-radioButton name="radGroup" value="scope" label="執行范圍" [(ngModel)]="condition"></p-radioButton> </div> <div class="form-row" [hidden]="selectItem.type!='connector' || condition!='scope'"> <div class="form-group col-md-4"> <label>運算符</label> <select class="form-control" [formControl]="operator"> <option value="">選擇運算符</option> <option value="大於">大於</option> <option value="小於">小於</option> <option value="等於">等於</option> </select> </div> <div class="form-group col-md-4"> <label class="">值</label> <div class=""> <input type="number" [(ngModel)]="scopeValue" class="form-control" placeholder="值"> </div> </div> <div class="form-group col-md-4"> <label>單位</label> <select class="form-control" [formControl]="unit"> <option value="">選擇單位</option> <option value="天">天</option> <option value="小時">小時</option> <option value="元">元</option> </select> </div> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" (click)="close()">關閉</button> <button type="button" class="btn btn-primary" (click)="save()"> <i class="fa fa-save"></i><span>保存</span> </button> </div> </div> </div> </div> </div>
流程頁面模板中加入了雙擊節點和連接器的事件,如果要開發工作流必須要對事件作處理,僅開發流程設計器則需要取消雙擊事件以免影響使用。
事件處理如下:
public doubleClick(args: IDoubleClickEventArgs): void { var node = this.diagram.selectedItems.nodes[0]; var connector = this.diagram.selectedItems.connectors[0]; if (node) { this.selectItem.id = node.id; this.selectItem.name = node.annotations[0].content; this.selectItem.type = 'node'; this.modal.show(); } if (connector) { this.selectItem.id = connector.id; this.selectItem.name = connector.annotations[0].content; this.selectItem.type = 'connector'; this.modal.show(); } } save(): void { if (this.diagram.selectedItems.nodes[0]) { this.diagram.selectedItems.nodes[0].annotations[0].content = this.selectItem.name; this.close(); } if (this.diagram.selectedItems.connectors[0]) { if(this.condition=='yes'){ this.diagram.selectedItems.connectors[0].annotations[0].content="通過"; } if(this.condition=='no'){ this.diagram.selectedItems.connectors[0].annotations[0].content="不通過"; } if(this.condition=='scope'){ this.diagram.selectedItems.connectors[0].annotations[0].content=this.operator.value+' '+this.scopeValue+' '+this.unit.value; } this.close(); } } close(): void { this.modal.hide(); }
流程器顯示和雙擊功能如下:
到這里angular的流程設計器已經介紹完畢,要實現工作流機制需要針對自己的系統作表單設計和業務關聯,相關的實現文檔有很多,這里就不過多介紹了。目前功能還比較粗糙,優化完后我會對這些功能進行開源,感興趣的可以后續關注。