工作流Activiti OA低代碼平臺-bpmnjs源碼分析(工作流引擎activiti表結(jié)構(gòu)和代碼詳解)
低代碼平臺第二步:bpmnjs源碼分析
帶您感受下低代碼的功能實現(xiàn)案例,通過簡單的幾步操作,就能完成一個模塊的CRUD操作。
更多技術(shù)文檔請點擊查看:bpmnjs源碼分析 · 語雀
預(yù)覽地址:青鋒后臺管理系統(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"                >預(yù)覽XML</el-button              >              <br />              <el-button                :size="headerButtonSize"                type="text"                @click="previewProcessJson"                >預(yù)覽JSON</el-button              >            </div>            <el-button              :size="headerButtonSize"              :type="headerButtonType"              icon="el-icon-view"              >預(yù)覽</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="恢復(fù)">            <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="預(yù)覽"      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)的用戶打通,導(dǎo)致設(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)導(dǎo)" value="5"></el-option>        <el-option label="分管領(lǐng)導(dǎo)" 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é)點辦理人為當(dāng)前組織下的人員,上一節(jié)點用戶發(fā)起流程可以從改組織下面所有的人員中選擇下級節(jié)點的辦理人。
用戶組選擇(選擇指定組內(nèi)成員)
指定用戶分組,節(jié)點辦理人為當(dāng)前用戶組下的人員,上一節(jié)點用戶發(fā)起流程可以從該分組下面所有的人員中選擇下級節(jié)點的辦理人。
發(fā)起人本組織選擇
節(jié)點辦理人為當(dāng)前用戶同組織下的人員,上一節(jié)點用戶發(fā)起流程可以從發(fā)起人同組織下面所有的人員中選擇下級節(jié)點的辦理人。
部門經(jīng)理
在人員中設(shè)置人員的部門經(jīng)理,流程發(fā)起人員發(fā)起的流程由發(fā)起人的部門經(jīng)理進行審批。
上級領(lǐng)導(dǎo)
在人員中設(shè)置人員的上級領(lǐng)導(dǎo),流程發(fā)起人員發(fā)起的流程由發(fā)起人的上級領(lǐng)導(dǎo)進行審批。
分管領(lǐng)導(dǎo)
在人員中設(shè)置人員的分管領(lǐng)導(dǎo),流程發(fā)起人員發(fā)起的流程由發(fā)起人的分管領(lǐng)導(dǎo)進行審批。
流程發(fā)起人
用戶節(jié)點由流程發(fā)起人審批。
指定范圍選擇
指定范圍選擇-可以選擇一個用戶集合,用戶節(jié)點審核時,由上一節(jié)點辦理人指定用戶節(jié)點具體的辦理人。
代理人(選擇單用戶)
如果節(jié)點設(shè)置為代理人,則可以指定當(dāng)前審核代理人,制定后節(jié)點由設(shè)置的代理人進行審核。
候選人(選擇多用戶)
候選人,可以設(shè)置多為候選人,流程審批節(jié)點會同時給多為候選人發(fā)起審批任務(wù),誰先認(rèn)領(lǐng)誰審批,由第一個認(rèn)領(lǐng)的候選人審批。
候選組(選擇多組織)
候選組的概念同候選人,流程審批節(jié)點會同時給多為候選組下的所有人發(fā)起審批任務(wù),誰先認(rèn)領(lǐng)誰審批,由第一個認(rèn)領(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: "確認(rèn)",          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;  },};
                                            
