Browse Source

完善 bpmnProcessDesigner 流程设计器的使用,基本可用了!

YunaiV 3 years ago
parent
commit
490f907ada
20 changed files with 242 additions and 135 deletions
  1. 6 9
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/form/BpmFormController.java
  2. 17 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/form/vo/BpmFormSimpleRespVO.java
  3. 5 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/model/vo/BpmModelRespVO.java
  4. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/form/BpmFormConvert.java
  5. 14 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/ModelConvert.java
  6. 2 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
  7. 8 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/form/BpmFormService.java
  8. 5 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/form/impl/BpmFormServiceImpl.java
  9. 14 5
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/model/impl/BpmModelServiceImpl.java
  10. 6 1
      yudao-admin-ui/src/api/bpm/form.js
  11. 14 1
      yudao-admin-ui/src/components/RADME.md
  12. 24 6
      yudao-admin-ui/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue
  13. 5 2
      yudao-admin-ui/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
  14. 42 25
      yudao-admin-ui/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue
  15. 1 2
      yudao-admin-ui/src/main.js
  16. 15 71
      yudao-admin-ui/src/views/bpm/model/index.vue
  17. 52 10
      yudao-admin-ui/src/views/bpm/model/modelEditor.vue
  18. 1 0
      yudao-admin-ui/vue.config.js
  19. 8 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java
  20. 1 0
      更新日志.md

+ 6 - 9
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/form/BpmFormController.java

@@ -1,9 +1,9 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.form;
 
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.form.BpmFormConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.form.BpmFormService;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.*;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import io.swagger.annotations.Api;
@@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.util.Collection;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -62,13 +61,11 @@ public class BpmFormController {
         return success(BpmFormConvert.INSTANCE.convert(form));
     }
 
