一、环境springboot2.6.4 activiti6.0.0 vue2.x

二、后台

  1. pom.xml
  2. <!– activiti工作流引擎 –>
    <dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>${activiti.version}</version>
    </dependency>
    <dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-model</artifactId>
    <version>7.1.0.M6</version>
    </dependency>
    <dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-json-converter</artifactId>
    <version>7.1.0.M6</version>
    </dependency>

    <!– 配合activiti流工具 –>
    <dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-codec</artifactId>
    <version>1.14</version>
    </dependency>
    <dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-css</artifactId>
    <version>1.14</version>
    </dependency>
    <dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-svg-dom</artifactId>
    <version>1.14</version>
    </dependency>
    <dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-svggen</artifactId>
    <version>1.14</version>
    </dependency>

    <!– IO 工具类 –>
    <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>${commons.io.version}</version>
    </dependency>

    1. 配置文件
    2. activiti:
      # 1.false:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
      # 2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
      # 3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
      # 4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
      database-schema-update: true
      # 启动是否检查流程图
      check-process-definitions: false
      # 默认
      historyLevel: audit
      # 记录历史等级 可配置的历史级别有none, activity, audit, full
      # none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
      # activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
      # audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
      # full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
      # historyLevel: full
      # 默认关闭的,不生成历史记录
      db-history-used: true

    1. controller
    2. /**
      * 流程工具列表
      *
      * @return {@link String}
      */
      @ResponseBody
      @GetMapping(value = “stencilset”, produces = “application/json;charset=utf-8”)
      public String stencilset() {
      InputStream stream = this.getClass().getClassLoader().getResourceAsStream(“static/stencilset.json”);
      try {
      return IOUtils.toString(stream, StandardCharsets.UTF_8);
      } catch (Exception e) {
      throw new ActivitiException(“Error while loading stencil set”, e);
      }
      }

      /**
      * 根据 modelId 获取流程模型
      *
      * @param modelId 模型ID
      * @return {@link ObjectNode}
      */
      @RequestMapping(value = “/{modelId}/json”, method = RequestMethod.GET, produces = “application/json”)
      @ResponseBody
      public Object getEditorJson(@PathVariable String modelId) {
      ObjectNode modelNode = null;
      Model model = repositoryService.getModel(modelId);
      if (model != null) {
      try {
      if (org.apache.commons.lang3.StringUtils.isNotEmpty(model.getMetaInfo())) {
      modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
      } else {
      modelNode = objectMapper.createObjectNode();
      modelNode.put(MODEL_NAME, model.getName());
      }
      modelNode.put(MODEL_ID, model.getId());
      ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
      new String(repositoryService.getModelEditorSource(model.getId()), StandardCharsets.UTF_8));
      modelNode.replace(“model”, editorJsonNode);
      } catch (Exception e) {
      throw new ActivitiException(“Error creating model JSON”, e);
      }
      }
      return JSONObject.parse(modelNode.toString());
      }

      /**
      * 保存流程模型
      *
      * @param modelId 模型ID
      * @param name 流程模型名称
      * @param description 流程描述
      * @param json_xml 流程文件
      * @param svg_xml 流程图片
      */
      @RequestMapping(value = “/{modelId}/save”, method = RequestMethod.PUT)
      @ResponseStatus(value = HttpStatus.OK)
      public void saveModel(@PathVariable String modelId, String name, String description, String json_xml, String svg_xml) {
      try {
      Model model = repositoryService.getModel(modelId);
      ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
      modelJson.put(MODEL_NAME, name);
      modelJson.put(MODEL_DESCRIPTION, description);
      model.setMetaInfo(modelJson.toString());
      model.setName(name);
      repositoryService.saveModel(model);
      repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes(StandardCharsets.UTF_8));

      InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes(StandardCharsets.UTF_8));
      TranscoderInput input = new TranscoderInput(svgStream);
      PNGTranscoder transcoder = new PNGTranscoder();
      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
      TranscoderOutput output = new TranscoderOutput(outStream);

      transcoder.transcode(input, output);
      final byte[] result = outStream.toByteArray();
      repositoryService.addModelEditorSourceExtra(model.getId(), result);
      outStream.close();
      } catch (Exception e) {
      throw new ActivitiException(“Error saving model”, e);
      }
      }

    3. 三、前端图形化设计器

      1. 需准备内容
        1. 官网下载内容(有的可能带图标,有的最外层的html可能不叫这个名字,都一样,只要其
        2. 中是引入了一大堆js的那个就是):

           

           

          及汉化包:

           

           前端设计器文件放入public/static下:

           

           汉化包放入后台静态资源位置

        3.  

           

          1. 准备新的vue页面
          2. <template>
            <div>
            <iframe :src=”src” frameborder=”0″></iframe>
            </div>
            </template>
            <script>
            import Vue from ‘vue’
            import { ACCESS_TOKEN } from ‘@/store/mutation-types’
            export default {
            data () {
            return {
            modelId: ”,
            iframeUrl: ”
            }
            },
            computed: {
            src () {
            // 设计器最外部主入口html
            return ‘./static/editor.html?modelId=’ + this.modelId
            },
            apiUrl () {
            // 后台部署的api服务
            return process.env.VUE_APP_API_BASE_URL + ‘/pc/process’
            },
            token () {
            // token
            return Vue.ls.get(ACCESS_TOKEN)
            }
            },
            mounted () {
            window.getMyVue = this // 全局存入当前vue实例,供activiti调用
            this.modelId = this.$route.params.processModelCode
            }
            }
            </script>
            <style lang=”less” scoped>
            iframe {
            width: 100%;
            min-height: calc(100vh – 80px);
            }
            </style>

            1. 设计器主入口editor.html文件加入如下代码
            2. <script>
              (
              function(open){
              XMLHttpRequest.prototype.open = function(method,url,async,user,pass){
              // this指XMLHttpRequest
              open.call(this,method,url,async,user,pass);
              // mounted时传入的token
              this.setRequestHeader(“token”,window.parent.getMyVue.token);
              }
              }
              )(XMLHttpRequest.prototype.open)
              </script>

              1. 设计器文件/editor-app/app-cfg.js修改为(主要修改调用后台方法的地址)
              2. var ACTIVITI = ACTIVITI || {}
                ACTIVITI.CONFIG = {
                contextRoot: (function () {
                return window.parent.getMyVue.apiUrl// 上一步将vue实例传入的父窗口
                })()
                }

              3. 完成前后台的基本集成

                四、踩坑问题

                1. namespace问题

                发生TypeError: Cannot read properties of undefined (reading ‘namespace’)问题加入后台getEditorJson方法调整输出类型为JSON,如下

                
                
              
              
            
            

          /**
          * 根据 modelId 获取流程模型
          *
          * @param modelId 模型ID
          * @return {@link ObjectNode}
          */
          @RequestMapping(value = “/{modelId}/json”, method = RequestMethod.GET, produces = “application/json”)
          @ResponseBody
          public Object getEditorJson(@PathVariable String modelId) {
          ObjectNode modelNode = null;
          Model model = repositoryService.getModel(modelId);
          if (model != null) {
          try {
          if (org.apache.commons.lang3.StringUtils.isNotEmpty(model.getMetaInfo())) {
          modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
          } else {
          modelNode = objectMapper.createObjectNode();
          modelNode.put(MODEL_NAME, model.getName());
          }
          modelNode.put(MODEL_ID, model.getId());
          ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
          new String(repositoryService.getModelEditorSource(model.getId()), StandardCharsets.UTF_8));
          modelNode.replace(“model”, editorJsonNode);
          } catch (Exception e) {
          throw new ActivitiException(“Error creating model JSON”, e);
          }
          }
          return JSON.parse(modelNode.toString());
          }

           

          split问题
          由于上面调整了JSON输出,所以转码时出现问题,如下解决
          转载:发生TypeError: Cannot read property ‘split’ of undefined问题解决方案(这个博主的activiti版本为5.x,可能跟6.x的行数不一样,不过文件里搜一下就可以了).
          Refused to get unsafe header “X-JSON”不影响开发

           

          五、js

          1. 访问后台接口文件
            editor-app/configuration/url-config.js

          结果图

        4.  

           转自:https://blog.csdn.net/qq_36268025/article/details/123651903