From 22495195222f2beafda3b53a705400796438aca0 Mon Sep 17 00:00:00 2001 From: konbai <1527468660@qq.com> Date: Fri, 8 Jul 2022 21:33:58 +0800 Subject: [PATCH] =?UTF-8?q?add=20--=20=E6=B7=BB=E5=8A=A0=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=92=8C=E9=83=A8=E7=BD=B2=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/WfDeployController.java | 53 +- .../workflow/WfModelController.java | 116 ++++ .../common/constant/ProcessConstants.java | 2 + .../ruoyi/flowable/common/enums/FormType.java | 33 ++ .../com/ruoyi/flowable/utils/ModelUtils.java | 43 ++ .../ruoyi/workflow/domain/WfDeployForm.java | 21 +- .../ruoyi/workflow/domain/bo/WfModelBo.java | 50 ++ .../ruoyi/workflow/domain/bo/WfProcessBo.java | 9 + .../workflow/domain/dto/WfMetaInfoDto.java | 25 + .../ruoyi/workflow/domain/vo/WfDeployVo.java | 90 +++ .../ruoyi/workflow/domain/vo/WfModelVo.java | 63 +++ .../service/IWfDeployFormService.java | 8 + .../workflow/service/IWfDeployService.java | 21 + .../workflow/service/IWfModelService.java | 31 ++ .../service/impl/WfDeployFormServiceImpl.java | 14 + .../service/impl/WfDeployServiceImpl.java | 147 +++++ .../service/impl/WfModelServiceImpl.java | 286 ++++++++++ ruoyi-ui/src/api/workflow/deploy.js | 35 ++ ruoyi-ui/src/api/workflow/model.js | 66 +++ ruoyi-ui/src/views/workflow/deploy/index.vue | 302 ++++++++++ .../src/views/workflow/model/designer.vue | 295 ++++++++++ ruoyi-ui/src/views/workflow/model/index.vue | 525 ++++++++++++++++++ 22 files changed, 2225 insertions(+), 10 deletions(-) create mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfModelController.java create mode 100644 ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FormType.java create mode 100644 ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfModelBo.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/domain/dto/WfMetaInfoDto.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfDeployVo.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfModelVo.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfModelService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployServiceImpl.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java create mode 100644 ruoyi-ui/src/api/workflow/model.js create mode 100644 ruoyi-ui/src/views/workflow/deploy/index.vue create mode 100644 ruoyi-ui/src/views/workflow/model/designer.vue create mode 100644 ruoyi-ui/src/views/workflow/model/index.vue diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfDeployController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfDeployController.java index f2e01c24..57c38895 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfDeployController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfDeployController.java @@ -1,19 +1,22 @@ package com.ruoyi.web.controller.workflow; +import cn.dev33.satoken.annotation.SaCheckPermission; import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.PageQuery; import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.workflow.domain.bo.WfProcessBo; +import com.ruoyi.workflow.domain.vo.WfDeployVo; import com.ruoyi.workflow.domain.vo.WfFormVo; import com.ruoyi.workflow.service.IWfDeployFormService; +import com.ruoyi.workflow.service.IWfDeployService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.Map; import java.util.Objects; @@ -29,8 +32,50 @@ import java.util.Objects; @RequestMapping("/workflow/deploy") public class WfDeployController extends BaseController { + private final IWfDeployService deployService; private final IWfDeployFormService deployFormService; + /** + * 查询流程部署列表 + */ + @SaCheckPermission("workflow:deploy:list") + @GetMapping("/list") + public TableDataInfo list(WfProcessBo processBo, PageQuery pageQuery) { + return deployService.queryPageList(processBo, pageQuery); + } + + /** + * 查询流程部署版本列表 + */ + @SaCheckPermission("workflow:deploy:list") + @GetMapping("/publishList") + public TableDataInfo publishList(@ApiParam(value = "流程定义Key", required = true) @RequestParam String processKey, + PageQuery pageQuery) { + return deployService.queryPublishList(processKey, pageQuery); + } + + /** + * + * @param state + * @param definitionId + * @return + */ + @ApiOperation(value = "激活或挂起流程") + @SaCheckPermission("workflow:deploy:state") + @PutMapping(value = "/changeState") + public R changeState(@ApiParam(value = "状态(active:激活 suspended:挂起)", required = true) @RequestParam String state, + @ApiParam(value = "流程定义ID", required = true) @RequestParam String definitionId) { + deployService.updateState(definitionId, state); + return R.ok(); + } + + @ApiOperation(value = "读取xml文件") + @SaCheckPermission("workflow:deploy:query") + @GetMapping("/bpmnXml/{definitionId}") + public R getBpmnXml(@ApiParam(value = "流程定义ID") @PathVariable(value = "definitionId") String definitionId) { + return R.ok(null, deployService.queryBpmnXmlById(definitionId)); + } + /** * * @param deployId diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfModelController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfModelController.java new file mode 100644 index 00000000..48cd36f5 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/workflow/WfModelController.java @@ -0,0 +1,116 @@ +package com.ruoyi.web.controller.workflow; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.workflow.domain.bo.WfModelBo; +import com.ruoyi.workflow.domain.vo.WfModelVo; +import com.ruoyi.workflow.service.IWfModelService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; + +/** + * @author KonBAI + * @createTime 2022/6/21 9:09 + */ +@Slf4j +@Api(tags = "工作流流程模型管理") +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/model") +public class WfModelController extends BaseController { + + private final IWfModelService modelService; + + @ApiOperation(value = "查询流程模型列表") + @SaCheckPermission("workflow:model:list") + @GetMapping("/list") + public TableDataInfo list(WfModelBo modelBo, PageQuery pageQuery) { + return modelService.list(modelBo, pageQuery); + } + + @ApiOperation(value = "查询流程模型列表") + @SaCheckPermission("workflow:model:list") + @GetMapping("/historyList") + public TableDataInfo historyList(WfModelBo modelBo, PageQuery pageQuery) { + return modelService.historyList(modelBo, pageQuery); + } + + /** + * 获取流程模型详细信息 + */ + @ApiOperation(value = "查询流程模型详情信息") + @SaCheckPermission("workflow:model:query") + @GetMapping(value = "/{modelId}") + public R getInfo(@ApiParam("主键") @NotNull(message = "主键不能为空") @PathVariable("modelId") String modelId) { + return R.ok(modelService.getModel(modelId)); + } + + /** + * 获取流程表单详细信息 + */ + @SaCheckPermission("workflow:model:query") + @GetMapping(value = "/bpmnXml/{modelId}") + public R getBpmnXml(@ApiParam("主键") @NotNull(message = "主键不能为空") @PathVariable("modelId") String modelId) { + return R.ok("操作成功", modelService.queryBpmnXmlById(modelId)); + } + + /** + * 保存流程模型 + */ + @ApiOperation("保存流程模型") + @SaCheckPermission("workflow:model:save") + @Log(title = "保存流程模型", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R save(@RequestBody WfModelBo modelBo) { + modelService.saveModel(modelBo); + return R.ok(); + } + + @ApiOperation("设为最新流程模型") + @SaCheckPermission("workflow:model:save") + @Log(title = "设为最新流程模型", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/latest") + public R latest(@RequestParam String modelId) { + modelService.latestModel(modelId); + return R.ok(); + } + + /** + * 删除流程模型 + */ + @ApiOperation("删除流程模型") + @SaCheckPermission("workflow:model:remove") + @Log(title = "删除流程模型", businessType = BusinessType.DELETE) + @DeleteMapping("/{modelIds}") + public R remove(@ApiParam("主键串") @NotEmpty(message = "主键不能为空") @PathVariable String[] modelIds) { + modelService.deleteByIds(Arrays.asList(modelIds)); + return R.ok(); + } + + @ApiOperation("部署流程模型") + @SaCheckPermission("workflow:model:deploy") + @Log(title = "部署流程模型", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/deploy") + public R deployModel(@RequestParam String modelId) { + modelService.deployModel(modelId); + return R.ok(); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/constant/ProcessConstants.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/constant/ProcessConstants.java index 14a2c66d..76bc9538 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/constant/ProcessConstants.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/constant/ProcessConstants.java @@ -8,6 +8,8 @@ package com.ruoyi.flowable.common.constant; */ public class ProcessConstants { + public static final String SUFFIX = ".bpmn"; + /** * 动态数据 */ diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FormType.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FormType.java new file mode 100644 index 00000000..adce3516 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FormType.java @@ -0,0 +1,33 @@ +package com.ruoyi.flowable.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author KonBAI + * @createTime 2022/6/28 9:51 + */ +@Getter +@AllArgsConstructor +public enum FormType { + + /** + * 流程表单 + */ + PROCESS(0), + + /** + * 外置表单 + */ + EXTERNAL(1), + + /** + * 节点独立表单 + */ + INDEPENDENT(2); + + /** + * 表单类型 + */ + private final Integer type; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java new file mode 100644 index 00000000..e1d6ebe8 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java @@ -0,0 +1,43 @@ +package com.ruoyi.flowable.utils; + +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.StartEvent; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.Collection; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection elements = process.getFlowElements(); + return (StartEvent) elements.stream() + .filter(flowElement -> flowElement instanceof StartEvent) + .findFirst().orElse(null); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/WfDeployForm.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/WfDeployForm.java index a2cca68c..5fbcb2bc 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/WfDeployForm.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/WfDeployForm.java @@ -1,8 +1,5 @@ package com.ruoyi.workflow.domain; -import com.alibaba.excel.annotation.ExcelProperty; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; import lombok.Data; @@ -22,13 +19,25 @@ public class WfDeployForm { /** * 流程定义主键 */ - @TableId(type = IdType.INPUT) - @ExcelProperty(value = "流程定义主键") private String deployId; /** * 表单主键 */ - @ExcelProperty(value = "表单主键") private Long formId; + + /** + * 节点Key + */ + private String nodeKey; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 表单内容 + */ + private String content; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfModelBo.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfModelBo.java new file mode 100644 index 00000000..076fc51a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfModelBo.java @@ -0,0 +1,50 @@ +package com.ruoyi.workflow.domain.bo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author KonBAI + * @createTime 2022/6/21 9:16 + */ +@Data +@ApiModel("流程模型对象") +public class WfModelBo { + + @ApiModelProperty(value = "模型主键") + private String modelId; + + @ApiModelProperty(value = "模型名称", required = true) + @NotNull(message = "模型名称不能为空") + private String modelName; + + @ApiModelProperty(value = "模型Key", required = true) + @NotNull(message = "模型Key不能为空") + private String modelKey; + + @ApiModelProperty(value = "流程分类", required = true) + @NotBlank(message = "流程分类不能为空") + private String category; + + @ApiModelProperty(value = "描述") + private String description; + + @ApiModelProperty(value = "表单类型", required = true) + @NotBlank(message = "表单类型不能为空") + private Integer formType; + + @ApiModelProperty(value = "表单主键", required = true) + @NotBlank(message = "表单不能为空") + private Long formId; + + @ApiModelProperty(value = "流程xml", required = true) + @NotBlank(message = "流程xml不能为空") + private String bpmnXml; + + @ApiModelProperty(value = "是否保存为新版本", required = true) + private Boolean newVersion; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfProcessBo.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfProcessBo.java index 8b24ceb9..71c0c6ba 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfProcessBo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/bo/WfProcessBo.java @@ -14,6 +14,15 @@ import lombok.Data; @ApiModel("流程业务对象") public class WfProcessBo { + @ApiModelProperty("流程标识") + private String processKey; + @ApiModelProperty("流程名称") private String processName; + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("状态") + private String state; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/dto/WfMetaInfoDto.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/dto/WfMetaInfoDto.java new file mode 100644 index 00000000..46142d40 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/dto/WfMetaInfoDto.java @@ -0,0 +1,25 @@ +package com.ruoyi.workflow.domain.dto; + +import lombok.Data; + +/** + * @author KonBAI + * @createTime 2022/6/21 9:16 + */ +@Data +public class WfMetaInfoDto { + + /** + * 流程描述 + */ + private String description; + /** + * 表单类型 + */ + private Integer formType; + /** + * 表单编号 + */ + private Long formId; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfDeployVo.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfDeployVo.java new file mode 100644 index 00000000..f8734196 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfDeployVo.java @@ -0,0 +1,90 @@ +package com.ruoyi.workflow.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + + +/** + * 流程部署视图对象 + * + * @author KonBAI + * @date 2022-06-30 + */ +@Data +@ApiModel("流程部署视图对象") +@ExcelIgnoreUnannotated +public class WfDeployVo { + + private static final long serialVersionUID = 1L; + + /** + * 流程定义ID + */ + @ExcelProperty(value = "流程定义ID") + @ApiModelProperty("流程定义ID") + private String definitionId; + + /** + * 流程名称 + */ + @ExcelProperty(value = "流程名称") + @ApiModelProperty("流程名称") + private String processName; + + /** + * 流程Key + */ + @ExcelProperty(value = "流程Key") + @ApiModelProperty("流程Key") + private String processKey; + + /** + * 分类编码 + */ + @ExcelProperty(value = "分类编码") + @ApiModelProperty("分类编码") + private String category; + + @ApiModelProperty("版本") + private Integer version; + + /** + * 表单ID + */ + @ExcelProperty(value = "表单ID") + @ApiModelProperty("表单ID") + private Long formId; + + /** + * 表单名称 + */ + @ExcelProperty(value = "表单名称") + @ApiModelProperty("表单名称") + private String formName; + + /** + * 部署ID + */ + @ExcelProperty(value = "部署ID") + @ApiModelProperty("部署ID") + private String deploymentId; + + /** + * 流程定义状态: 1:激活 , 2:中止 + */ + @ExcelProperty(value = "流程定义状态: 1:激活 , 2:中止") + @ApiModelProperty("流程定义状态: 1:激活 , 2:中止") + private Boolean suspended; + + /** + * 部署时间 + */ + @ExcelProperty(value = "部署时间") + @ApiModelProperty("部署时间") + private Date deploymentTime; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfModelVo.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfModelVo.java new file mode 100644 index 00000000..475eac71 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfModelVo.java @@ -0,0 +1,63 @@ +package com.ruoyi.workflow.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * @author KonBAI + * @createTime 2022/6/21 9:16 + */ +@Data +@ApiModel("流程模型视图对象") +@ExcelIgnoreUnannotated +public class WfModelVo { + + @ExcelProperty(value = "模型ID") + @ApiModelProperty("模型ID") + private String modelId; + + @ExcelProperty(value = "模型名称") + @ApiModelProperty("模型名称") + private String modelName; + + @ExcelProperty(value = "模型Key") + @ApiModelProperty("模型Key") + private String modelKey; + + @ExcelProperty(value = "分类编码") + @ApiModelProperty("分类编码") + private String category; + + @ExcelProperty(value = "版本") + @ApiModelProperty("版本") + private Integer version; + + @ExcelProperty(value = "表单类型") + @ApiModelProperty("表单类型") + private Integer formType; + + @ExcelProperty(value = "表单ID") + @ApiModelProperty("表单ID") + private Long formId; + + @ExcelProperty(value = "模型描述") + @ApiModelProperty("模型描述") + private String description; + + @ExcelProperty(value = "创建时间") + @ApiModelProperty("创建时间") + private Date createTime; + + @ExcelProperty(value = "流程xml") + @ApiModelProperty("流程xml") + private String bpmnXml; + + @ExcelProperty(value = "表单内容") + @ApiModelProperty("表单内容") + private String content; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployFormService.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployFormService.java index c8602c1a..81808972 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployFormService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployFormService.java @@ -19,6 +19,14 @@ public interface IWfDeployFormService { */ int insertWfDeployForm(WfDeployForm wfDeployForm); + /** + * 保存流程实例关联表单 + * @param deployId + * @param formId + * @return + */ + int saveInternalDeployForm(String deployId, Long formId); + /** * 查询流程挂着的表单 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployService.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployService.java new file mode 100644 index 00000000..b6af812b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfDeployService.java @@ -0,0 +1,21 @@ +package com.ruoyi.workflow.service; + +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.workflow.domain.bo.WfProcessBo; +import com.ruoyi.workflow.domain.vo.WfDeployVo; + +/** + * @author KonBAI + * @createTime 2022/6/30 9:03 + */ +public interface IWfDeployService { + + TableDataInfo queryPageList(WfProcessBo processBo, PageQuery pageQuery); + + TableDataInfo queryPublishList(String processKey, PageQuery pageQuery); + + void updateState(String definitionId, String stateCode); + + String queryBpmnXmlById(String definitionId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfModelService.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfModelService.java new file mode 100644 index 00000000..d9ce3f50 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/IWfModelService.java @@ -0,0 +1,31 @@ +package com.ruoyi.workflow.service; + +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.workflow.domain.bo.WfModelBo; +import com.ruoyi.workflow.domain.vo.WfModelVo; + +import java.util.Collection; + +/** + * @author KonBAI + * @createTime 2022/6/21 9:11 + */ +public interface IWfModelService { + + TableDataInfo list(WfModelBo modelBo, PageQuery pageQuery); + + TableDataInfo historyList(WfModelBo modelBo, PageQuery pageQuery); + + WfModelVo getModel(String modelId); + + String queryBpmnXmlById(String modelId); + + void saveModel(WfModelBo modelBo); + + void latestModel(String modelId); + + void deleteByIds(Collection ids); + + void deployModel(String modelId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployFormServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployFormServiceImpl.java index 739e1d4d..37524b7c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployFormServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployFormServiceImpl.java @@ -46,6 +46,20 @@ public class WfDeployFormServiceImpl implements IWfDeployFormService { return baseMapper.insert(deployForm); } + @Override + @Transactional(rollbackFor = Exception.class) + public int saveInternalDeployForm(String deployId, Long formId) { + WfDeployForm deployForm = new WfDeployForm(); + deployForm.setDeployId(deployId); + deployForm.setFormId(formId); + WfForm wfForm = formMapper.selectById(formId); + if (ObjectUtil.isNotNull(wfForm)) { + deployForm.setContent(wfForm.getContent()); + } + // 新增部署流程和表单关系 + return baseMapper.insert(deployForm); + } + /** * 查询流程挂着的表单 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployServiceImpl.java new file mode 100644 index 00000000..9d8d5ffc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfDeployServiceImpl.java @@ -0,0 +1,147 @@ +package com.ruoyi.workflow.service.impl; + +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.IoUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.workflow.domain.bo.WfProcessBo; +import com.ruoyi.workflow.domain.vo.WfDeployVo; +import com.ruoyi.workflow.service.IWfDeployService; +import lombok.RequiredArgsConstructor; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author KonBAI + * @createTime 2022/6/30 9:04 + */ +@RequiredArgsConstructor +@Service +public class WfDeployServiceImpl implements IWfDeployService { + + private final RepositoryService repositoryService; + + @Override + public TableDataInfo queryPageList(WfProcessBo processBo, PageQuery pageQuery) { + // 流程定义列表数据查询 + ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery() + .latestVersion() + .orderByProcessDefinitionKey() + .asc(); + if (StringUtils.isNotBlank(processBo.getProcessKey())) { + processDefinitionQuery.processDefinitionKeyLike("%" + processBo.getProcessKey() + "%"); + } + if (StringUtils.isNotBlank(processBo.getProcessName())) { + processDefinitionQuery.processDefinitionNameLike("%" + processBo.getProcessName() + "%"); + } + if (StringUtils.isNotBlank(processBo.getCategory())) { + processDefinitionQuery.processDefinitionCategory(processBo.getCategory()); + } + if (StringUtils.isNotBlank(processBo.getState())) { + if (SuspensionState.ACTIVE.toString().equals(processBo.getState())) { + processDefinitionQuery.active(); + } else if (SuspensionState.SUSPENDED.toString().equals(processBo.getState())) { + processDefinitionQuery.suspended(); + } + } + // SuspensionState.ACTIVE + long pageTotal = processDefinitionQuery.count(); + if (pageTotal <= 0) { + return TableDataInfo.build(); + } + int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1); + List definitionList = processDefinitionQuery.listPage(offset, pageQuery.getPageSize()); + + List deployVoList = new ArrayList<>(definitionList.size()); + for (ProcessDefinition processDefinition : definitionList) { + String deploymentId = processDefinition.getDeploymentId(); + Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult(); + WfDeployVo vo = new WfDeployVo(); + vo.setDefinitionId(processDefinition.getId()); + vo.setProcessKey(processDefinition.getKey()); + vo.setProcessName(processDefinition.getName()); + vo.setVersion(processDefinition.getVersion()); + vo.setCategory(processDefinition.getCategory()); + vo.setDeploymentId(processDefinition.getDeploymentId()); + vo.setSuspended(processDefinition.isSuspended()); + // 流程部署信息 + vo.setCategory(deployment.getCategory()); + vo.setDeploymentTime(deployment.getDeploymentTime()); + deployVoList.add(vo); + } + Page page = new Page<>(); + page.setRecords(deployVoList); + page.setTotal(pageTotal); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo queryPublishList(String processKey, PageQuery pageQuery) { + // 创建查询条件 + ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery() + .processDefinitionKey(processKey) + .orderByProcessDefinitionVersion() + .desc(); + long pageTotal = processDefinitionQuery.count(); + if (pageTotal <= 0) { + return TableDataInfo.build(); + } + // 根据查询条件,查询所有版本 + int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1); + List processDefinitionList = processDefinitionQuery + .listPage(offset, pageQuery.getPageSize()); + List deployVoList = processDefinitionList.stream().map(item -> { + WfDeployVo vo = new WfDeployVo(); + vo.setDefinitionId(item.getId()); + vo.setProcessKey(item.getKey()); + vo.setProcessName(item.getName()); + vo.setVersion(item.getVersion()); + vo.setCategory(item.getCategory()); + vo.setDeploymentId(item.getDeploymentId()); + vo.setSuspended(item.isSuspended()); + return vo; + }).collect(Collectors.toList()); + Page page = new Page<>(); + page.setRecords(deployVoList); + page.setTotal(pageTotal); + return TableDataInfo.build(page); + } + + /** + * 激活或挂起流程 + * + * @param state 状态 + * @param definitionId 流程定义ID + */ + @Override + public void updateState(String definitionId, String state) { + if (SuspensionState.ACTIVE.toString().equals(state)) { + // 激活 + repositoryService.activateProcessDefinitionById(definitionId, true, null); + } else if (SuspensionState.SUSPENDED.toString().equals(state)) { + // 挂起 + repositoryService.suspendProcessDefinitionById(definitionId, true, null); + } + } + + @Override + public String queryBpmnXmlById(String definitionId) { + InputStream inputStream = repositoryService.getProcessModel(definitionId); + try { + return IoUtil.readUtf8(inputStream); + } catch (IORuntimeException exception) { + throw new RuntimeException("加载xml文件异常"); + } + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java new file mode 100644 index 00000000..ec787e93 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java @@ -0,0 +1,286 @@ +package com.ruoyi.workflow.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.flowable.common.constant.ProcessConstants; +import com.ruoyi.flowable.common.enums.FormType; +import com.ruoyi.flowable.factory.FlowServiceFactory; +import com.ruoyi.flowable.utils.ModelUtils; +import com.ruoyi.workflow.domain.bo.WfModelBo; +import com.ruoyi.workflow.domain.dto.WfMetaInfoDto; +import com.ruoyi.workflow.domain.vo.WfFormVo; +import com.ruoyi.workflow.domain.vo.WfModelVo; +import com.ruoyi.workflow.service.IWfDeployFormService; +import com.ruoyi.workflow.service.IWfFormService; +import com.ruoyi.workflow.service.IWfModelService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author KonBAI + * @createTime 2022/6/21 9:11 + */ +@RequiredArgsConstructor +@Service +@Slf4j +public class WfModelServiceImpl extends FlowServiceFactory implements IWfModelService { + + private final IWfFormService formService; + private final IWfDeployFormService deployFormService; + + @Override + public TableDataInfo list(WfModelBo modelBo, PageQuery pageQuery) { + ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc(); + // 构建查询条件 + if (StringUtils.isNotBlank(modelBo.getModelKey())) { + modelQuery.modelKey(modelBo.getModelKey()); + } + if (StringUtils.isNotBlank(modelBo.getModelName())) { + modelQuery.modelNameLike("%" + modelBo.getModelName() + "%"); + } + if (StringUtils.isNotBlank(modelBo.getCategory())) { + modelQuery.modelCategory(modelBo.getCategory()); + } + // 执行查询 + long pageTotal = modelQuery.count(); + if (pageTotal <= 0) { + return TableDataInfo.build(); + } + int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1); + List modelList = modelQuery.listPage(offset, pageQuery.getPageSize()); + List modelVoList = new ArrayList<>(modelList.size()); + modelList.forEach(model -> { + WfModelVo modelVo = new WfModelVo(); + modelVo.setModelId(model.getId()); + modelVo.setModelName(model.getName()); + modelVo.setModelKey(model.getKey()); + modelVo.setCategory(model.getCategory()); + modelVo.setCreateTime(model.getCreateTime()); + modelVo.setVersion(model.getVersion()); + WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class); + if (metaInfo != null) { + modelVo.setDescription(metaInfo.getDescription()); + modelVo.setFormType(metaInfo.getFormType()); + modelVo.setFormId(metaInfo.getFormId()); + } + modelVoList.add(modelVo); + }); + Page page = new Page<>(); + page.setRecords(modelVoList); + page.setTotal(pageTotal); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo historyList(WfModelBo modelBo, PageQuery pageQuery) { + ModelQuery modelQuery = repositoryService.createModelQuery() + .modelKey(modelBo.getModelKey()) + .orderByModelVersion() + .desc(); + // 执行查询(不显示最新版,-1) + long pageTotal = modelQuery.count() - 1; + if (pageTotal <= 0) { + return TableDataInfo.build(); + } + // offset+1,去掉最新版 + int offset = 1 + pageQuery.getPageSize() * (pageQuery.getPageNum() - 1); + List modelList = modelQuery.listPage(offset, pageQuery.getPageSize()); + List modelVoList = new ArrayList<>(modelList.size()); + modelList.forEach(model -> { + WfModelVo modelVo = new WfModelVo(); + modelVo.setModelId(model.getId()); + modelVo.setModelName(model.getName()); + modelVo.setModelKey(model.getKey()); + modelVo.setCategory(model.getCategory()); + modelVo.setCreateTime(model.getCreateTime()); + modelVo.setVersion(model.getVersion()); + WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class); + if (metaInfo != null) { + modelVo.setDescription(metaInfo.getDescription()); + modelVo.setFormType(metaInfo.getFormType()); + modelVo.setFormId(metaInfo.getFormId()); + } + modelVoList.add(modelVo); + }); + Page page = new Page<>(); + page.setRecords(modelVoList); + page.setTotal(pageTotal); + return TableDataInfo.build(page); + } + + @Override + public WfModelVo getModel(String modelId) { + // 获取流程模型 + Model model = repositoryService.getModel(modelId); + if (ObjectUtil.isNull(model)) { + throw new RuntimeException("流程模型不存在!"); + } + // 获取流程图 + String bpmnXml = queryBpmnXmlById(modelId); + WfModelVo modelVo = new WfModelVo(); + modelVo.setModelId(model.getId()); + modelVo.setModelName(model.getName()); + modelVo.setModelKey(model.getKey()); + modelVo.setCategory(model.getCategory()); + modelVo.setCreateTime(model.getCreateTime()); + modelVo.setVersion(model.getVersion()); + modelVo.setBpmnXml(bpmnXml); + WfMetaInfoDto metaInfo = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDto.class); + if (metaInfo != null) { + modelVo.setDescription(metaInfo.getDescription()); + modelVo.setFormType(metaInfo.getFormType()); + modelVo.setFormId(metaInfo.getFormId()); + if (FormType.PROCESS.getType().equals(metaInfo.getFormType())) { + WfFormVo wfFormVo = formService.queryById(metaInfo.getFormId()); + modelVo.setContent(wfFormVo.getContent()); + } + } + return modelVo; + } + + @Override + public String queryBpmnXmlById(String modelId) { + byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId); + if (ObjectUtil.isNull(bpmnBytes)) { + throw new RuntimeException("流程图不存在!"); + } + return StrUtil.utf8Str(bpmnBytes); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveModel(WfModelBo modelBo) { + // 根据模型Key查询模型信息 + Model model = repositoryService.createModelQuery().modelKey(modelBo.getModelKey()).singleResult(); + Model newModel; + if (ObjectUtil.isNull(model)) { + newModel = repositoryService.newModel(); + } else { + if (modelBo.getNewVersion() != null && modelBo.getNewVersion()) { + newModel = repositoryService.newModel(); + newModel.setVersion(model.getVersion() + 1); + } else { + newModel = model; + } + } + newModel.setName(modelBo.getModelName()); + newModel.setKey(modelBo.getModelKey()); + newModel.setCategory(modelBo.getCategory()); + newModel.setMetaInfo(buildMetaInfo(modelBo)); + // 保存流程模型 + repositoryService.saveModel(newModel); + // 保存 BPMN XML + saveModelBpmnXml(newModel.getId(), modelBo.getBpmnXml()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void latestModel(String modelId) { + // 获取流程模型 + Model model = repositoryService.getModel(modelId); + if (ObjectUtil.isNull(model)) { + throw new RuntimeException("流程模型不存在!"); + } + String bpmnXml = queryBpmnXmlById(modelId); + Integer latestVersion = repositoryService.createModelQuery() + .modelKey(model.getKey()) + .latestVersion() + .singleResult() + .getVersion(); + if (model.getVersion().equals(latestVersion)) { + throw new RuntimeException("当前版本已是最新版!"); + } + Model newModel = repositoryService.newModel(); + newModel.setName(model.getName()); + newModel.setKey(model.getKey()); + newModel.setCategory(model.getCategory()); + newModel.setMetaInfo(model.getMetaInfo()); + newModel.setVersion(latestVersion + 1); + // 保存流程模型 + repositoryService.saveModel(newModel); + // 保存 BPMN XML + saveModelBpmnXml(newModel.getId(), bpmnXml); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByIds(Collection ids) { + ids.forEach(id -> { + Model model = repositoryService.getModel(id); + if (ObjectUtil.isNull(model)) { + throw new RuntimeException("流程模型不存在!"); + } + repositoryService.deleteModel(id); + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deployModel(String modelId) { + // 获取流程模型 + Model model = repositoryService.getModel(modelId); + if (ObjectUtil.isNull(model)) { + throw new RuntimeException("流程模型不存在!"); + } + WfMetaInfoDto metaInfo = getMetaInfo(model.getMetaInfo()); + // 获取流程图 + String bpmnXml = queryBpmnXmlById(modelId); + BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXml); + String processName = model.getName() + ProcessConstants.SUFFIX; + Deployment deployment = repositoryService.createDeployment() + .name(model.getName()) + .key(model.getKey()) + .addBpmnModel(processName, bpmnModel) + .category(model.getCategory()) + .deploy(); + // 保存部署表单 + if (FormType.PROCESS.getType().equals(metaInfo.getFormType())) { + deployFormService.saveInternalDeployForm(deployment.getId(), metaInfo.getFormId()); + } + } + + private String buildMetaInfo(WfModelBo modelBo) { + WfMetaInfoDto metaInfo = new WfMetaInfoDto(); + // 只有非空,才进行设置,避免更新时的覆盖 + if (StringUtils.isNotEmpty(modelBo.getDescription())) { + metaInfo.setDescription(modelBo.getDescription()); + } + if (ObjectUtil.isNotNull(modelBo.getFormType())) { + metaInfo.setFormType(modelBo.getFormType()); + metaInfo.setFormId(modelBo.getFormId()); + } + return JsonUtils.toJsonString(metaInfo); + } + + private WfMetaInfoDto getMetaInfo(String metaInfoJson) { + WfMetaInfoDto metaInfo = JsonUtils.parseObject(metaInfoJson, WfMetaInfoDto.class); + if (ObjectUtil.isNull(metaInfo) || ObjectUtil.hasNull(metaInfo.getFormType(), metaInfo.getFormId())) { + throw new RuntimeException("未配置表单信息!"); + } + return metaInfo; + } + + private void saveModelBpmnXml(String modelId, String bpmnXml) { + if (StringUtils.isBlank(modelId)) { + throw new RuntimeException("模板主键不能为空!"); + } + if (StringUtils.isNotEmpty(bpmnXml)) { + repositoryService.addModelEditorSource(modelId, StrUtil.utf8Bytes(bpmnXml)); + } + } +} diff --git a/ruoyi-ui/src/api/workflow/deploy.js b/ruoyi-ui/src/api/workflow/deploy.js index 098663c1..420af4f7 100644 --- a/ruoyi-ui/src/api/workflow/deploy.js +++ b/ruoyi-ui/src/api/workflow/deploy.js @@ -1,5 +1,40 @@ import request from '@/utils/request' +// 查询流程部署列表 +export function listDeploy(query) { + return request({ + url: '/workflow/deploy/list', + method: 'get', + params: query + }) +} + +export function listPublish(query) { + return request({ + url: '/workflow/deploy/publishList', + method: 'get', + params: query + }) +} + +// 获取流程模型流程图 +export function getBpmnXml(definitionId) { + return request({ + url: '/workflow/deploy/bpmnXml/' + definitionId, + method: 'get' + }) +} + +// 修改流程状态 +export function changeState(params) { + return request({ + url: '/workflow/deploy/changeState', + method: 'put', + params: params + }) +} + + // 查询流程部署关联表单信息 export function getFormByDeployId(deployId) { return request({ diff --git a/ruoyi-ui/src/api/workflow/model.js b/ruoyi-ui/src/api/workflow/model.js new file mode 100644 index 00000000..73165b03 --- /dev/null +++ b/ruoyi-ui/src/api/workflow/model.js @@ -0,0 +1,66 @@ +import request from '@/utils/request' + +// 查询流程模型信息 +export function listModel(query) { + return request({ + url: '/workflow/model/list', + method: 'get', + params: query + }) +} + +// 查询流程模型信息 +export function historyModel(query) { + return request({ + url: '/workflow/model/historyList', + method: 'get', + params: query + }) +} + +export function getModel(modelId) { + return request({ + url: '/workflow/model/' + modelId, + method: 'get' + }) +} + +// 保存流程模型 +export function saveModel(data) { + return request({ + url: '/workflow/model', + method: 'post', + data: data + }) +} + +export function latestModel(params) { + return request({ + url: '/workflow/model/latest', + method: 'post', + params: params + }) +} + +export function delModel(modelIds) { + return request({ + url: '/workflow/model/' + modelIds, + method: 'delete' + }) +} + +export function deployModel(params) { + return request({ + url: '/workflow/model/deploy', + method: 'post', + params: params + }) +} + +// 获取流程模型流程图 +export function getBpmnXml(modelId) { + return request({ + url: '/workflow/model/bpmnXml/' + modelId, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/views/workflow/deploy/index.vue b/ruoyi-ui/src/views/workflow/deploy/index.vue new file mode 100644 index 00000000..3fd6dbb5 --- /dev/null +++ b/ruoyi-ui/src/views/workflow/deploy/index.vue @@ -0,0 +1,302 @@ + + + + + diff --git a/ruoyi-ui/src/views/workflow/model/designer.vue b/ruoyi-ui/src/views/workflow/model/designer.vue new file mode 100644 index 00000000..edf6b347 --- /dev/null +++ b/ruoyi-ui/src/views/workflow/model/designer.vue @@ -0,0 +1,295 @@ + + + + + diff --git a/ruoyi-ui/src/views/workflow/model/index.vue b/ruoyi-ui/src/views/workflow/model/index.vue new file mode 100644 index 00000000..7893f7ab --- /dev/null +++ b/ruoyi-ui/src/views/workflow/model/index.vue @@ -0,0 +1,525 @@ + + +