-    @GetMapping("/list")
-    @ApiOperation("获得动态表单列表")
-    @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
-    @PreAuthorize("@ss.hasPermission('bpm:form:query')")
-    public CommonResult<List<BpmFormRespVO>> getFormList(@RequestParam("ids") Collection<Long> ids) {
-        List<BpmFormDO> list = formService.getFormList(ids);
-        return success(BpmFormConvert.INSTANCE.convertList(list));
+    @GetMapping("/list-all-simple")
+    @ApiOperation(value = "获得动态表单的精简列表", notes = "用于表单下拉框")
+    public CommonResult<List<BpmFormSimpleRespVO>> getSimpleForms() {
+        List<BpmFormDO> list = formService.getFormList();
+        return success(BpmFormConvert.INSTANCE.convertList2(list));
     }
 
     @GetMapping("/page")

+ 17 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/form/vo/BpmFormSimpleRespVO.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@ApiModel("流程表单精简 Response VO")
+@Data
+public class BpmFormSimpleRespVO {
+
+    @ApiModelProperty(value = "表单编号", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "表单名称", required = true, example = "芋道")
+    private String name;
+
+}

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/model/vo/BpmModelRespVO.java

@@ -6,6 +6,8 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import java.util.Date;
+
 @ApiModel("流程模型的创建 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -18,4 +20,7 @@ public class BpmModelRespVO extends BpmModelBaseVO {
     @ApiModelProperty(value = "BPMN XML", required = true)
     private String bpmnXml;
 
+    @ApiModelProperty(value = "创建时间", required = true)
+    private Date createTime;
+
 }

+ 2 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/form/BpmFormConvert.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.convert.form;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormCreateReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormSimpleRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormUpdateReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -26,7 +27,7 @@ public interface BpmFormConvert {
 
     BpmFormRespVO convert(BpmFormDO bean);
 
-    List<BpmFormRespVO> convertList(List<BpmFormDO> list);
+    List<BpmFormSimpleRespVO> convertList2(List<BpmFormDO> list);
 
     PageResult<BpmFormRespVO> convertPage(PageResult<BpmFormDO> page);
 

+ 14 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/ModelConvert.java

@@ -57,7 +57,20 @@ public interface ModelConvert {
         return modelRespVO;
     }
 
-    BpmModelRespVO convert(Model model);
+    default BpmModelRespVO convert(Model model) {
+        BpmModelRespVO modelRespVO = new BpmModelRespVO();
+        modelRespVO.setId(model.getId());
+        modelRespVO.setName(model.getName());
+        modelRespVO.setKey(model.getKey());
+        modelRespVO.setCategory(model.getCategory());
+        modelRespVO.setCreateTime(model.getCreateTime());
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
+        if (metaInfo != null) {
+            modelRespVO.setFormId(metaInfo.getFormId());
+            modelRespVO.setDescription(metaInfo.getDescription());
+        }
+        return modelRespVO;
+    }
 
     default BpmDefinitionCreateReqDTO convert2(Model model) {
         BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO();

+ 2 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java

@@ -26,6 +26,8 @@ public interface BpmErrorCodeConstants {
     // ========== OA 工作流模块 1-009-002-000 ==========
     ErrorCode BPM_MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程");
     ErrorCode BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在");
+    ErrorCode BPMN_MODEL_KEY_VALID = new ErrorCode(1009002002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
+
 
     ErrorCode BPMN_MODEL_ERROR = new ErrorCode(1004001002, "工作流模型异常");
     ErrorCode BPMN_MODEL_PROCESS_NOT_EXISTS = new ErrorCode(1004001009, "流程数据为空");

+ 8 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/form/BpmFormService.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormUpdate
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import org.activiti.engine.repository.Model;
 
 import javax.validation.Valid;
 import java.util.Collection;
@@ -52,6 +53,13 @@ public interface BpmFormService {
      */
     BpmFormDO getForm(Long id);
 
+    /**
+     * 获得动态表单列表
+     *
+     * @return 动态表单列表
+     */
+    List<BpmFormDO> getFormList();
+
     /**
      * 获得动态表单列表
      *

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/form/impl/BpmFormServiceImpl.java

@@ -75,6 +75,11 @@ public class BpmFormServiceImpl implements BpmFormService {
         return formMapper.selectById(id);
     }
 
+    @Override
+    public List<BpmFormDO> getFormList() {
+        return formMapper.selectList();
+    }
+
     @Override
     public List<BpmFormDO> getFormList(Collection<Long> ids) {
         return formMapper.selectBatchIds(ids);

+ 14 - 5
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/model/impl/BpmModelServiceImpl.java

@@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.activiti.engine.RepositoryService;
 import org.activiti.engine.repository.Model;
@@ -29,8 +30,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.BPMN_MODEL_NOT_EXISTS;
-import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.BPM_MODEL_KEY_EXISTS;
+import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 
@@ -89,6 +89,9 @@ public class BpmModelServiceImpl implements BpmModelService {
     @Override
     public BpmModelRespVO getModel(String id) {
         Model model = repositoryService.getModel(id);
+        if (model == null) {
+            return null;
+        }
         BpmModelRespVO modelRespVO = ModelConvert.INSTANCE.convert(model);
         // 拼接 bpmn XML
         byte[] bpmnBytes = repositoryService.getModelEditorSource(id);
@@ -99,12 +102,12 @@ public class BpmModelServiceImpl implements BpmModelService {
     @Override
     @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务
     public String createModel(BpmModelCreateReqVO createReqVO) {
+        checkKeyNCName(createReqVO.getKey());
         // 校验流程标识已经存在
         Model keyModel = this.getModelByKey(createReqVO.getKey());
         if (keyModel != null) {
-            throw exception(BPM_MODEL_KEY_EXISTS);
+            throw exception(BPM_MODEL_KEY_EXISTS, createReqVO.getKey());
         }
-        // TODO @芋艿:需要校验下 key 的格式
 
         // 创建流程定义
         Model model = repositoryService.newModel();
@@ -119,12 +122,12 @@ public class BpmModelServiceImpl implements BpmModelService {
     @Override
     @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务
     public void updateModel(BpmModelUpdateReqVO updateReqVO) {
+        checkKeyNCName(updateReqVO.getKey());
         // 校验流程模型存在
         Model model = repositoryService.getModel(updateReqVO.getId());
         if (model == null) {
             throw exception(BPMN_MODEL_NOT_EXISTS);
         }
-        // TODO @芋艿:需要校验下 key 的格式
 
         // 修改流程定义
         ModelConvert.INSTANCE.copy(model, updateReqVO);
@@ -173,4 +176,10 @@ public class BpmModelServiceImpl implements BpmModelService {
         return repositoryService.createModelQuery().modelKey(key).singleResult();
     }
 
+    private void checkKeyNCName(String key) {
+        if (!ValidationUtils.isXmlNCName(key)) {
+            throw exception(BPMN_MODEL_KEY_VALID);
+        }
+    }
+
 }

+ 6 - 1
yudao-admin-ui/src/api/bpm/form.js

@@ -43,5 +43,10 @@ export function getFormPage(query) {
   })
 }
 
-export class exportFormExcel {
+// 获得动态表单的精简列表
+export function getSimpleForms() {
+  return request({
+    url: '/bpm/form/list-all-simple',
+    method: 'get'
+  })
 }

+ 14 - 1
yudao-admin-ui/src/components/RADME.md

@@ -5,4 +5,17 @@ github 地址:https://github.com/JakHuang/form-generator
 * generator
 * parser
 * render
-* tinymce
+* tinymce
+
+## bpmn-process-designer
+
+github 地址:https://github.com/miyuesc/bpmn-process-designer
+
+* bpmnProcessDesigner
+
+TODO 目前存在的问题,如果选择 activiti 类型时,因为不支持内置表单的设计,所以会报 Error: unknown type activiti:FormData 错误。具体可见 https://github.com/miyuesc/bpmn-process-designer/issues/16 。
+
+另外几个流程设计器的选型:
+
+* https://gitee.com/jimlow/vue-bpmn 相比差一些,已经停止维护,不算推荐。
+* https://github.com/GoldSubmarine/workflow-bpmn-modeler 仅支持 flowable 流程引擎。如果只考虑 flowable 的话,也是非常不错的选择。

+ 24 - 6
yudao-admin-ui/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue

@@ -4,7 +4,7 @@
       <slot name="control-header"></slot>
       <template v-if="!$slots['control-header']">
         <el-button-group key="file-control">
-          <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-folder-opened" @click="$refs.refFile.click()">打开文件</el-button>
+          <el-button :size="headerButtonSize" icon="el-icon-folder-opened" @click="$refs.refFile.click()">打开文件</el-button>
           <el-tooltip effect="light">
             <div slot="content">
               <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsXml()">下载为XML文件</el-button>
@@ -13,7 +13,7 @@
               <br />
               <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button>
             </div>
-            <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-download">下载文件</el-button>
+            <el-button :size="headerButtonSize" icon="el-icon-download">下载文件</el-button>
           </el-tooltip>
           <el-tooltip effect="light">
             <div slot="content">
@@ -21,10 +21,10 @@
               <br />
               <el-button :size="headerButtonSize" type="text" @click="previewProcessJson">预览JSON</el-button>
             </div>
-            <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-view">预览</el-button>
+            <el-button :size="headerButtonSize" icon="el-icon-view">预览</el-button>
           </el-tooltip>
           <el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '退出模拟' : '开启模拟'">
-            <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-cpu" @click="processSimulation">
+            <el-button :size="headerButtonSize" icon="el-icon-cpu" @click="processSimulation">
               模拟
             </el-button>
           </el-tooltip>
@@ -72,6 +72,7 @@
             <el-button :size="headerButtonSize" icon="el-icon-refresh" @click="processRestart" />
           </el-tooltip>
         </el-button-group>
+        <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-plus" @click="processSave">保存模型</el-button>
       </template>
       <!-- 用于打开本地文件-->
       <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" />
@@ -111,8 +112,9 @@ export default {
   componentName: "MyProcessDesigner",
   props: {
     value: String, // xml 字符串
-    processId: String,
-    processName: String,
+    processId: String, // 流程 key 标识
+    processName: String, // 流程 name 名字
+    formId: Number, // 流程 form 表单编号
     translations: Object, // 自定义的翻译文件
     additionalModel: [Object, Array], // 自定义model
     moddleExtension: Object, // 自定义moddle
@@ -244,6 +246,11 @@ export default {
       this.bpmnModeler = null;
     });
   },
+  watch: {
+    value: function (newValue) { // 在 xmlString 发生变化时,重新创建,从而绘制流程图
+      this.createNewDiagram(newValue);
+    }
+  },
   methods: {
     initBpmnModeler() {
       if (this.bpmnModeler) return;
@@ -445,6 +452,17 @@ export default {
         this.previewType = "json";
         this.previewModelVisible = true;
       });
+    },
+    /* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
+    async processSave() {
+      const { err, xml } = await this.bpmnModeler.saveXML();
+      // 读取异常时抛出异常
+      if (err) {
+        this.msgError('保存模型失败,请重试!')
+        return
+      }
+      // 触发 save 事件
+      this.$emit('save', xml)
     }
   }
 };

+ 5 - 2
yudao-admin-ui/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue

@@ -3,7 +3,8 @@
     <el-collapse v-model="activeTab">
       <el-collapse-item name="base">
         <div slot="title" class="panel-tab__title"><i class="el-icon-info"></i>常规</div>
-        <element-base-info :id-edit-disabled="idEditDisabled" :business-object="elementBusinessObject" :type="elementType" />
+        <element-base-info :id-edit-disabled="idEditDisabled" :business-object="elementBusinessObject" :type="elementType"
+                           :model="model" />
       </el-collapse-item>
       <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message">
         <div slot="title" class="panel-tab__title"><i class="el-icon-s-comment"></i>消息与信号</div>
@@ -89,7 +90,8 @@ export default {
     idEditDisabled: {
       type: Boolean,
       default: false
-    }
+    },
+    model: Object, // 流程模型的数据
   },
   provide() {
     return {
@@ -159,6 +161,7 @@ export default {
     // 初始化数据
     initFormOnChanged(element) {
       let activatedElement = element;
+      // debugger
       if (!activatedElement) {
         activatedElement =
           window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ??

+ 42 - 25
yudao-admin-ui/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue

@@ -1,43 +1,53 @@
 <template>
   <div class="panel-tab__content">
-    <el-form size="mini" label-width="90px" @submit.native.prevent>
-      <el-form-item label="ID">
-        <el-input
-          v-model="elementBaseInfo.id"
-          :disabled="idEditDisabled || elementBaseInfo.$type === 'bpmn:Process'"
-          clearable
-          @change="updateBaseInfo('id')"
-        />
+    <el-form size="mini" label-width="90px" :model="model" :rules="rules" @submit.native.prevent>
+      <el-form-item label="流程标识" prop="key">
+        <el-input v-model="model.key" placeholder="请输入流标标识"
+                  :disabled="model.id !== undefined && model.id.length > 0"/>
       </el-form-item>
-      <el-form-item label="名称">
-        <el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('name')" />
+      <el-form-item label="流程名称" prop="name">
+        <el-input v-model="model.name" placeholder="请输入流程名称" clearable />
+      </el-form-item>
+      <el-form-item label="流程分类" prop="category">
+        <el-select v-model="model.category" placeholder="请选择流程分类" clearable style="width: 100%">
+          <el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="流程表单" prop="formId">
+        <el-select v-model="model.formId" placeholder="请选择流程表单,非必选哟!" clearable style="width: 100%">
+          <el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="流程描述" prop="description">
+        <el-input type="textarea" v-model="model.description" clearable />
       </el-form-item>
-      <!--流程的基础属性-->
-      <template v-if="elementBaseInfo.$type === 'bpmn:Process'">
-        <el-form-item label="版本标签">
-          <el-input v-model="elementBaseInfo.versionTag" clearable @change="updateBaseInfo('versionTag')" />
-        </el-form-item>
-        <el-form-item label="可执行">
-          <el-switch v-model="elementBaseInfo.isExecutable" active-text="是" inactive-text="否" @change="updateBaseInfo('isExecutable')" />
-        </el-form-item>
-      </template>
     </el-form>
   </div>
 </template>
 <script>
+import {DICT_TYPE, getDictDatas} from "@/utils/dict";
+import {getSimpleForms} from "@/api/bpm/form";
+import {getModel} from "@/api/bpm/model";
+
 export default {
   name: "ElementBaseInfo",
   props: {
     businessObject: Object,
-    type: String,
-    idEditDisabled: {
-      type: Boolean,
-      default: true
-    }
+    model: Object, // 流程模型的数据
   },
   data() {
     return {
-      elementBaseInfo: {}
+      elementBaseInfo: {},
+      // 流程表单的下拉框的数据
+      forms: [],
+      // 流程模型的校验
+      rules: {
+        key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
+        name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
+        category: [{ required: true, message: "流程分类不能为空", trigger: "blur" }],
+      },
+      // 数据字典
+      categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
     };
   },
   watch: {
@@ -50,12 +60,19 @@ export default {
       }
     }
   },
+  created() {
+    // 获得流程表单的下拉框的数据
+    getSimpleForms().then(response => {
+      this.forms = response.data
+    })
+  },
   methods: {
     resetBaseInfo() {
       this.bpmnElement = window?.bpmnInstances?.bpmnElement;
       this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject));
     },
     updateBaseInfo(key) {
+      // 触发 elementBaseInfo 对应的字段
       const attrObj = Object.create(null);
       attrObj[key] = this.elementBaseInfo[key];
       if (key === "id") {

+ 1 - 2
yudao-admin-ui/src/main.js

@@ -76,11 +76,10 @@ Vue.component('RightToolbar', RightToolbar)
 Vue.use(permission)
 // Vue.use(hljs.vuePlugin);
 
-// TODO 芋艿:bpmnProcessDesigner 引入
+// bpmnProcessDesigner 需要引入
 import MyPD from "@/components/bpmnProcessDesigner/package/index.js";
 Vue.use(MyPD);
 import "@/components/bpmnProcessDesigner/package/theme/index.scss";
-// TODO 芋艿:bpmnProcessDesigner 引入
 import "bpmn-js/dist/assets/diagram-js.css";
 import "bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
 import "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";

+ 15 - 71
yudao-admin-ui/src/views/bpm/model/index.vue

@@ -67,12 +67,6 @@
     <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
                 @pagination="getList"/>
 
-    <!-- 流程编辑器 -->
-<!--    <el-dialog class="bpmnclass dialogClass" :visible.sync="showBpmnOpen" :before-cancel="cancel" :fullscreen="true">-->
-<!--      <vue-bpmn v-if="showBpmnOpen" product="activiti" @processSave="processSave"-->
-<!--                :bpmnXml="bpmnXML" :bpmnData="bpmnData" @beforeClose="cancel" />-->
-<!--    </el-dialog>-->
-
     <!-- 流程表单配置详情 -->
     <el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
       <div class="test-form">
@@ -83,8 +77,7 @@
 </template>
 
 <script>
-import {deleteModel, deployModel, createModel, updateModel, getModelPage, getModel} from "@/api/bpm/model";
-// import VueBpmn from "@/components/bpmn/VueBpmn";
+import {deleteModel, deployModel, getModelPage} from "@/api/bpm/model";
 import {DICT_TYPE, getDictDatas} from "@/utils/dict";
 import {getForm} from "@/api/bpm/form";
 import {decodeFields} from "@/utils/formGenerator";
@@ -93,8 +86,7 @@ import Parser from '@/components/parser/Parser'
 export default {
   name: "model",
   components: {
-    Parser,
-    // VueBpmn
+    Parser
   },
   data() {
     return {
@@ -157,55 +149,22 @@ export default {
       this.resetForm("queryForm");
       this.handleQuery();
     },
-    processSave(data) {
-      // TODO 芋艿:临时写死的参数
-      data.category = '1'
-      data.formId = 11
-
-      // 修改的提交
-      if (data.id) {
-        updateModel(data).then(response => {
-          this.msgSuccess("修改成功");
-          // 关闭弹窗,刷新列表
-          this.showBpmnOpen = false
-          this.getList();
-        })
-        return
-      }
-      // 添加的提交
-      createModel(data).then(response => {
-        this.bpmnData.id = response.data
-        this.msgSuccess("保存成功");
-        // 关闭弹窗,刷新列表
-        this.showBpmnOpen = false
-        this.getList();
-      })
-    },
+    /** 新增按钮操作 */
     handleAdd() {
-      // 重置 Model 信息
-      this.reset()
-      // 打开弹窗
-      this.showBpmnOpen = true
-    },
-    cancel() {
-      // 打开弹窗
-      this.showBpmnOpen = false
-      // 重置 Model 信息
-      this.reset()
-      // 刷新列表
-      this.getList()
+      this.$router.push({
+        path:"/bpm/manager/model/edit"
+      });
     },
+    /** 修改按钮操作 */
     handleUpdate(row) {
-      // 重置 Model 信息
-      this.reset()
-      // 获得 Model 信息
-      getModel(row.id).then(response => {
-        this.bpmnXML = response.data.bpmnXml
-        this.bpmnData = response.data
-        // 打开弹窗
-        this.showBpmnOpen = true
-      })
+      this.$router.push({
+        path:"/bpm/manager/model/edit",
+        query:{
+          modelId: row.id
+        }
+      });
     },
+    /** 删除按钮操作 */
     handleDelete(row) {
       const that = this;
       this.$confirm('是否删除该流程!!', "警告", {
@@ -219,6 +178,7 @@ export default {
         })
       })
     },
+    /** 部署按钮操作 */
     handleDeploy(row) {
       const that = this;
       this.$confirm('是否部署该流程!!', "提示", {
@@ -248,19 +208,3 @@ export default {
   }
 };
 </script>
-<style>
-.el-dialog > .el-dialog__body{
-  margin: 0;
-  border: 0;
-}
-.bpmn-viewer-header{
-  background: white;
-}
-.v-modal{
-  z-index: 2000!important;
-}
-.dialogClass{
-  padding: 0  ;
-}
-</style>
-

+ 52 - 10
yudao-admin-ui/src/views/bpm/model/modelEditor.vue

@@ -1,11 +1,14 @@
 <template>
   <div class="app-container">
 
-    <!-- 流程编辑器 -->
+    <!-- 流程设计器,负责绘制流程等 -->
     <my-process-designer :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm"
-      keyboard ref="processDesigner" @init-finished="initModeler"/>
-    <!-- 右边工具栏 -->
-    <my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel" />
+      keyboard ref="processDesigner" @init-finished="initModeler"
+      @save="save"/>
+
+    <!-- 流程属性器,负责编辑每个流程节点的属性 -->
+    <my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel"
+      :model="model" />
 
   </div>
 </template>
@@ -18,6 +21,7 @@ import CustomContentPadProvider from "@/components/bpmnProcessDesigner/package/d
 import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette";
 // import xmlObj2json from "./utils/xml2json";
 import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette";
+import {createModel, getModel, updateModel} from "@/api/bpm/model";
 // 自定义侧边栏
 // import MyProcessPanel from "../package/process-panel/ProcessPanel";
 
@@ -32,23 +36,34 @@ export default {
       controlDrawerVisible: false,
       translationsSelf: translations,
       controlForm: {
-        processId: "",
-        processName: "",
         simulation: true,
         labelEditing: false,
         labelVisible: false,
         prefix: "activiti",
         headerButtonSize: "mini",
-        // additionalModel: []
         additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
       },
       addis: {
         CustomContentPadProvider,
         CustomPaletteProvider
-      }
+      },
+      // 流程模型的信息
+      model: {},
     };
   },
-  created() {},
+  created() {
+    // 如果 modelId 非空,说明是修改流程模型
+    const modelId = this.$route.query && this.$route.query.modelId
+    if (modelId) {
+      getModel(modelId).then(response => {
+        this.xmlString = response.data.bpmnXml
+        this.model = {
+          ...response.data,
+          bpmnXml: undefined, // 清空 bpmnXml 属性
+        }
+      })
+    }
+  },
   methods: {
     initModeler(modeler) {
       setTimeout(() => {
@@ -70,7 +85,34 @@ export default {
       //   this.xmlString = undefined;
       //   this.$refs.processDesigner.processRestart();
       // }
-    }
+    },
+    save(bpmnXml) {
+      const data = {
+        ...this.model,
+        bpmnXml: bpmnXml, // this.bpmnXml 只是初始化流程图,后续修改无法通过它获得
+      }
+
+      // 修改的提交
+      if (data.id) {
+        updateModel(data).then(response => {
+          this.msgSuccess("修改成功")
+          // 跳转回去
+          this.close()
+        })
+        return
+      }
+      // 添加的提交
+      createModel(data).then(response => {
+        this.msgSuccess("保存成功")
+        // 跳转回去
+        this.close()
+      })
+    },
+    /** 关闭按钮 */
+    close() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.push({ path: "/bpm/manager/model", query: { t: Date.now()}})
+    },
   }
 };
 </script>

+ 1 - 0
yudao-admin-ui/vue.config.js

@@ -35,6 +35,7 @@ module.exports = {
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
         target: `http://localhost:48080`,
+        // target: `http://api-dashboard.yudao.iocoder.cn`,
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''

+ 8 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java

@@ -12,7 +12,9 @@ import java.util.regex.Pattern;
  */
 public class ValidationUtils {
 
-    private static Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
+    private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
+
+    private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
 
     public static boolean isMobile(String mobile) {
         if (StrUtil.length(mobile) != 11) {
@@ -27,4 +29,9 @@ public class ValidationUtils {
                 && PATTERN_URL.matcher(url).matches();
     }
 
+    public static boolean isXmlNCName(String str) {
+        return StringUtils.hasText(str)
+                && PATTERN_XML_NCNAME.matcher(str).matches();
+    }
+
 }

+ 1 - 0
更新日志.md

@@ -28,6 +28,7 @@
 
 * 【优化】引入 form generator 0.2.0 版本,并重构相关代码
 * 【新增】新增流程表单,支持动态进行表单的配置
+* 【新增】引入 bpmn-process-designer 0.0.1 版本,提供流程设计器的能力
 
 ### 🐞 Bug Fixes