关于ruoyi框架集成activiti步骤

5 篇文章 0 订阅
1 篇文章 1 订阅

简介

此集成参照的是gitee作者 CalvinHwang123源代码位置的集成方式,若依属于前后端分离的版本3.5.0。

  • ruoyi3.5.0
  • activiti6.0

傻瓜式集成步骤(后端部分)

  1. 在ruoyi根目录添加新的模块,命名为ruoyi-activiti,得到如下结构:
    在这里插入图片描述
  2. 在父pom.xml文件中设置全局activiti版本
    在这里插入图片描述
  3. 在ruoyi-activiti的pom.xml中加入如下依赖:
    <dependencies>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-rest-api</artifactId>
            <version>${activiti.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-framework</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-system</artifactId>
        </dependency>
        <!--activiti modeler 5.22 start-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>6.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.activiti</groupId>
                    <artifactId>activiti-bpmn-model</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- xml解析依赖-->
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-css</artifactId>
            <version> 1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svg-dom</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svggen</artifactId>
            <version>1.7</version>
        </dependency>
        <!-- xml解析依赖-->
        <!--activiti modeler 5.22 end-->
    </dependencies>
  1. 在ruoyi-admin的pom.xml中加入如下依赖:
        <!-- 依赖activiti -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-activiti</artifactId>
            <version>3.5.0</version>
        </dependency>
  1. 在ruoyi-admin的application.yml文件中加入如下配置(在spring配置的下级节点上加):
  # activiti 模块
  # 解决启动报错:class path resource [processes/] cannot be resolved to URL because it does not exist
  activiti:
    check-process-definitions: false
    # 检测身份信息表是否存在
    db-identity-used: false
  1. 在 ruoyi-admin的application-druid.yml文件中的数据库路径最后加上:&nullCatalogMeansCurrent=true
    如:
# 数据源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主库数据源
            master:
                url: jdbc:mysql://localhost:3306/dcm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
                username: admin
                password: 1234
  1. 在ruoyi启动类RuoYiApplication上加入注解:
    在这里插入图片描述

/**
 * 启动程序
 *
 * @author ruoyi
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
        org.activiti.spring.boot.SecurityAutoConfiguration.class })
public class RuoYiApplication
{
    public static void main(String[] args)
    {
        // System.setProperty("spring.devtools.restart.enabled", "false");
        SpringApplication.run(RuoYiApplication.class, args);
        System.out.println("(♥◠‿◠)ノ゙  若依启动成功   ლ(´ڡ`ლ)゙  \n" +
                " .-------.       ____     __        \n" +
                " |  _ _   \\      \\   \\   /  /    \n" +
                " | ( ' )  |       \\  _. /  '       \n" +
                " |(_ o _) /        _( )_ .'         \n" +
                " | (_,_).' __  ___(_ o _)'          \n" +
                " |  |\\ \\  |  ||   |(_,_)'         \n" +
                " |  | \\ `'   /|   `-'  /           \n" +
                " |  |  \\    /  \\      /           \n" +
                " ''-'   `'-'    `-..-'              ");
    }
}

9.启动测试,看ruoyi集成activiti有没有成功,如果成功了会自动生成以下表:

ACT_EVT_LOG
ACT_GE_BYTEARRAY
ACT_GE_PROPERTY
ACT_HI_ACTINST
ACT_HI_ATTACHMENT
ACT_HI_COMMENT
ACT_HI_DETAIL
ACT_HI_IDENTITYLINK
ACT_HI_PROCINST
ACT_HI_TASKINST
ACT_HI_VARINST
ACT_PROCDEF_INFO
ACT_RE_DEPLOYMENT
ACT_RE_MODEL
ACT_RE_PROCDEF
ACT_RU_DEADLETTER_JOB
ACT_RU_EVENT_SUBSCR
ACT_RU_EXECUTION
ACT_RU_IDENTITYLINK
ACT_RU_JOB
ACT_RU_SUSPENDED_JOB
ACT_RU_TASK
ACT_RU_TIMER_JOB
ACT_RU_VARIABLE
  1. 对资源进行放行:
    在com.ruoyi.framework.config.SecurityConfig中加入如下代码:
    在这里插入图片描述
   //activiti相关接口放行
                .antMatchers("/modeler/**").anonymous()
                .antMatchers("/activiti/definition/upload").anonymous()
                .antMatchers("/activiti/definition/readResource").anonymous()
                .antMatchers("/activiti/process/read-resource").anonymous()
  1. 你可以直接在浏览器上输入:http://localhost:8080/management/tables进行访问,看是否可以返回表信息
    在这里插入图片描述
    12.再拿到作者的源码,再ruoyi-activiti的resources下拷贝以下文件:
    在这里插入图片描述
  2. 再到ruoyi-activiti下加入以下包名com.ruoyi.activiti.modeler,并拷贝添加以下类:
    在这里插入图片描述
    ModelEditorJsonRestResource:
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ruoyi.activiti.modeler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author Tijs Rademakers
 * @author 一只闲鹿
 */
@RestController
public class ModelEditorJsonRestResource extends BaseController implements ModelDataJsonConstants {

  private static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);

  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private ObjectMapper objectMapper;
  @Autowired
  private ISysUserService userService;
  @Autowired
  private ISysRoleService roleService;
  @Autowired
  private SysUserMapper userMapper;

  @RequestMapping(value="/modeler/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
  public ObjectNode getEditorJson(@PathVariable String modelId) {
    ObjectNode modelNode = null;

    Model model = repositoryService.getModel(modelId);

    if (model != null) {
      try {
        if (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.put("model", editorJsonNode);

      } catch (Exception e) {
        LOGGER.error("Error creating model JSON", e);
        throw new ActivitiException("Error creating model JSON", e);
      }
    }
    return modelNode;
  }

  /**
   * 免登录访问用户列表
   * @param user
   * @return
   */
  @GetMapping("/list")
  public TableDataInfo list(SysUser user)
  {
    startPage();
    List<SysUser> list = userService.selectUserList(user);
    return getDataTable(list);
  }

  /**
   * 角色列表
   * @param role
   * @return
   */
  @GetMapping("/modeler/role/list")
  public TableDataInfo list(SysRole role)
  {
    startPage();
    List<SysRole> list = roleService.selectRoleList(role);
    return getDataTable(list);
  }

  /**
   * 某角色下的用户列表
   * @param roleKey
   * @return
   */
  @GetMapping("/modeler/user/listByRoleKey")
  public TableDataInfo list(String roleKey)
  {
    startPage();
    SysUser sysUser = new SysUser();
//    sysUser.setRoleIds();
    List<SysUser> list = userMapper.selectUserList(sysUser);
    return getDataTable(list);
  }

}

ModelerController:

package com.ruoyi.activiti.modeler;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.Page;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.enums.BusinessType;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.persistence.entity.ModelEntityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static org.activiti.editor.constants.ModelDataJsonConstants.MODEL_DESCRIPTION;
import static org.activiti.editor.constants.ModelDataJsonConstants.MODEL_NAME;

/**
 * 流程模型控制层
 *
 * @author 一只闲鹿
 */
@Controller
@RequestMapping("/activiti/modeler")
public class ModelerController extends BaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 模型列表
     */
    @PreAuthorize("@ss.hasPermi('activiti:modeler:list')")
    @GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(ModelEntityImpl modelEntity) {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        modelQuery.orderByLastUpdateTime().desc();

        // 条件过滤
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(modelEntity.getKey())) {
            modelQuery.modelKey(modelEntity.getKey());
        }
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(modelEntity.getName())) {
            modelQuery.modelNameLike("%" + modelEntity.getName() + "%");
        }

        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();

        List<Model> resultList = modelQuery.listPage((pageNum - 1) * pageSize, pageSize);
        Page<ModelEntityImpl> list = new Page<>();
        resultList.parallelStream().forEach(model -> {
            ModelEntityImpl modelEntity1 = (ModelEntityImpl) model;
            list.add(modelEntity1);
        });

        list.setTotal(modelQuery.count());
        list.setPageNum(pageNum);
        list.setPageSize(pageSize);

        return getDataTable(list);
    }

    /**
     * 创建模型
     */
    @PostMapping(value = "/create")
    @ResponseBody
    public AjaxResult create(@RequestParam("name") String name, @RequestParam("key") String key,
                             @RequestParam(value = "description", required = false) String description) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode editorNode = objectMapper.createObjectNode();
            editorNode.put("id", "canvas");
            editorNode.put("resourceId", "canvas");
            ObjectNode stencilSetNode = objectMapper.createObjectNode();
            stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
            editorNode.put("stencilset", stencilSetNode);

            ObjectNode modelObjectNode = objectMapper.createObjectNode();
            modelObjectNode.put(MODEL_NAME, name);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            description = StringUtils.defaultString(description);
            modelObjectNode.put(MODEL_DESCRIPTION, description);

            Model newModel = repositoryService.newModel();
            newModel.setMetaInfo(modelObjectNode.toString());
            newModel.setName(name);
            newModel.setKey(StringUtils.defaultString(key));

            repositoryService.saveModel(newModel);
            repositoryService.addModelEditorSource(newModel.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8));

            return new AjaxResult(200, "创建模型成功", newModel.getId());
        } catch (Exception e) {
            logger.error("创建模型失败:", e);
        }
        return error();
    }

    /**
     * 根据Model部署流程
     */
    @GetMapping(value = "/deploy/{modelId}")
    @ResponseBody
    public AjaxResult deploy(@PathVariable("modelId") String modelId) {
        try {
            Model modelData = repositoryService.getModel(modelId);
            ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));

            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
            LOGGER.info("部署成功,部署ID=" + deployment.getId());
            return success("部署成功");
        } catch (Exception e) {
            LOGGER.error("根据模型部署流程失败:modelId={}", modelId, e);

        }
        return error("部署失败");
    }

    /**
     * 导出model的xml文件
     */
    @GetMapping(value = "/export/{modelId}")
    public void export(@PathVariable("modelId") String modelId, HttpServletResponse response) {
        try {
            Model modelData = repositoryService.getModel(modelId);
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);

            // 流程非空判断
            if (!CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
                BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
                byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

                ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
                String filename = bpmnModel.getMainProcess().getId() + ".bpmn";
                response.setHeader("Content-Disposition", "attachment; filename=" + filename);
                IOUtils.copy(in, response.getOutputStream());
                response.flushBuffer();
            }

        } catch (Exception e) {
            LOGGER.error("导出model的xml文件失败:modelId={}", modelId, e);
        }
    }

    @Log(title = "流程模型", businessType = BusinessType.DELETE)
    @DeleteMapping("/remove/{ids}")
    @ResponseBody
    public AjaxResult remove(@PathVariable("ids") String ids) {
        try {
            repositoryService.deleteModel(ids);
            return AjaxResult.success();
        }
        catch (Exception e) {
            return error(e.getMessage());
        }
    }

}

