联系我们进群

群主微信:QF_qingfeng1024
image.png

核心代码位置-package

bpmn核心代码全部在package包下面,如下图的位置:
image.png

流程设计器模块process-designer

页面对应内容

image.png
image.png

页面布局代码

  1. <div class="my-process-designer">
  2. <div class="my-process-designer__header">
  3. <slot name="control-header"></slot>
  4. <template v-if="!$slots['control-header']">
  5. <el-button-group key="file-control">
  6. <el-button
  7. :size="headerButtonSize"
  8. :type="headerButtonType"
  9. icon="el-icon-folder-opened"
  10. @click="$refs.refFile.click()"
  11. >打开文件</el-button
  12. >
  13. <el-button
  14. :size="headerButtonSize"
  15. :type="headerButtonType"
  16. icon="el-icon-folder-download"
  17. @click="save()"
  18. >保存</el-button
  19. >
  20. <el-button
  21. :size="headerButtonSize"
  22. :type="headerButtonType"
  23. icon="el-icon-folder-download"
  24. @click="saveAndDeploy()"
  25. >保存并部署</el-button
  26. >
  27. <el-tooltip effect="light">
  28. <div slot="content">
  29. <el-button
  30. :size="headerButtonSize"
  31. type="text"
  32. @click="downloadProcessAsXml()"
  33. >下载为XML文件</el-button
  34. >
  35. <br />
  36. <el-button
  37. :size="headerButtonSize"
  38. type="text"
  39. @click="downloadProcessAsSvg()"
  40. >下载为SVG文件</el-button
  41. >
  42. <br />
  43. <el-button
  44. :size="headerButtonSize"
  45. type="text"
  46. @click="downloadProcessAsBpmn()"
  47. >下载为BPMN文件</el-button
  48. >
  49. </div>
  50. <el-button
  51. :size="headerButtonSize"
  52. :type="headerButtonType"
  53. icon="el-icon-download"
  54. @click="deploy()"
  55. >下载文件</el-button
  56. >
  57. </el-tooltip>
  58. <el-tooltip effect="light">
  59. <div slot="content">
  60. <el-button
  61. :size="headerButtonSize"
  62. type="text"
  63. @click="previewProcessXML"
  64. >预览XML</el-button
  65. >
  66. <br />
  67. <el-button
  68. :size="headerButtonSize"
  69. type="text"
  70. @click="previewProcessJson"
  71. >预览JSON</el-button
  72. >
  73. </div>
  74. <el-button
  75. :size="headerButtonSize"
  76. :type="headerButtonType"
  77. icon="el-icon-view"
  78. >预览</el-button
  79. >
  80. </el-tooltip>
  81. <el-tooltip
  82. v-if="simulation"
  83. effect="light"
  84. :content="this.simulationStatus ? '退出模拟' : '开启模拟'"
  85. >
  86. <el-button
  87. :size="headerButtonSize"
  88. :type="headerButtonType"
  89. icon="el-icon-cpu"
  90. @click="processSimulation"
  91. >
  92. 模拟
  93. </el-button>
  94. </el-tooltip>
  95. </el-button-group>
  96. <el-button-group key="align-control">
  97. <el-tooltip effect="light" content="向左对齐">
  98. <el-button
  99. :size="headerButtonSize"
  100. class="align align-left"
  101. icon="el-icon-s-data"
  102. @click="elementsAlign('left')"
  103. />
  104. </el-tooltip>
  105. <el-tooltip effect="light" content="向右对齐">
  106. <el-button
  107. :size="headerButtonSize"
  108. class="align align-right"
  109. icon="el-icon-s-data"
  110. @click="elementsAlign('right')"
  111. />
  112. </el-tooltip>
  113. <el-tooltip effect="light" content="向上对齐">
  114. <el-button
  115. :size="headerButtonSize"
  116. class="align align-top"
  117. icon="el-icon-s-data"
  118. @click="elementsAlign('top')"
  119. />
  120. </el-tooltip>
  121. <el-tooltip effect="light" content="向下对齐">
  122. <el-button
  123. :size="headerButtonSize"
  124. class="align align-bottom"
  125. icon="el-icon-s-data"
  126. @click="elementsAlign('bottom')"
  127. />
  128. </el-tooltip>
  129. <el-tooltip effect="light" content="水平居中">
  130. <el-button
  131. :size="headerButtonSize"
  132. class="align align-center"
  133. icon="el-icon-s-data"
  134. @click="elementsAlign('center')"
  135. />
  136. </el-tooltip>
  137. <el-tooltip effect="light" content="垂直居中">
  138. <el-button
  139. :size="headerButtonSize"
  140. class="align align-middle"
  141. icon="el-icon-s-data"
  142. @click="elementsAlign('middle')"
  143. />
  144. </el-tooltip>
  145. </el-button-group>
  146. <el-button-group key="scale-control">
  147. <el-tooltip effect="light" content="缩小视图">
  148. <el-button
  149. :size="headerButtonSize"
  150. :disabled="defaultZoom < 0.2"
  151. icon="el-icon-zoom-out"
  152. @click="processZoomOut()"
  153. />
  154. </el-tooltip>
  155. <el-button :size="headerButtonSize">{{
  156. Math.floor(this.defaultZoom * 10 * 10) + "%"
  157. }}</el-button>
  158. <el-tooltip effect="light" content="放大视图">
  159. <el-button
  160. :size="headerButtonSize"
  161. :disabled="defaultZoom > 4"
  162. icon="el-icon-zoom-in"
  163. @click="processZoomIn()"
  164. />
  165. </el-tooltip>
  166. <el-tooltip effect="light" content="重置视图并居中">
  167. <el-button
  168. :size="headerButtonSize"
  169. icon="el-icon-c-scale-to-original"
  170. @click="processReZoom()"
  171. />
  172. </el-tooltip>
  173. </el-button-group>
  174. <el-button-group key="stack-control">
  175. <el-tooltip effect="light" content="撤销">
  176. <el-button
  177. :size="headerButtonSize"
  178. :disabled="!revocable"
  179. icon="el-icon-refresh-left"
  180. @click="processUndo()"
  181. />
  182. </el-tooltip>
  183. <el-tooltip effect="light" content="恢复">
  184. <el-button
  185. :size="headerButtonSize"
  186. :disabled="!recoverable"
  187. icon="el-icon-refresh-right"
  188. @click="processRedo()"
  189. />
  190. </el-tooltip>
  191. <el-tooltip effect="light" content="重新绘制">
  192. <el-button
  193. :size="headerButtonSize"
  194. icon="el-icon-refresh"
  195. @click="processRestart"
  196. />
  197. </el-tooltip>
  198. </el-button-group>
  199. </template>
  200. <!-- 用于打开本地文件-->
  201. <input
  202. type="file"
  203. id="files"
  204. ref="refFile"
  205. style="display: none"
  206. accept=".xml, .bpmn"
  207. @change="importLocalFile"
  208. />
  209. </div>
  210. <div class="my-process-designer__container">
  211. <div class="my-process-designer__canvas" ref="bpmn-canvas"></div>
  212. </div>
  213. <el-dialog
  214. title="预览"
  215. width="60%"
  216. :visible.sync="previewModelVisible"
  217. append-to-body
  218. destroy-on-close
  219. >
  220. <highlightjs :language="previewType" :code="previewResult" />
  221. </el-dialog>
  222. </div>

