Commit 490f907ada7c51b325bf493013b789b4d4e964ba

Authored by YunaiV
1 parent c4003396

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

Showing 20 changed files with 242 additions and 135 deletions
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/form/BpmFormController.java
1 package cn.iocoder.yudao.adminserver.modules.bpm.controller.form; 1 package cn.iocoder.yudao.adminserver.modules.bpm.controller.form;
2 2
  3 +import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.*;
3 import cn.iocoder.yudao.adminserver.modules.bpm.convert.form.BpmFormConvert; 4 import cn.iocoder.yudao.adminserver.modules.bpm.convert.form.BpmFormConvert;
4 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO; 5 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
5 import cn.iocoder.yudao.adminserver.modules.bpm.service.form.BpmFormService; 6 import cn.iocoder.yudao.adminserver.modules.bpm.service.form.BpmFormService;
6 -import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.*;  
7 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import io.swagger.annotations.Api; 9 import io.swagger.annotations.Api;
@@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*; @@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
15 15
16 import javax.annotation.Resource; 16 import javax.annotation.Resource;
17 import javax.validation.Valid; 17 import javax.validation.Valid;
18 -import java.util.Collection;  
19 import java.util.List; 18 import java.util.List;
20 19
21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -62,13 +61,11 @@ public class BpmFormController { @@ -62,13 +61,11 @@ public class BpmFormController {
62 return success(BpmFormConvert.INSTANCE.convert(form)); 61 return success(BpmFormConvert.INSTANCE.convert(form));
63 } 62 }
64 63
65 - @GetMapping("/list")  
66 - @ApiOperation("获得动态表单列表")  
67 - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)  
68 - @PreAuthorize("@ss.hasPermission('bpm:form:query')")  
69 - public CommonResult<List<BpmFormRespVO>> getFormList(@RequestParam("ids") Collection<Long> ids) {  
70 - List<BpmFormDO> list = formService.getFormList(ids);  
71 - return success(BpmFormConvert.INSTANCE.convertList(list)); 64 + @GetMapping("/list-all-simple")
  65 + @ApiOperation(value = "获得动态表单的精简列表", notes = "用于表单下拉框")
  66 + public CommonResult<List<BpmFormSimpleRespVO>> getSimpleForms() {
  67 + List<BpmFormDO> list = formService.getFormList();
  68 + return success(BpmFormConvert.INSTANCE.convertList2(list));
72 } 69 }
73 70
74 @GetMapping("/page") 71 @GetMapping("/page")
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/form/vo/BpmFormSimpleRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +@ApiModel("流程表单精简 Response VO")
  8 +@Data
  9 +public class BpmFormSimpleRespVO {
  10 +
  11 + @ApiModelProperty(value = "表单编号", required = true, example = "1024")
  12 + private Long id;
  13 +
  14 + @ApiModelProperty(value = "表单名称", required = true, example = "芋道")
  15 + private String name;
  16 +
  17 +}
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/model/vo/BpmModelRespVO.java
@@ -6,6 +6,8 @@ import lombok.Data; @@ -6,6 +6,8 @@ import lombok.Data;
6 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
7 import lombok.ToString; 7 import lombok.ToString;
8 8
  9 +import java.util.Date;
  10 +
9 @ApiModel("流程模型的创建 Request VO") 11 @ApiModel("流程模型的创建 Request VO")
10 @Data 12 @Data
11 @EqualsAndHashCode(callSuper = true) 13 @EqualsAndHashCode(callSuper = true)
@@ -18,4 +20,7 @@ public class BpmModelRespVO extends BpmModelBaseVO { @@ -18,4 +20,7 @@ public class BpmModelRespVO extends BpmModelBaseVO {
18 @ApiModelProperty(value = "BPMN XML", required = true) 20 @ApiModelProperty(value = "BPMN XML", required = true)
19 private String bpmnXml; 21 private String bpmnXml;
20 22
  23 + @ApiModelProperty(value = "创建时间", required = true)
  24 + private Date createTime;
  25 +
21 } 26 }
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; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.convert.form;
2 2
3 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormCreateReqVO; 3 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormCreateReqVO;
4 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormRespVO; 4 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormRespVO;
  5 +import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormSimpleRespVO;