ModelSaveRestResource:

/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ruoyi.activiti.modeler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * @author Tijs Rademakers
 */
@RestController
public class ModelSaveRestResource implements ModelDataJsonConstants {

  private static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);
  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private ObjectMapper objectMapper;

  @RequestMapping(value="/modeler/model/{modelId}/save", method = RequestMethod.POST)
  @ResponseStatus(value = HttpStatus.OK)
  public void saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) {
    try {

      Model model = repositoryService.getModel(modelId);

      ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());

      modelJson.put(MODEL_NAME, values.getFirst("name"));
      modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
      model.setMetaInfo(modelJson.toString());
      model.setName(values.getFirst("name"));

      repositoryService.saveModel(model);

      repositoryService.addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes(StandardCharsets.UTF_8));

      InputStream svgStream = new ByteArrayInputStream(values.getFirst("svg_xml").getBytes(StandardCharsets.UTF_8));
      TranscoderInput input = new TranscoderInput(svgStream);

      PNGTranscoder transcoder = new PNGTranscoder();
      // Setup output
      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
      TranscoderOutput output = new TranscoderOutput(outStream);

      // Do the transformation
      transcoder.transcode(input, output);
      final byte[] result = outStream.toByteArray();
      repositoryService.addModelEditorSourceExtra(model.getId(), result);
      outStream.close();

    } catch (Exception e) {
      LOGGER.error("Error saving model", e);
      throw new ActivitiException("Error saving model", e);
    }
  }
}

