工作流Activiti OA低代碼平臺-bpmnjs源碼分析
低代碼平臺第二步:bpmnjs源碼分析
帶您感受下低代碼的功能實現(xiàn)案例,通過簡單的幾步操作,就能完成一個模塊的CRUD操作。
更多技術(shù)文檔請點擊查看:bpmnjs源碼分析 · 語雀
預覽地址:青鋒后臺管理系統(tǒng)
核心代碼位置-package
bpmn核心代碼全部在package包下面,如下圖的位置:
流程設(shè)計器模塊process-designer
頁面對應(yīng)內(nèi)容
頁面布局代碼
<div class="my-process-designer"> <div class="my-process-designer__header"> <slot name="control-header"></slot> <template v-if="!$slots['control-header']"> <el-button-group key="file-control"> <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-folder-opened" @click="$refs.refFile.click()" >打開文件</el-button > <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-folder-download" @click="save()" >保存</el-button > <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-folder-download" @click="saveAndDeploy()" >保存并部署</el-button > <el-tooltip effect="light"> <div slot="content"> <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsXml()" >下載為XML文件</el-button > <br /> <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsSVG()" >下載為SVG文件</el-button > <br /> <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()" >下載為BPMN文件</el-button > </div> <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-download" @click="deploy()" >下載文件</el-button > </el-tooltip> <el-tooltip effect="light"> <div slot="content"> <el-button :size="headerButtonSize" type="text" @click="previewProcessXML" >預覽XML</el-button > <br /> <el-button :size="headerButtonSize" type="text" @click="previewProcessJson" >預覽JSON</el-button > </div> <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-view" >預覽</el-button > </el-tooltip> <el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '退出模擬' : '開啟模擬'" > <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-cpu" @click="processSimulation" > 模擬 </el-button> </el-tooltip> </el-button-group> <el-button-group key="align-control"> <el-tooltip effect="light" content="向左對齊"> <el-button :size="headerButtonSize" class="align align-left" icon="el-icon-s-data" @click="elementsAlign('left')" /> </el-tooltip> <el-tooltip effect="light" content="向右對齊"> <el-button :size="headerButtonSize" class="align align-right" icon="el-icon-s-data" @click="elementsAlign('right')" /> </el-tooltip> <el-tooltip effect="light" content="向上對齊"> <el-button :size="headerButtonSize" class="align align-top" icon="el-icon-s-data" @click="elementsAlign('top')" /> </el-tooltip> <el-tooltip effect="light" content="向下對齊"> <el-button :size="headerButtonSize" class="align align-bottom" icon="el-icon-s-data" @click="elementsAlign('bottom')" /> </el-tooltip> <el-tooltip effect="light" content="水平居中"> <el-button :size="headerButtonSize" class="align align-center" icon="el-icon-s-data" @click="elementsAlign('center')" /> </el-tooltip> <el-tooltip effect="light" content="垂直居中"> <el-button :size="headerButtonSize" class="align align-middle" icon="el-icon-s-data" @click="elementsAlign('middle')" /> </el-tooltip> </el-button-group> <el-button-group key="scale-control"> <el-tooltip effect="light" content="縮小視圖"> <el-button :size="headerButtonSize" :disabled="defaultZoom < 0.2" icon="el-icon-zoom-out" @click="processZoomOut()" /> </el-tooltip> <el-button :size="headerButtonSize">{{ Math.floor(this.defaultZoom * 10 * 10) "%" }}</el-button> <el-tooltip effect="light" content="放大視圖"> <el-button :size="headerButtonSize" :disabled="defaultZoom > 4" icon="el-icon-zoom-in" @click="processZoomIn()" /> </el-tooltip> <el-tooltip effect="light" content="重置視圖并居中"> <el-button :size="headerButtonSize" icon="el-icon-c-scale-to-original" @click="processReZoom()" /> </el-tooltip> </el-button-group> <el-button-group key="stack-control"> <el-tooltip effect="light" content="撤銷"> <el-button :size="headerButtonSize" :disabled="!revocable" icon="el-icon-refresh-left" @click="processUndo()" /> </el-tooltip> <el-tooltip effect="light" content="恢復"> <el-button :size="headerButtonSize" :disabled="!recoverable" icon="el-icon-refresh-right" @click="processRedo()" /> </el-tooltip> <el-tooltip effect="light" content="重新繪制"> <el-button :size="headerButtonSize" icon="el-icon-refresh" @click="processRestart" /> </el-tooltip> </el-button-group> </template> <!-- 用于打開本地文件--> <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" /> </div> <div class="my-process-designer__container"> <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> </div> <el-dialog title="預覽" width="60%" :visible.sync="previewModelVisible" append-to-body destroy-on-close > <highlightjs :language="previewType" :code="previewResult" /> </el-dialog> </div>
設(shè)計器控制面板process-panel
代碼重構(gòu)基礎(chǔ)組件refactor
包含了基礎(chǔ)組件、流程表達式、流程表單、監(jiān)聽器、多實例、其他任務(wù)、參數(shù)設(shè)置、信號消息、任務(wù)管理等等基礎(chǔ)組件。
了解了代碼重構(gòu)的基礎(chǔ)組件,在實際業(yè)務(wù)中可以根據(jù)自己的需求進行修改。
任務(wù)組件-改造講解
在工作流的設(shè)計器中,我們對任務(wù)組件做了改造,由于之前的任務(wù)只能選擇固定的人員或者組織,并沒有和實際的數(shù)據(jù)庫進行關(guān)聯(lián),無法與系統(tǒng)的用戶打通,導致設(shè)計流程任務(wù)審批節(jié)點無法動態(tài)配置。
為了打通工作流與系統(tǒng)用戶、組織之間的壁壘,我們通過對流程設(shè)計器的改造,重新指定了任務(wù)組件。
靜態(tài)用戶任務(wù)
基礎(chǔ)業(yè)務(wù)介紹
1、選擇靜態(tài)分配后,我們可以選擇用戶和組織信息。
2、選擇用戶和組織的公共組件可以查看單選用戶、單選組織、多選用戶、多選組織的案例。
功能代碼介紹
創(chuàng)建候選靜態(tài)候選用戶表單,可以動態(tài)選擇用戶或者組織。
<el-form-item label="候選用戶" v-show="userTaskForm.type == '0'"> <el-input type="textarea" v-model="userTaskForm.candidateUsersName" clearable disabled /> <el-button @click="selectCandidateUsers()" type="primary" style="margin-top: 8px" >選擇</el-button > <el-button @click="clearCandidateUsers()" type="primary" style="margin-top: 8px" >清空</el-button > </el-form-item> <el-form-item label="候選分組" v-show="userTaskForm.type == '0'"> <el-input type="textarea" v-model="userTaskForm.candidateGroupsName" clearable disabled /> <el-button @click="selectCandidateGroups()" type="primary" style="margin-top: 8px" >選擇</el-button > <el-button @click="selectCandidateGroups()" type="primary" style="margin-top: 8px" >清空</el-button > </el-form-item>
重新設(shè)置用戶表單
resetTaskForm() { this.$set(this.userTaskForm, "model_id", this.bpmnElement.parent.id); this.$set(this.userTaskForm, "node_key", this.bpmnElement?.id); for (let key in this.defaultTaskForm) { let value; if (key === "candidateUsers" || key === "candidateGroups") { value = this.bpmnElement?.businessObject[key] ? this.bpmnElement.businessObject[key].split(",") : []; if (value != "") { findUserOrOrganizeNames({ type: key, ids: value.join(",") }).then( (response) => { console.log(response); if (key === "candidateUsers") { this.$set( this.userTaskForm, "candidateUsers", response.data.data.myIds ); this.$set( this.userTaskForm, "candidateUsersName", response.data.data.myNames ); } else if (key === "candidateGroups") { this.$set( this.userTaskForm, "candidateGroups", response.data.data.myIds ); this.$set( this.userTaskForm, "candidateGroupsName", response.data.data.myNames ); } } ); } else { if (key === "candidateUsers") { this.$set(this.userTaskForm, "candidateUsers", ""); this.$set(this.userTaskForm, "candidateUsersName", ""); } else if (key === "candidateGroups") { this.$set(this.userTaskForm, "candidateGroups", ""); this.$set(this.userTaskForm, "candidateGroupsName", ""); } } } else if (key === "assignee") { value = this.bpmnElement?.businessObject[key] || this.defaultTaskForm[key]; if (value != "") { findUserOrOrganizeNames({ type: key, ids: value }).then( (response) => { this.$set( this.userTaskForm, "assignee", response.data.data.myIds ); this.$set( this.userTaskForm, "assigneeName", response.data.data.myNames ); } ); } else { this.$set(this.userTaskForm, "assignee", ""); this.$set(this.userTaskForm, "assigneeName", ""); } } else { value = this.bpmnElement?.businessObject[key] || this.defaultTaskForm[key]; this.$set(this.userTaskForm, key, value); } console.log(key "|" value); }
動態(tài)用戶任務(wù)
<el-select v-model="userTaskForm.assign_mode" placeholder="請選擇指定類型" @change="selectAssignMode()" > <el-option label="所有人員中選擇(根據(jù)組織選擇)" value="0"></el-option> <el-option label="組織選擇(指定組織父節(jié)點)" value="1"></el-option> <el-option label="用戶組選擇(選擇指定組內(nèi)成員)" value="2"></el-option> <el-option label="發(fā)起人本組織選擇" value="3"></el-option> <el-option label="部門經(jīng)理" value="4"></el-option> <el-option label="上級領(lǐng)導" value="5"></el-option> <el-option label="分管領(lǐng)導" value="6"></el-option> <el-option label="流程發(fā)起人" value="7"></el-option> <el-option label="指定范圍選擇" value="8"></el-option> <el-option label="代理人(選擇單用戶)" value="9"></el-option> <el-option label=" 候選人(選擇多用戶)" value="10"></el-option> <el-option label="候選組(選擇多組織)" value="11"></el-option> </el-select>
節(jié)點解析
所有人員中選擇(根據(jù)組織選擇)
可以從系統(tǒng)人員中,選擇節(jié)點需要審批的人員,可以指定一個人或者多個人,如果指定一個,則這個人就是本節(jié)點的辦理人,如果指定了多個人,則流程下發(fā)時由上一級用戶選擇。
組織選擇(指定組織父節(jié)點)
指定組織父節(jié)點后,節(jié)點辦理人為當前組織下的人員,上一節(jié)點用戶發(fā)起流程可以從改組織下面所有的人員中選擇下級節(jié)點的辦理人。
用戶組選擇(選擇指定組內(nèi)成員)
指定用戶分組,節(jié)點辦理人為當前用戶組下的人員,上一節(jié)點用戶發(fā)起流程可以從該分組下面所有的人員中選擇下級節(jié)點的辦理人。
發(fā)起人本組織選擇
節(jié)點辦理人為當前用戶同組織下的人員,上一節(jié)點用戶發(fā)起流程可以從發(fā)起人同組織下面所有的人員中選擇下級節(jié)點的辦理人。
部門經(jīng)理
在人員中設(shè)置人員的部門經(jīng)理,流程發(fā)起人員發(fā)起的流程由發(fā)起人的部門經(jīng)理進行審批。
上級領(lǐng)導
在人員中設(shè)置人員的上級領(lǐng)導,流程發(fā)起人員發(fā)起的流程由發(fā)起人的上級領(lǐng)導進行審批。
分管領(lǐng)導
在人員中設(shè)置人員的分管領(lǐng)導,流程發(fā)起人員發(fā)起的流程由發(fā)起人的分管領(lǐng)導進行審批。
流程發(fā)起人
用戶節(jié)點由流程發(fā)起人審批。
指定范圍選擇
指定范圍選擇-可以選擇一個用戶集合,用戶節(jié)點審核時,由上一節(jié)點辦理人指定用戶節(jié)點具體的辦理人。
代理人(選擇單用戶)
如果節(jié)點設(shè)置為代理人,則可以指定當前審核代理人,制定后節(jié)點由設(shè)置的代理人進行審核。
候選人(選擇多用戶)
候選人,可以設(shè)置多為候選人,流程審批節(jié)點會同時給多為候選人發(fā)起審批任務(wù),誰先認領(lǐng)誰審批,由第一個認領(lǐng)的候選人審批。
候選組(選擇多組織)
候選組的概念同候選人,流程審批節(jié)點會同時給多為候選組下的所有人發(fā)起審批任務(wù),誰先認領(lǐng)誰審批,由第一個認領(lǐng)的候選人審批。
在activiti7中,拋棄了候選組審批的功能,在activiti5 和activiti6中依然保持著候選組的審批模式。
功能代碼分析
下面是具體的核心方法源碼,完成的源碼需要在代碼中進行查看和分析。
cellclick(row) { var key = this.checktype; let taskAttr = Object.create(null); this.userTaskForm.assignee = row.id; this.dialogVisible = false; if (key === "candidateUsers" || key === "candidateGroups") { taskAttr[key] = this.userTaskForm[key] && this.userTaskForm[key].length ? this.userTaskForm[key].join() : null; } else { taskAttr[key] = this.userTaskForm[key] || null; console.log(taskAttr[key]); } window.bpmnInstances.modeling.updateProperties( this.bpmnElement, taskAttr ); }, clearAssignee(key) { let value; let taskAttr = Object.create(null); if (key === "candidateUsers" || key === "candidateGroups") { console.log(key); } else { taskAttr[key] = ""; //this.bpmnElement?.businessObject[key] || this.defaultTaskForm[key]; } this.$set(this.userTaskForm, key, ""); window.bpmnInstances.modeling.updateProperties( this.bpmnElement, taskAttr ); }, //初始化initGroup initGroup() { findGroupList({}).then((response) => { this.groupList = response.data.data; }); }, //選擇辦理人 selectAssignee() { this.dialog(SelectOneUser, "assignee", { user_id: this.userTaskForm.assignee, user_name: this.userTaskForm.assigneeName, }); }, //選擇候選人 selectCandidateUsers() { this.dialog(SelectMoreUser, "candidateUsers", { user_ids: this.userTaskForm.candidateUsers, user_names: this.userTaskForm.candidateUsersName, }); }, //選擇候選組 selectCandidateGroups() { this.dialog(SelectMoreOrganize, "candidateGroups", { organize_ids: this.userTaskForm.candidateGroups, organize_names: this.userTaskForm.candidateGroupsName, }); }, //選擇單組織 selectOneOrganize() { this.dialog(SelectOneOrganize, "oneOrganize", { organize_id: this.userTaskForm.organize_id, organize_name: this.userTaskForm.organize_name, }); }, //選擇多用戶 selectMoreUser() { this.dialog(SelectMoreUser, "moreUser", { user_ids: this.userTaskForm.user_ids, user_names: this.userTaskForm.user_names, }); }, //選擇單用戶 selectOneUser() { this.dialog(SelectOneUser, "oneUser", { user_id: this.userTaskForm.user_id, user_name: this.userTaskForm.user_name, }); }, //選擇多組織 selectMoreOrganize() { this.dialog(SelectMoreOrganize, "moreOrganize", { organize_ids: this.userTaskForm.organize_ids, organize_names: this.userTaskForm.organize_names, }); }, //選擇用戶組織彈框 dialog(component, fileType, record) { console.log(component, fileType, record); const that = this; this.$dialog( component, // component props { record, on: { ok() { console.log("ok 回調(diào)"); }, cancel() { console.log("cancel 回調(diào)"); }, close() { console.log("modal close 回調(diào)"); }, initValue(value, type) { if (type == "1") { if (fileType == "assignee") { that.userTaskForm.assignee = value.split(":")[0]; that.userTaskForm.assigneeName = value.split(":")[1]; //更新文檔參數(shù) that.updateActivitiProperties( "assignee", value.split(":")[0] ); that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } else if (fileType == "oneUser") { that.userTaskForm.user_id = value.split(":")[0]; that.userTaskForm.user_name = value.split(":")[1]; that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } } else if (type == "2") { if (fileType == "candidateUsers") { that.userTaskForm.candidateUsers = value.split(":")[0]; that.userTaskForm.candidateUsersName = value.split(":")[1]; //更新文檔參數(shù) that.updateActivitiProperties( "candidateUsers", value.split(":")[0] ); that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } else if (fileType == "moreUser") { that.userTaskForm.user_ids = value.split(":")[0]; that.userTaskForm.user_names = value.split(":")[1]; that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } } else if (type == "3") { if (fileType == "oneOrganize") { that.userTaskForm.organize_id = value.split(":")[0]; that.userTaskForm.organize_name = value.split(":")[1]; that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } } else if (type == "4") { if (fileType == "candidateGroups") { that.userTaskForm.candidateGroups = value.split(":")[0]; that.userTaskForm.candidateGroupsName = value.split(":")[1]; //更新文檔參數(shù) that.updateActivitiProperties( "candidateGroups", value.split(":")[0] ); that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } else if (fileType == "moreOrganize") { that.userTaskForm.organize_ids = value.split(":")[0]; that.userTaskForm.organize_names = value.split(":")[1]; that.saveAssignMode( value.split(":")[0] "#" value.split(":")[1] ); } } that.$forceUpdate(); }, }, }, // modal props { title: "操作", width: 800, height: 500, centered: true, maskClosable: false, okText: "確認", cancelText: "取消", } ); }, updateActivitiProperties(key, value) { let taskAttr = {}; taskAttr[key] = value; window.bpmnInstances.modeling.updateProperties( this.bpmnElement, taskAttr ); }, selectAssignMode() { let assign_mode = this.userTaskForm.assign_mode; let assign_content = this.userTaskForm.assign_content; if ( assign_mode == "0" || assign_mode == "3" || assign_mode == "4" || assign_mode == "5" || assign_mode == "6" || assign_mode == "7" || (assign_mode == "2" && assign_content != "" && assign_content != undefined) ) { saveAssignment({ id: this.userTaskForm.node_id, type: this.userTaskForm.type, assign_mode: this.userTaskForm.assign_mode, assign_content: this.userTaskForm.assign_content, model_id: this.userTaskForm.model_id, node_key: this.userTaskForm.node_key, }).then((response) => { console.log(response); }); } }, saveAssignMode(assign_content) { saveAssignment({ id: this.userTaskForm.node_id, type: this.userTaskForm.type, assign_mode: this.userTaskForm.assign_mode, model_id: this.userTaskForm.model_id, node_key: this.userTaskForm.node_key, assign_content: assign_content, }).then((response) => { console.log(response); }); }, }, beforeDestroy() { this.bpmnElement = null; },};