工作流在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=" 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="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的流程设计器已经介绍完毕,要实现工作流机制需要针对自己的系统作表单设计和业务关联,相关的实现文档有很多,这里就不过多介绍了。目前功能还比较粗糙,优化完后我会对这些功能进行开源,感兴趣的可以后续关注。