StencilsetRestResource:

/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ruoyi.activiti.modeler;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import org.activiti.engine.ActivitiException;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Tijs Rademakers
 */
@RestController
public class StencilsetRestResource {

  @RequestMapping(value="/modeler/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
  public @ResponseBody String getStencilset() {
    InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json");
    try {
      return IOUtils.toString(stencilsetStream, StandardCharsets.UTF_8);
    } catch (Exception e) {
      throw new ActivitiException("Error while loading stencil set", e);
    }
  }
}

傻瓜式集成步骤(前端部分)

14.加入前端代码,也是进行拷贝就可以了:
在这里插入图片描述
definition.js:

import request from '@/utils/request'

// 查询modeler列表
export function listDefinition(query) {
  return request({
    url: '/processDefinition/list',
    method: 'get',
    params: query
  })
}

// 查询请假详细
export function getDefinitionsByInstanceId(instanceId) {
  return request({
    url: '/processDefinition/getDefinitions/' + instanceId,
    method: 'get'
  })
}

// 挂起激活转换
export function suspendOrActiveApply(data) {
  return request({
    url: '/processDefinition/suspendOrActiveApply',
    method: 'post',
    data:data
  })
}


// 删除Modeler
export function delDefinition(id) {
  return request({
    url: '/processDefinition/remove/' + id,
    method: 'delete'
  })
}



historyFormdata.js:

import request from '@/utils/request'
// 查询请假详细
export function historyFromData(instanceId) {
  return request({
    url: '/historyFromData/ByInstanceId/' + instanceId,
    method: 'get'
  })
}

modeler.js:

import request from '@/utils/request'

// 查询模型列表
export function listModeler(query) {
  return request({
    url: '/activiti/modeler/list',
    method: 'get',
    params: query
  })
}

// 新增模型
export function addModeler(data) {
  return request({
    url: '/activiti/modeler/create',
    method: 'post',
    params: data
  })
}

// 删除模型
export function delModeler(modelId) {
  return request({
    url: '/activiti/modeler/remove/' + modelId,
    method: 'delete',
  })
}

// 导出模型
export function exportModeler(modelId) {
  return request({
    url: '/activiti/modeler/export/' + modelId,
    method: 'get',
  })
}

// 部署模型
export function deployModeler(modelId) {
  return request({
    url: '/activiti/modeler/deploy/' + modelId,
    method: 'get',
  })
}