5 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormUpdateReqVO; 6 import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormUpdateReqVO;
6 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO; 7 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -26,7 +27,7 @@ public interface BpmFormConvert { @@ -26,7 +27,7 @@ public interface BpmFormConvert {
26 27
27 BpmFormRespVO convert(BpmFormDO bean); 28 BpmFormRespVO convert(BpmFormDO bean);
28 29
29 - List<BpmFormRespVO> convertList(List<BpmFormDO> list); 30 + List<BpmFormSimpleRespVO> convertList2(List<BpmFormDO> list);
30 31
31 PageResult<BpmFormRespVO> convertPage(PageResult<BpmFormDO> page); 32 PageResult<BpmFormRespVO> convertPage(PageResult<BpmFormDO> page);
32 33
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/model/ModelConvert.java
@@ -57,7 +57,20 @@ public interface ModelConvert { @@ -57,7 +57,20 @@ public interface ModelConvert {
57 return modelRespVO; 57 return modelRespVO;
58 } 58 }
59 59
60 - BpmModelRespVO convert(Model model); 60 + default BpmModelRespVO convert(Model model) {
  61 + BpmModelRespVO modelRespVO = new BpmModelRespVO();
  62 + modelRespVO.setId(model.getId());
  63 + modelRespVO.setName(model.getName());
  64 + modelRespVO.setKey(model.getKey());
  65 + modelRespVO.setCategory(model.getCategory());
  66 + modelRespVO.setCreateTime(model.getCreateTime());
  67 + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
  68 + if (metaInfo != null) {
  69 + modelRespVO.setFormId(metaInfo.getFormId());
  70 + modelRespVO.setDescription(metaInfo.getDescription());
  71 + }
  72 + return modelRespVO;
  73 + }
61 74
62 default BpmDefinitionCreateReqDTO convert2(Model model) { 75 default BpmDefinitionCreateReqDTO convert2(Model model) {
63 BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO(); 76 BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO();
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
@@ -26,6 +26,8 @@ public interface BpmErrorCodeConstants { @@ -26,6 +26,8 @@ public interface BpmErrorCodeConstants {
26 // ========== OA 工作流模块 1-009-002-000 ========== 26 // ========== OA 工作流模块 1-009-002-000 ==========
27 ErrorCode BPM_MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程"); 27 ErrorCode BPM_MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程");
28 ErrorCode BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在"); 28 ErrorCode BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在");
  29 + ErrorCode BPMN_MODEL_KEY_VALID = new ErrorCode(1009002002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
  30 +
29 31
30 ErrorCode BPMN_MODEL_ERROR = new ErrorCode(1004001002, "工作流模型异常"); 32 ErrorCode BPMN_MODEL_ERROR = new ErrorCode(1004001002, "工作流模型异常");
31 ErrorCode BPMN_MODEL_PROCESS_NOT_EXISTS = new ErrorCode(1004001009, "流程数据为空"); 33 ErrorCode BPMN_MODEL_PROCESS_NOT_EXISTS = new ErrorCode(1004001009, "流程数据为空");
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 @@ -7,6 +7,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.form.vo.BpmFormUpdate
7 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO; 7 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 9 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  10 +import org.activiti.engine.repository.Model;
10 11
11 import javax.validation.Valid; 12 import javax.validation.Valid;
12 import java.util.Collection; 13 import java.util.Collection;
@@ -55,6 +56,13 @@ public interface BpmFormService { @@ -55,6 +56,13 @@ public interface BpmFormService {
55 /** 56 /**
56 * 获得动态表单列表 57 * 获得动态表单列表
57 * 58 *
  59 + * @return 动态表单列表
  60 + */
  61 + List<BpmFormDO> getFormList();
  62 +
  63 + /**
  64 + * 获得动态表单列表
  65 + *
58 * @param ids 编号 66 * @param ids 编号
59 * @return 动态表单列表 67 * @return 动态表单列表
60 */ 68 */
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/form/impl/BpmFormServiceImpl.java
@@ -76,6 +76,11 @@ public class BpmFormServiceImpl implements BpmFormService { @@ -76,6 +76,11 @@ public class BpmFormServiceImpl implements BpmFormService {
76 } 76 }
77 77
78 @Override 78 @Override
  79 + public List<BpmFormDO> getFormList() {
  80 + return formMapper.selectList();
  81 + }
  82 +
  83 + @Override
79 public List<BpmFormDO> getFormList(Collection<Long> ids) { 84 public List<BpmFormDO> getFormList(Collection<Long> ids) {
80 return formMapper.selectBatchIds(ids); 85 return formMapper.selectBatchIds(ids);
81 } 86 }
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; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
13 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 13 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
14 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 14 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
15 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 15 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
  16 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
16 import lombok.extern.slf4j.Slf4j; 17 import lombok.extern.slf4j.Slf4j;
17 import org.activiti.engine.RepositoryService; 18 import org.activiti.engine.RepositoryService;
18 import org.activiti.engine.repository.Model; 19 import org.activiti.engine.repository.Model;
@@ -29,8 +30,7 @@ import java.util.List; @@ -29,8 +30,7 @@ import java.util.List;
29 import java.util.Map; 30 import java.util.Map;
30 import java.util.Set; 31 import java.util.Set;
31 32
32 -import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.BPMN_MODEL_NOT_EXISTS;  
33 -import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.BPM_MODEL_KEY_EXISTS; 33 +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
35 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 35 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
36 36
@@ -89,6 +89,9 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -89,6 +89,9 @@ public class BpmModelServiceImpl implements BpmModelService {
89 @Override 89 @Override
90 public BpmModelRespVO getModel(String id) { 90 public BpmModelRespVO getModel(String id) {
91 Model model = repositoryService.getModel(id); 91 Model model = repositoryService.getModel(id);
  92 + if (model == null) {
  93 + return null;
  94 + }
92 BpmModelRespVO modelRespVO = ModelConvert.INSTANCE.convert(model); 95 BpmModelRespVO modelRespVO = ModelConvert.INSTANCE.convert(model);
93 // 拼接 bpmn XML 96 // 拼接 bpmn XML
94 byte[] bpmnBytes = repositoryService.getModelEditorSource(id); 97 byte[] bpmnBytes = repositoryService.getModelEditorSource(id);
@@ -99,12 +102,12 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -99,12 +102,12 @@ public class BpmModelServiceImpl implements BpmModelService {
99 @Override 102 @Override
100 @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务 103 @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务
101 public String createModel(BpmModelCreateReqVO createReqVO) { 104 public String createModel(BpmModelCreateReqVO createReqVO) {
  105 + checkKeyNCName(createReqVO.getKey());
102 // 校验流程标识已经存在 106 // 校验流程标识已经存在
103 Model keyModel = this.getModelByKey(createReqVO.getKey()); 107 Model keyModel = this.getModelByKey(createReqVO.getKey());
104 if (keyModel != null) { 108 if (keyModel != null) {
105 - throw exception(BPM_MODEL_KEY_EXISTS); 109 + throw exception(BPM_MODEL_KEY_EXISTS, createReqVO.getKey());
106 } 110 }
107 - // TODO @芋艿:需要校验下 key 的格式  
108 111
109 // 创建流程定义 112 // 创建流程定义
110 Model model = repositoryService.newModel(); 113 Model model = repositoryService.newModel();
@@ -119,12 +122,12 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -119,12 +122,12 @@ public class BpmModelServiceImpl implements BpmModelService {
119 @Override 122 @Override
120 @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务 123 @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务
121 public void updateModel(BpmModelUpdateReqVO updateReqVO) { 124 public void updateModel(BpmModelUpdateReqVO updateReqVO) {
  125 + checkKeyNCName(updateReqVO.getKey());
122 // 校验流程模型存在 126 // 校验流程模型存在
123 Model model = repositoryService.getModel(updateReqVO.getId()); 127 Model model = repositoryService.getModel(updateReqVO.getId());
124 if (model == null) { 128 if (model == null) {
125 throw exception(BPMN_MODEL_NOT_EXISTS); 129 throw exception(BPMN_MODEL_NOT_EXISTS);
126 } 130 }
127 - // TODO @芋艿:需要校验下 key 的格式  
128 131
129 // 修改流程定义 132 // 修改流程定义
130 ModelConvert.INSTANCE.copy(model, updateReqVO); 133 ModelConvert.INSTANCE.copy(model, updateReqVO);
@@ -173,4 +176,10 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -173,4 +176,10 @@ public class BpmModelServiceImpl implements BpmModelService {
173 return repositoryService.createModelQuery().modelKey(key).singleResult(); 176 return repositoryService.createModelQuery().modelKey(key).singleResult();
174 } 177 }
175 178
  179 + private void checkKeyNCName(String key) {
  180 + if (!ValidationUtils.isXmlNCName(key)) {
  181 + throw exception(BPMN_MODEL_KEY_VALID);
  182 + }
  183 + }
  184 +
176 } 185 }
yudao-admin-ui/src/api/bpm/form.js
@@ -43,5 +43,10 @@ export function getFormPage(query) { @@ -43,5 +43,10 @@ export function getFormPage(query) {
43 }) 43 })
44 } 44 }
45 45
46 -export class exportFormExcel { 46 +// 获得动态表单的精简列表
  47 +export function getSimpleForms() {
  48 + return request({
  49 + url: '/bpm/form/list-all-simple',
  50 + method: 'get'
  51 + })
47 } 52 }
yudao-admin-ui/src/components/RADME.md
@@ -5,4 +5,17 @@ github 地址:https://github.com/JakHuang/form-generator @@ -5,4 +5,17 @@ github 地址:https://github.com/JakHuang/form-generator
5 * generator 5 * generator
6 * parser 6 * parser
7 * render 7 * render
8 -* tinymce  
9 \ No newline at end of file 8 \ No newline at end of file
  9 +* tinymce
  10 +
  11 +## bpmn-process-designer
  12 +
  13 +github 地址:https://github.com/miyuesc/bpmn-process-designer
  14 +
  15 +* bpmnProcessDesigner
  16 +
  17 +TODO 目前存在的问题,如果选择 activiti 类型时,因为不支持内置表单的设计,所以会报 Error: unknown type activiti:FormData 错误。具体可见 https://github.com/miyuesc/bpmn-process-designer/issues/16 。
  18 +
  19 +另外几个流程设计器的选型:
  20 +
  21 +* https://gitee.com/jimlow/vue-bpmn 相比差一些,已经停止维护,不算推荐。
  22 +* https://github.com/GoldSubmarine/workflow-bpmn-modeler 仅支持 flowable 流程引擎。如果只考虑 flowable 的话,也是非常不错的选择。
10 \ No newline at end of file 23 \ No newline at end of file
yudao-admin-ui/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <slot name="control-header"></slot> 4 <slot name="control-header"></slot>
5 <template v-if="!$slots['control-header']"> 5 <template v-if="!$slots['control-header']">
6 <el-button-group key="file-control"> 6 <el-button-group key="file-control">
7 - <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-folder-opened" @click="$refs.refFile.click()">打开文件</el-button> 7 + <el-button :size="headerButtonSize" icon="el-icon-folder-opened" @click="$refs.refFile.click()">打开文件</el-button>
8 <el-tooltip effect="light"> 8 <el-tooltip effect="light">
9 <div slot="content"> 9 <div slot="content">
10 <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsXml()">下载为XML文件</el-button> 10 <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsXml()">下载为XML文件</el-button>
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 <br /> 13 <br />
14 <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button> 14 <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button>
15 </div> 15 </div>
16 - <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-download">下载文件</el-button> 16 + <el-button :size="headerButtonSize" icon="el-icon-download">下载文件</el-button>
17 </el-tooltip> 17 </el-tooltip>
18 <el-tooltip effect="light"> 18 <el-tooltip effect="light">
19 <div slot="content"> 19 <div slot="content">
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 <br /> 21 <br />
22 <el-button :size="headerButtonSize" type="text" @click="previewProcessJson">预览JSON</el-button> 22 <el-button :size="headerButtonSize" type="text" @click="previewProcessJson">预览JSON</el-button>
23 </div> 23 </div>
24 - <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-view">预览</el-button> 24 + <el-button :size="headerButtonSize" icon="el-icon-view">预览</el-button>
25 </el-tooltip> 25 </el-tooltip>
26 <el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '退出模拟' : '开启模拟'"> 26 <el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '退出模拟' : '开启模拟'">
27 - <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-cpu" @click="processSimulation"> 27 + <el-button :size="headerButtonSize" icon="el-icon-cpu" @click="processSimulation">
28 模拟 28 模拟
29 </el-button> 29 </el-button>
30 </el-tooltip> 30 </el-tooltip>
@@ -72,6 +72,7 @@ @@ -72,6 +72,7 @@
72 <el-button :size="headerButtonSize" icon="el-icon-refresh" @click="processRestart" /> 72 <el-button :size="headerButtonSize" icon="el-icon-refresh" @click="processRestart" />
73 </el-tooltip> 73 </el-tooltip>
74 </el-button-group> 74 </el-button-group>
  75 + <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-plus" @click="processSave">保存模型</el-button>
75 </template> 76 </template>
76 <!-- 用于打开本地文件--> 77 <!-- 用于打开本地文件-->
77 <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" /> 78 <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" />
@@ -111,8 +112,9 @@ export default { @@ -111,8 +112,9 @@ export default {
111 componentName: "MyProcessDesigner", 112 componentName: "MyProcessDesigner",
112 props: { 113 props: {
113 value: String, // xml 字符串 114 value: String, // xml 字符串
114 - processId: String,  
115 - processName: String, 115 + processId: String, // 流程 key 标识
  116 + processName: String, // 流程 name 名字
  117 + formId: Number, // 流程 form 表单编号
116 translations: Object, // 自定义的翻译文件 118 translations: Object, // 自定义的翻译文件
117 additionalModel: [Object, Array], // 自定义model 119 additionalModel: [Object, Array], // 自定义model
118 moddleExtension: Object, // 自定义moddle 120 moddleExtension: Object, // 自定义moddle
@@ -244,6 +246,11 @@ export default { @@ -244,6 +246,11 @@ export default {
244 this.bpmnModeler = null; 246 this.bpmnModeler = null;
245 }); 247 });
246 }, 248 },
  249 + watch: {
  250 + value: function (newValue) { // 在 xmlString 发生变化时,重新创建,从而绘制流程图
  251 + this.createNewDiagram(newValue);
  252 + }
  253 + },
247 methods: { 254 methods: {
248 initBpmnModeler() { 255 initBpmnModeler() {
249 if (this.bpmnModeler) return; 256 if (this.bpmnModeler) return;
@@ -445,6 +452,17 @@ export default { @@ -445,6 +452,17 @@ export default {
445 this.previewType = "json"; 452 this.previewType = "json";
446 this.previewModelVisible = true; 453 this.previewModelVisible = true;
447 }); 454 });
  455 + },
  456 + /* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
  457 + async processSave() {
  458 + const { err, xml } = await this.bpmnModeler.saveXML();
  459 + // 读取异常时抛出异常
  460 + if (err) {
  461 + this.msgError('保存模型失败,请重试!')
  462 + return
  463 + }
  464 + // 触发 save 事件
  465 + this.$emit('save', xml)
448 } 466 }
449 } 467 }
450 }; 468 };
yudao-admin-ui/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
@@ -3,7 +3,8 @@ @@ -3,7 +3,8 @@
3 <el-collapse v-model="activeTab"> 3 <el-collapse v-model="activeTab">
4 <el-collapse-item name="base"> 4 <el-collapse-item name="base">
5 <div slot="title" class="panel-tab__title"><i class="el-icon-info"></i>常规</div> 5 <div slot="title" class="panel-tab__title"><i class="el-icon-info"></i>常规</div>
6 - <element-base-info :id-edit-disabled="idEditDisabled" :business-object="elementBusinessObject" :type="elementType" /> 6 + <element-base-info :id-edit-disabled="idEditDisabled" :business-object="elementBusinessObject" :type="elementType"
  7 + :model="model" />
7 </el-collapse-item> 8 </el-collapse-item>
8 <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message"> 9 <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message">
9 <div slot="title" class="panel-tab__title"><i class="el-icon-s-comment"></i>消息与信号</div> 10 <div slot="title" class="panel-tab__title"><i class="el-icon-s-comment"></i>消息与信号</div>
@@ -89,7 +90,8 @@ export default { @@ -89,7 +90,8 @@ export default {
89 idEditDisabled: { 90 idEditDisabled: {
90 type: Boolean, 91 type: Boolean,
91 default: false 92 default: false
92 - } 93 + },
  94 + model: Object, // 流程模型的数据
93 }, 95 },
94 provide() { 96 provide() {
95 return { 97 return {
@@ -159,6 +161,7 @@ export default { @@ -159,6 +161,7 @@ export default {
159 // 初始化数据 161 // 初始化数据
160 initFormOnChanged(element) { 162 initFormOnChanged(element) {
161 let activatedElement = element; 163 let activatedElement = element;
  164 + // debugger
162 if (!activatedElement) { 165 if (!activatedElement) {
163 activatedElement = 166 activatedElement =
164 window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ?? 167 window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ??
yudao-admin-ui/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue
1 <template> 1 <template>
2 <div class="panel-tab__content"> 2 <div class="panel-tab__content">
3 - <el-form size="mini" label-width="90px" @submit.native.prevent>  
4 - <el-form-item label="ID">  
5 - <el-input  
6 - v-model="elementBaseInfo.id"  
7 - :disabled="idEditDisabled || elementBaseInfo.$type === 'bpmn:Process'"  
8 - clearable  
9 - @change="updateBaseInfo('id')"  
10 - /> 3 + <el-form size="mini" label-width="90px" :model="model" :rules="rules" @submit.native.prevent>
  4 + <el-form-item label="流程标识" prop="key">
  5 + <el-input v-model="model.key" placeholder="请输入流标标识"
  6 + :disabled="model.id !== undefined && model.id.length > 0"/>
11 </el-form-item> 7 </el-form-item>
12 - <el-form-item label="名称">  
13 - <el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('name')" /> 8 + <el-form-item label="流程名称" prop="name">
  9 + <el-input v-model="model.name" placeholder="请输入流程名称" clearable />
  10 + </el-form-item>
  11 + <el-form-item label="流程分类" prop="category">
  12 + <el-select v-model="model.category" placeholder="请选择流程分类" clearable style="width: 100%">
  13 + <el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
  14 + </el-select>
  15 + </el-form-item>
  16 + <el-form-item label="流程表单" prop="formId">
  17 + <el-select v-model="model.formId" placeholder="请选择流程表单,非必选哟!" clearable style="width: 100%">
  18 + <el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="流程描述" prop="description">
  22 + <el-input type="textarea" v-model="model.description" clearable />
14 </el-form-item> 23 </el-form-item>
15 - <!--流程的基础属性-->  
16 - <template v-if="elementBaseInfo.$type === 'bpmn:Process'">  
17 - <el-form-item label="版本标签">  
18 - <el-input v-model="elementBaseInfo.versionTag" clearable @change="updateBaseInfo('versionTag')" />  
19 - </el-form-item>  
20 - <el-form-item label="可执行">  
21 - <el-switch v-model="elementBaseInfo.isExecutable" active-text="是" inactive-text="否" @change="updateBaseInfo('isExecutable')" />  
22 - </el-form-item>  
23 - </template>  
24 </el-form> 24 </el-form>
25 </div> 25 </div>
26 </template> 26 </template>
27 <script> 27 <script>
  28 +import {DICT_TYPE, getDictDatas} from "@/utils/dict";
  29 +import {getSimpleForms} from "@/api/bpm/form";
  30 +import {getModel} from "@/api/bpm/model";
  31 +
28 export default { 32 export default {
29 name: "ElementBaseInfo", 33 name: "ElementBaseInfo",
30 props: { 34 props: {
31 businessObject: Object, 35 businessObject: Object,
32 - type: String,  
33 - idEditDisabled: {  
34 - type: Boolean,  
35 - default: true  
36 - } 36 + model: Object, // 流程模型的数据
37 }, 37 },
38 data() { 38 data() {
39 return { 39 return {
40 - elementBaseInfo: {} 40 + elementBaseInfo: {},
  41 + // 流程表单的下拉框的数据
  42 + forms: [],
  43 + // 流程模型的校验
  44 + rules: {
  45 + key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
  46 + name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
  47 + category: [{ required: true, message: "流程分类不能为空", trigger: "blur" }],
  48 + },
  49 + // 数据字典
  50 + categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
41 }; 51 };
42 }, 52 },
43 watch: { 53 watch: {
@@ -50,12 +60,19 @@ export default { @@ -50,12 +60,19 @@ export default {
50 } 60 }
51 } 61 }
52 }, 62 },
  63 + created() {
  64 + // 获得流程表单的下拉框的数据
  65 + getSimpleForms().then(response => {
  66 + this.forms = response.data
  67 + })
  68 + },
53 methods: { 69 methods: {
54 resetBaseInfo() { 70 resetBaseInfo() {
55 this.bpmnElement = window?.bpmnInstances?.bpmnElement; 71 this.bpmnElement = window?.bpmnInstances?.bpmnElement;
56 this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject)); 72 this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject));
57 }, 73 },
58 updateBaseInfo(key) { 74 updateBaseInfo(key) {
  75 + // 触发 elementBaseInfo 对应的字段
59 const attrObj = Object.create(null); 76 const attrObj = Object.create(null);
60 attrObj[key] = this.elementBaseInfo[key]; 77 attrObj[key] = this.elementBaseInfo[key];
61 if (key === "id") { 78 if (key === "id") {
yudao-admin-ui/src/main.js
@@ -76,11 +76,10 @@ Vue.component(&#39;RightToolbar&#39;, RightToolbar) @@ -76,11 +76,10 @@ Vue.component(&#39;RightToolbar&#39;, RightToolbar)
76 Vue.use(permission) 76 Vue.use(permission)
77 // Vue.use(hljs.vuePlugin); 77 // Vue.use(hljs.vuePlugin);
78 78
79 -// TODO 芋艿:bpmnProcessDesigner 引入 79 +// bpmnProcessDesigner 需要引入
80 import MyPD from "@/components/bpmnProcessDesigner/package/index.js"; 80 import MyPD from "@/components/bpmnProcessDesigner/package/index.js";
81 Vue.use(MyPD); 81 Vue.use(MyPD);
82 import "@/components/bpmnProcessDesigner/package/theme/index.scss"; 82 import "@/components/bpmnProcessDesigner/package/theme/index.scss";
83 -// TODO 芋艿:bpmnProcessDesigner 引入  
84 import "bpmn-js/dist/assets/diagram-js.css"; 83 import "bpmn-js/dist/assets/diagram-js.css";
85 import "bpmn-js/dist/assets/bpmn-font/css/bpmn.css"; 84 import "bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
86 import "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css"; 85 import "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
yudao-admin-ui/src/views/bpm/model/index.vue
@@ -67,12 +67,6 @@ @@ -67,12 +67,6 @@
67 <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 67 <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
68 @pagination="getList"/> 68 @pagination="getList"/>
69 69
70 - <!-- 流程编辑器 -->  
71 -<!-- <el-dialog class="bpmnclass dialogClass" :visible.sync="showBpmnOpen" :before-cancel="cancel" :fullscreen="true">-->  
72 -<!-- <vue-bpmn v-if="showBpmnOpen" product="activiti" @processSave="processSave"-->  
73 -<!-- :bpmnXml="bpmnXML" :bpmnData="bpmnData" @beforeClose="cancel" />-->  
74 -<!-- </el-dialog>-->  
75 -  
76 <!-- 流程表单配置详情 --> 70 <!-- 流程表单配置详情 -->
77 <el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body> 71 <el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
78 <div class="test-form"> 72 <div class="test-form">
@@ -83,8 +77,7 @@ @@ -83,8 +77,7 @@
83 </template> 77 </template>
84 78
85 <script> 79 <script>
86 -import {deleteModel, deployModel, createModel, updateModel, getModelPage, getModel} from "@/api/bpm/model";  
87 -// import VueBpmn from "@/components/bpmn/VueBpmn"; 80 +import {deleteModel, deployModel, getModelPage} from "@/api/bpm/model";
88 import {DICT_TYPE, getDictDatas} from "@/utils/dict"; 81 import {DICT_TYPE, getDictDatas} from "@/utils/dict";
89 import {getForm} from "@/api/bpm/form"; 82 import {getForm} from "@/api/bpm/form";
90 import {decodeFields} from "@/utils/formGenerator"; 83 import {decodeFields} from "@/utils/formGenerator";
@@ -93,8 +86,7 @@ import Parser from &#39;@/components/parser/Parser&#39; @@ -93,8 +86,7 @@ import Parser from &#39;@/components/parser/Parser&#39;
93 export default { 86 export default {
94 name: "model", 87 name: "model",
95 components: { 88 components: {
96 - Parser,  
97 - // VueBpmn 89 + Parser
98 }, 90 },
99 data() { 91 data() {
100 return { 92 return {
@@ -157,55 +149,22 @@ export default { @@ -157,55 +149,22 @@ export default {
157 this.resetForm("queryForm"); 149 this.resetForm("queryForm");
158 this.handleQuery(); 150 this.handleQuery();
159 }, 151 },
160 - processSave(data) {  
161 - // TODO 芋艿:临时写死的参数  
162 - data.category = '1'  
163 - data.formId = 11  
164 -  
165 - // 修改的提交  
166 - if (data.id) {  
167 - updateModel(data).then(response => {  
168 - this.msgSuccess("修改成功");  
169 - // 关闭弹窗,刷新列表  
170 - this.showBpmnOpen = false  
171 - this.getList();  
172 - })  
173 - return  
174 - }  
175 - // 添加的提交  
176 - createModel(data).then(response => {  
177 - this.bpmnData.id = response.data  
178 - this.msgSuccess("保存成功");  
179 - // 关闭弹窗,刷新列表  
180 - this.showBpmnOpen = false  
181 - this.getList();  
182 - })  
183 - }, 152 + /** 新增按钮操作 */
184 handleAdd() { 153 handleAdd() {
185 - // 重置 Model 信息  
186 - this.reset()  
187 - // 打开弹窗  
188 - this.showBpmnOpen = true  
189 - },  
190 - cancel() {  
191 - // 打开弹窗  
192 - this.showBpmnOpen = false  
193 - // 重置 Model 信息  
194 - this.reset()  
195 - // 刷新列表  
196 - this.getList() 154 + this.$router.push({
  155 + path:"/bpm/manager/model/edit"
  156 + });
197 }, 157 },
  158 + /** 修改按钮操作 */
198 handleUpdate(row) { 159 handleUpdate(row) {
199 - // 重置 Model 信息  
200 - this.reset()  
201 - // 获得 Model 信息  
202 - getModel(row.id).then(response => {  
203 - this.bpmnXML = response.data.bpmnXml  
204 - this.bpmnData = response.data  
205 - // 打开弹窗  
206 - this.showBpmnOpen = true  
207 - }) 160 + this.$router.push({
  161 + path:"/bpm/manager/model/edit",
  162 + query:{
  163 + modelId: row.id
  164 + }
  165 + });
208 }, 166 },
  167 + /** 删除按钮操作 */
209 handleDelete(row) { 168 handleDelete(row) {
210 const that = this; 169 const that = this;
211 this.$confirm('是否删除该流程!!', "警告", { 170 this.$confirm('是否删除该流程!!', "警告", {
@@ -219,6 +178,7 @@ export default { @@ -219,6 +178,7 @@ export default {
219 }) 178 })
220 }) 179 })
221 }, 180 },
  181 + /** 部署按钮操作 */
222 handleDeploy(row) { 182 handleDeploy(row) {
223 const that = this; 183 const that = this;
224 this.$confirm('是否部署该流程!!', "提示", { 184 this.$confirm('是否部署该流程!!', "提示", {
@@ -248,19 +208,3 @@ export default { @@ -248,19 +208,3 @@ export default {
248 } 208 }
249 }; 209 };
250 </script> 210 </script>
251 -<style>  
252 -.el-dialog > .el-dialog__body{  
253 - margin: 0;  
254 - border: 0;  
255 -}  
256 -.bpmn-viewer-header{  
257 - background: white;  
258 -}  
259 -.v-modal{  
260 - z-index: 2000!important;  
261 -}  
262 -.dialogClass{  
263 - padding: 0 ;  
264 -}  
265 -</style>  
266 -  
yudao-admin-ui/src/views/bpm/model/modelEditor.vue
1 <template> 1 <template>
2 <div class="app-container"> 2 <div class="app-container">
3 3
4 - <!-- 流程编辑器 --> 4 + <!-- 流程设计器,负责绘制流程等 -->
5 <my-process-designer :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm" 5 <my-process-designer :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm"
6 - keyboard ref="processDesigner" @init-finished="initModeler"/>  
7 - <!-- 右边工具栏 -->  
8 - <my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel" /> 6 + keyboard ref="processDesigner" @init-finished="initModeler"
  7 + @save="save"/>
  8 +
  9 + <!-- 流程属性器,负责编辑每个流程节点的属性 -->
  10 + <my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel"
  11 + :model="model" />
9 12
10 </div> 13 </div>
11 </template> 14 </template>
@@ -18,6 +21,7 @@ import CustomContentPadProvider from &quot;@/components/bpmnProcessDesigner/package/d @@ -18,6 +21,7 @@ import CustomContentPadProvider from &quot;@/components/bpmnProcessDesigner/package/d
18 import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette"; 21 import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette";
19 // import xmlObj2json from "./utils/xml2json"; 22 // import xmlObj2json from "./utils/xml2json";
20 import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette"; 23 import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette";
  24 +import {createModel, getModel, updateModel} from "@/api/bpm/model";
21 // 自定义侧边栏 25 // 自定义侧边栏
22 // import MyProcessPanel from "../package/process-panel/ProcessPanel"; 26 // import MyProcessPanel from "../package/process-panel/ProcessPanel";
23 27
@@ -32,23 +36,34 @@ export default { @@ -32,23 +36,34 @@ export default {
32 controlDrawerVisible: false, 36 controlDrawerVisible: false,
33 translationsSelf: translations, 37 translationsSelf: translations,
34 controlForm: { 38 controlForm: {
35 - processId: "",  
36 - processName: "",  
37 simulation: true, 39 simulation: true,
38 labelEditing: false, 40 labelEditing: false,
39 labelVisible: false, 41 labelVisible: false,
40 prefix: "activiti", 42 prefix: "activiti",
41 headerButtonSize: "mini", 43 headerButtonSize: "mini",
42 - // additionalModel: []  
43 additionalModel: [CustomContentPadProvider, CustomPaletteProvider] 44 additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
44 }, 45 },
45 addis: { 46 addis: {
46 CustomContentPadProvider, 47 CustomContentPadProvider,
47 CustomPaletteProvider 48 CustomPaletteProvider
48 - } 49 + },
  50 + // 流程模型的信息
  51 + model: {},
49 }; 52 };
50 }, 53 },
51 - created() {}, 54 + created() {
  55 + // 如果 modelId 非空,说明是修改流程模型
  56 + const modelId = this.$route.query && this.$route.query.modelId
  57 + if (modelId) {
  58 + getModel(modelId).then(response => {
  59 + this.xmlString = response.data.bpmnXml
  60 + this.model = {
  61 + ...response.data,
  62 + bpmnXml: undefined, // 清空 bpmnXml 属性
  63 + }
  64 + })
  65 + }
  66 + },
52 methods: { 67 methods: {
53 initModeler(modeler) { 68 initModeler(modeler) {
54 setTimeout(() => { 69 setTimeout(() => {
@@ -70,7 +85,34 @@ export default { @@ -70,7 +85,34 @@ export default {
70 // this.xmlString = undefined; 85 // this.xmlString = undefined;
71 // this.$refs.processDesigner.processRestart(); 86 // this.$refs.processDesigner.processRestart();
72 // } 87 // }
73 - } 88 + },
  89 + save(bpmnXml) {
  90 + const data = {
  91 + ...this.model,
  92 + bpmnXml: bpmnXml, // this.bpmnXml 只是初始化流程图,后续修改无法通过它获得
  93 + }
  94 +
  95 + // 修改的提交
  96 + if (data.id) {
  97 + updateModel(data).then(response => {
  98 + this.msgSuccess("修改成功")
  99 + // 跳转回去
  100 + this.close()
  101 + })
  102 + return
  103 + }
  104 + // 添加的提交
  105 + createModel(data).then(response => {
  106 + this.msgSuccess("保存成功")
  107 + // 跳转回去
  108 + this.close()
  109 + })
  110 + },
  111 + /** 关闭按钮 */
  112 + close() {
  113 + this.$store.dispatch("tagsView/delView", this.$route);
  114 + this.$router.push({ path: "/bpm/manager/model", query: { t: Date.now()}})
  115 + },
74 } 116 }
75 }; 117 };
76 </script> 118 </script>
yudao-admin-ui/vue.config.js
@@ -35,6 +35,7 @@ module.exports = { @@ -35,6 +35,7 @@ module.exports = {
35 // detail: https://cli.vuejs.org/config/#devserver-proxy 35 // detail: https://cli.vuejs.org/config/#devserver-proxy
36 [process.env.VUE_APP_BASE_API]: { 36 [process.env.VUE_APP_BASE_API]: {
37 target: `http://localhost:48080`, 37 target: `http://localhost:48080`,
  38 + // target: `http://api-dashboard.yudao.iocoder.cn`,
38 changeOrigin: true, 39 changeOrigin: true,
39 pathRewrite: { 40 pathRewrite: {
40 ['^' + process.env.VUE_APP_BASE_API]: '' 41 ['^' + process.env.VUE_APP_BASE_API]: ''
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; @@ -12,7 +12,9 @@ import java.util.regex.Pattern;
12 */ 12 */
13 public class ValidationUtils { 13 public class ValidationUtils {
14 14
15 - private static Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); 15 + private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
  16 +
  17 + private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
16 18
17 public static boolean isMobile(String mobile) { 19 public static boolean isMobile(String mobile) {
18 if (StrUtil.length(mobile) != 11) { 20 if (StrUtil.length(mobile) != 11) {
@@ -27,4 +29,9 @@ public class ValidationUtils { @@ -27,4 +29,9 @@ public class ValidationUtils {
27 && PATTERN_URL.matcher(url).matches(); 29 && PATTERN_URL.matcher(url).matches();
28 } 30 }
29 31
  32 + public static boolean isXmlNCName(String str) {
  33 + return StringUtils.hasText(str)
  34 + && PATTERN_XML_NCNAME.matcher(str).matches();
  35 + }
  36 +
30 } 37 }
更新日志.md
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 28
29 * 【优化】引入 form generator 0.2.0 版本,并重构相关代码 29 * 【优化】引入 form generator 0.2.0 版本,并重构相关代码
30 * 【新增】新增流程表单,支持动态进行表单的配置 30 * 【新增】新增流程表单,支持动态进行表单的配置
  31 +* 【新增】引入 bpmn-process-designer 0.0.1 版本,提供流程设计器的能力
31 32
32 ### 🐞 Bug Fixes 33 ### 🐞 Bug Fixes
33 34