设计器控制面板process-panel

image.png
image.png

代码重构基础组件refactor

包含了基础组件、流程表达式、流程表单、监听器、多实例、其他任务、参数设置、信号消息、任务管理等等基础组件。
了解了代码重构的基础组件,在实际业务中可以根据自己的需求进行修改。
image.png

任务组件-改造讲解

在工作流的设计器中,我们对任务组件做了改造,由于之前的任务只能选择固定的人员或者组织,并没有和实际的数据库进行关联,无法与系统的用户打通,导致设计流程任务审批节点无法动态配置。
为了打通工作流与系统用户、组织之间的壁垒,我们通过对流程设计器的改造,重新指定了任务组件。
image.png

静态用户任务

基础业务介绍

1、选择静态分配后,我们可以选择用户和组织信息。
2、选择用户和组织的公共组件可以查看单选用户、单选组织、多选用户、多选组织的案例。
image.png
image.png

功能代码介绍

创建候选静态候选用户表单,可以动态选择用户或者组织。

  <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>

重新设置用户表单
image.png

 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);
      }

动态用户任务

image.png

 <el-select
        v-model="userTaskForm.assign_mode"
        placeholder="请选择指定类型"
        @change="selectAssignMode()"
      >
        <el-option label="所有人员中选择(根据组织选择)" value="0"></el-option>
        <el-option label="组织选择(指定组织父节点)" value="1"></el-option>
        <el-option label="用户组选择(选择指定组内成员)" value="2"></el-option>
        <el-option label="发起人本组织选择" value="3"></el-option>
        <el-option label="部门经理" value="4"></el-option>
        <el-option label="上级领导" value="5"></el-option>
        <el-option label="分管领导" value="6"></el-option>
        <el-option label="流程发起人" 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>

节点解析

所有人员中选择(根据组织选择)

可以从系统人员中,选择节点需要审批的人员,可以指定一个人或者多个人,如果指定一个,则这个人就是本节点的办理人,如果指定了多个人,则流程下发时由上一级用户选择。

组织选择(指定组织父节点)

指定组织父节点后,节点办理人为当前组织下的人员,上一节点用户发起流程可以从改组织下面所有的人员中选择下级节点的办理人。

用户组选择(选择指定组内成员)

指定用户分组,节点办理人为当前用户组下的人员,上一节点用户发起流程可以从该分组下面所有的人员中选择下级节点的办理人。

发起人本组织选择

节点办理人为当前用户同组织下的人员,上一节点用户发起流程可以从发起人同组织下面所有的人员中选择下级节点的办理人。

部门经理

在人员中设置人员的部门经理,流程发起人员发起的流程由发起人的部门经理进行审批。

上级领导

在人员中设置人员的上级领导,流程发起人员发起的流程由发起人的上级领导进行审批。

分管领导

在人员中设置人员的分管领导,流程发起人员发起的流程由发起人的分管领导进行审批。

流程发起人

用户节点由流程发起人审批。

指定范围选择

指定范围选择-可以选择一个用户集合,用户节点审核时,由上一节点办理人指定用户节点具体的办理人。

代理人(选择单用户)

如果节点设置为代理人,则可以指定当前审核代理人,制定后节点由设置的代理人进行审核。

候选人(选择多用户)

候选人,可以设置多为候选人,流程审批节点会同时给多为候选人发起审批任务,谁先认领谁审批,由第一个认领的候选人审批。

候选组(选择多组织)

候选组的概念同候选人,流程审批节点会同时给多为候选组下的所有人发起审批任务,谁先认领谁审批,由第一个认领的候选人审批。

在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 回调");
            },
            cancel() {
              console.log("cancel 回调");
            },
            close() {
              console.log("modal close 回调");
            },
            initValue(value, type) {
              if (type == "1") {
                if (fileType == "assignee") {
                  that.userTaskForm.assignee = value.split(":")[0];
                  that.userTaskForm.assigneeName = value.split(":")[1];
                  //更新文档参数
                  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];
                  //更新文档参数
                  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];
                  //更新文档参数
                  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;
  },
};