  1. 加如views代码:
    在这里插入图片描述
    definition/index.vue:
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true">
      <el-form-item label="KEY" prop="key">
        <el-input
          v-model="queryParams.key"
          placeholder="请输入流程KEY"
          clearable
          size="small"
          style="width: 240px"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="名称" prop="name">
        <el-input
          v-model="queryParams.name"
          placeholder="请输入名称"
          clearable
          size="small"
          style="width: 240px"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="所属分类" prop="name">
        <el-input
          v-model="queryParams.category"
          placeholder="请输入所属分类"
          clearable
          size="small"
          style="width: 240px"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          icon="el-icon-upload"
          size="mini"
          @click="handleUpload"
        >部署流程定义</el-button>
        <span style="color: red; font-size: 12px;">(查看流程图模糊时,可选择 .bpmn 和 .png 一同打成 zip 包部署)</span>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="definitionList">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="流程ID" align="center" prop="id" width="120" />
      <el-table-column label="流程KEY" align="center" prop="key" width="120" />
      <el-table-column label="流程名称" align="center" prop="name" width="150" />
      <el-table-column label="版本" align="center" prop="version" width="90" />
      <el-table-column label="流程描述" align="center" prop="description" width="150" />
      <el-table-column label="所属分类" align="center" prop="category" width="120" />
      <el-table-column label="部署时间" align="center" prop="deploymentTime" width="100" />
      <!--<el-table-column label="流程定义" align="center" prop="resourceName" width="120">
        <template slot-scope="scope">
          <a
            :href="baseURL + '/activiti/definition/readResource?pdid=' + scope.row.id + '&resourceName=' + scope.row.resourceName "
            target="_blank"
            style="color: #409EFF;"
          >{{ scope.row.resourceName.substring(scope.row.resourceName.lastIndexOf('/') + 1) }}</a>
        </template>
      </el-table-column>
      <el-table-column label="流程图" align="center" prop="diagramResourceName" width="120">
        <template slot-scope="scope">
          <img
            style="width: 100px;"
            :src="baseURL + '/activiti/definition/readResource?pdid=' + scope.row.id + '&resourceName=' + scope.row.diagramResourceName"
          />
        </template>
      </el-table-column>-->
      <el-table-column label="状态" align="center" prop="suspendStateName" width="90" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-sort"
            @click="handleState(scope.row)"
          >{{ scope.row.suspendState === '2' ? '激活' : '挂起' }}</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-refresh"
            @click="handleConvert(scope.row)"
          >转模型</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 部署流程定义对话框 -->
    <el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
        <el-form-item label="上传" prop="upload" required>
          <el-upload ref="upload" :file-list="uploadfileList" :action="uploadAction"
                     :before-upload="uploadBeforeUpload">
            <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="close">取消</el-button>
        <el-button type="primary" @click="handelConfirm">确定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { listDefinition, delDefinition, suspendOrActiveDefinition, convert2Model } from "@/api/activiti/definition.js"
import { format } from "@/utils/activiti/myUtil.js"

export default {
  name: "Definition",
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 流程定义表格数据
      definitionList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 日期范围
      dateRange: [],
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        key: undefined,
        name: undefined,
        category: undefined,
      },
      // 表单参数
      form: {},
      rules: {},
      formData: {
        upload: null,
      },
      uploadAction: process.env.VUE_APP_BASE_API + '/activiti/definition/upload',
      uploadfileList: [],
      baseURL: process.env.VUE_APP_BASE_API,
    };
  },
  created() {
    this.getList();
  },
  methods: {
    /** 查询流程定义列表 */
    getList() {
      this.loading = true;
      listDefinition(this.queryParams).then(
        response => {
          this.definitionList = response.rows;
          this.definitionList.forEach(row => {
            row.deploymentTime = format(row.deploymentTime, 'yyyy-MM-dd HH:mm:ss');
          });
          this.total = response.total;
          this.loading = false;
        }
      );
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.handleQuery();
    },
    /** 新增按钮操作 */
    handleUpload() {
      this.open = true;
      this.title = "部署流程定义";
    },
    onOpen() {},
    onClose() {
      this.$refs['elForm'].resetFields()
    },
    close() {
      this.open = false;
      this.formData = {
        upload: null,
      };
      this.uploadfileList = [];
    },
    handelConfirm() {
      // this.$refs['elForm'].validate(valid => {
      //   if (!valid) return;
      //   this.close()
      // })
      this.$refs.upload.submit();
      this.close();
      this.getList();
    },
    uploadBeforeUpload(file) {
      let isRightSize = file.size / 1024 / 1024 < 2
      if (!isRightSize) {
        this.$message.error('文件大小超过 2MB')
      }
      return isRightSize
    },
    /** 激活挂起按钮操作 */
    handleState(row) {
      const pid = row.id;
      const suspendState = row.suspendState;
      const opt = row.suspendState === '2' ? '激活': '挂起';
      this.$confirm('是否确认' + opt + 'ID为"' + pid + '"的流程定义?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function () {
        const data = { "id": pid, "suspendState": suspendState };
        return suspendOrActiveDefinition(data);
      }).then(response => {
        this.msgSuccess("操作成功");
        this.getList();
      }).catch(function () {
      });
    },
    /** 转模型按钮操作 */
    handleConvert(row) {
      const pid = row.id;
      this.$confirm('是否确认将ID为"' + pid + '"的流程定义转换成流程模型?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function () {
        const data = { "processDefinitionId": pid };
        return convert2Model(data);
      }).then(response => {
        this.msgSuccess("操作成功");
        this.getList();
      }).catch(function () {
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const deploymentId = row.deploymentId;
      this.$confirm('是否确认删除ID为"' + deploymentId + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function () {
        return delDefinition(deploymentId);
      }).then(() => {
        this.getList();
        this.msgSuccess("删除成功");
      }).catch(function () {
      });
    },
  },
}
</script>

<style scoped>

</style>

@/utils/activiti/myUtil.js

export function format(time, format) {
  var t = new Date(time);
  var tf = function (i) { return (i < 10 ? '0' : '') + i };
  return format.replace(/yyyy|MM|dd|HH|mm|ss/g, function (a) {
    switch (a) {
      case 'yyyy':
        return tf(t.getFullYear());
        break;
      case 'MM':
        return tf(t.getMonth() + 1);
        break;
      case 'mm':
        return tf(t.getMinutes());
        break;
      case 'dd':
        return tf(t.getDate());
        break;
      case 'HH':
        return tf(t.getHours());
        break;
      case 'ss':
        return tf(t.getSeconds());
        break;
    }
  })
}

/**
 * 计算出相差天数
 * @param secondSub
 */
export function formatTotalDateSub (secondSub) {
  var days = Math.floor(secondSub / (24 * 3600));     // 计算出小时数
  var leave1 = secondSub % (24*3600) ;                // 计算天数后剩余的毫秒数
  var hours = Math.floor(leave1 / 3600);              // 计算相差分钟数
  var leave2 = leave1 % (3600);                       // 计算小时数后剩余的毫秒数
  var minutes = Math.floor(leave2 / 60);              // 计算相差秒数
  var leave3 = leave2 % 60;                           // 计算分钟数后剩余的毫秒数
  var seconds = Math.round(leave3);
  return days + "天" + hours + "时" + minutes + "分" + seconds + '秒';
}


modeler/index.vue:

<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true">
      <el-form-item label="KEY" prop="key">
        <el-input
          v-model="queryParams.key"
          placeholder="请输入KEY"
          clearable
          size="small"
          style="width: 240px"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="名称" prop="name">
        <el-input
          v-model="queryParams.name"
          placeholder="请输入名称"
          clearable
          size="small"
          style="width: 240px"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['activiti:modeler:add']"
        >创建新模型</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="modelerList">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="ID" align="center" prop="id" width="120" />
      <el-table-column label="KEY" align="center" prop="key" width="120" />
      <el-table-column label="名称" align="center" prop="name" width="150" />
      <el-table-column label="版本" align="center" prop="version" width="150" />
      <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
      <el-table-column label="最后更新时间" align="center" prop="lastUpdateTime" width="180" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['activiti:modeler:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-upload2"
            @click="handleDeploy(scope.row)"
            v-hasPermi="['activiti:modeler:deploy']"
          >部署</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-download"
            @click="handleExport(scope.row)"
            v-hasPermi="['activiti:modeler:export']"
          >导出</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['activiti:modeler:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改模型对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="KEY" prop="key">
          <el-input v-model="form.key" placeholder="请输入KEY" />
        </el-form-item>
        <el-form-item label="名称" prop="name">
          <el-input v-model="form.name" placeholder="请输入名称" />
        </el-form-item>
        <el-form-item label="描述" prop="description">
          <el-input v-model="form.description" type="textarea" placeholder="请输入描述" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { listModeler, delModeler, addModeler, deployModeler, exportModeler } from "@/api/activiti/modeler.js"
import { format } from "@/utils/activiti/myUtil.js"

export default {
  name: "Modeler",
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 模型表格数据
      modelerList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 日期范围
      dateRange: [],
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        key: undefined,
        name: undefined,
      },
      // 表单参数
      form: {},
      defaultProps: {
        children: "children",
        label: "label"
      },
      // 表单校验
      rules: {
        key: [
          { required: true, message: "KEY不能为空", trigger: "blur" }
        ],
        name: [
          { required: true, message: "名称不能为空", trigger: "blur" }
        ],
      }
    };
  },
  created() {
    this.getList();
  },
  methods: {
    /** 查询模型列表 */
    getList() {
      this.loading = true;
      listModeler(this.queryParams).then(
        response => {
          this.modelerList = response.rows;
          this.modelerList.forEach(row => {
            row.createTime = format(row.createTime, 'yyyy-MM-dd HH:mm:ss');
            row.lastUpdateTime = format(row.lastUpdateTime, 'yyyy-MM-dd HH:mm:ss');
          });
          this.total = response.total;
          this.loading = false;
        }
      );
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        id: undefined,
        name: undefined,
        key: undefined,
        description: undefined,
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.handleQuery();
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加模型";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const modelId = row.id;
      this.designModeler(modelId);
    },
    /** 提交按钮 */
    submitForm: function () {
      this.$refs["form"].validate(valid => {
        if (valid) {
          addModeler(this.form).then(response => {
            if (response.code === 200) {
              this.msgSuccess("新增成功");
              this.open = false;
              this.getList();

              this.designModeler(response.data)
            }
          });
        }
      });
    },
    /** 模型在线设计 */
    designModeler(id) {
      // 打开新标签页
      const routeUrl = this.$router.resolve({
        path: process.env.VUE_APP_BASE_API + "/modeler/modeler.html?modelId=" + id,
      });
      window.open(routeUrl.href, '_blank');
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const modelIds = row.id;
      this.$confirm('是否确认删除ID为"' + modelIds + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function () {
        return delModeler(modelIds);
      }).then(() => {
        this.getList();
        this.msgSuccess("删除成功");
      }).catch(function () {
      });
    },
    /** 导出按钮操作 */
    handleExport(row) {
      const modelId = row.id;
      this.$confirm('是否确认导出ID为"' + modelId + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function () {
        return exportModeler(modelId);
      }).then(response => {
        //创建一个隐藏的a连接,
        const link = document.createElement('a');
        let blob = new Blob([response]/*, {type: 'application/xml'}*/);
        link.style.display = 'none';
        //设置连接
        link.href = URL.createObjectURL(blob);
        link.download = '导出_' + new Date().getTime() + '.bpmn';
        document.body.appendChild(link);
        //模拟点击事件
        link.click();
      }).catch(function () {
      });
    },
    /** 部署按钮操作 */
    handleDeploy(row) {
      const modelId = row.id;
      this.$confirm('是否确认部署ID为"' + modelId + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function () {
        return deployModeler(modelId);
      }).then(response => {
        this.msgSuccess("部署成功");
      }).catch(function () {
      });
    },
  },
}
</script>

<style scoped>

</style>

  1. 添加菜单路径:
    在这里插入图片描述在这里插入图片描述

  2. 这时候就可以刷新页面,进入流程模型菜单里了
    在这里插入图片描述
    18.创建新的模型对象,会在新的窗口打开流程在线设计器,但有个别的会出现设计器出来了,但没得操作图标那些,是因为需要清理缓存:也就是下面这个打了勾,去掉刷新就可以了:
    在这里插入图片描述

  • 26
    点赞
  • 150
    收藏
    觉得还不错? 一键收藏
  • 30
    评论
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值