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

pull/2/head
YunaiV 2022-01-03 01:12:36 +08:00
parent c4003396a5
commit 490f907ada
20 changed files with 242 additions and 135 deletions

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

@ -57,7 +57,20 @@ public interface ModelConvert {
return modelRespVO; 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) { default BpmDefinitionCreateReqDTO convert2(Model model) {
BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO(); BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO();

View File

@ -26,6 +26,8 @@ public interface BpmErrorCodeConstants {
// ========== OA 工作流模块 1-009-002-000 ========== // ========== OA 工作流模块 1-009-002-000 ==========
ErrorCode BPM_MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程"); ErrorCode BPM_MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程");
ErrorCode BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在"); 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_ERROR = new ErrorCode(1004001002, "工作流模型异常");
ErrorCode BPMN_MODEL_PROCESS_NOT_EXISTS = new ErrorCode(1004001009, "流程数据为空"); ErrorCode BPMN_MODEL_PROCESS_NOT_EXISTS = new ErrorCode(1004001009, "流程数据为空");

View File

@ -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.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.activiti.engine.repository.Model;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collection; import java.util.Collection;
@ -52,6 +53,13 @@ public interface BpmFormService {
*/ */
BpmFormDO getForm(Long id); BpmFormDO getForm(Long id);
/**
*
*
* @return
*/
List<BpmFormDO> getFormList();
/** /**
* *
* *

View File

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

View File

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

View File

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

View File

@ -5,4 +5,17 @@ github 地址https://github.com/JakHuang/form-generator
* generator * generator
* parser * parser
* render * 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 的话,也是非常不错的选择。

View File

@ -4,7 +4,7 @@
<slot name="control-header"></slot> <slot name="control-header"></slot>
<template v-if="!$slots['control-header']"> <template v-if="!$slots['control-header']">
<el-button-group key="file-control"> <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"> <el-tooltip effect="light">
<div slot="content"> <div slot="content">
<el-button :size="headerButtonSize" type="text" @click="downloadProcessAsXml()">XML</el-button> <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsXml()">XML</el-button>
@ -13,7 +13,7 @@
<br /> <br />
<el-button :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()">BPMN</el-button> <el-button :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()">BPMN</el-button>
</div> </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>
<el-tooltip effect="light"> <el-tooltip effect="light">
<div slot="content"> <div slot="content">
@ -21,10 +21,10 @@
<br /> <br />
<el-button :size="headerButtonSize" type="text" @click="previewProcessJson">JSON</el-button> <el-button :size="headerButtonSize" type="text" @click="previewProcessJson">JSON</el-button>
</div> </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>
<el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '退出模拟' : '开启模拟'"> <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-button>
</el-tooltip> </el-tooltip>
@ -72,6 +72,7 @@
<el-button :size="headerButtonSize" icon="el-icon-refresh" @click="processRestart" /> <el-button :size="headerButtonSize" icon="el-icon-refresh" @click="processRestart" />
</el-tooltip> </el-tooltip>
</el-button-group> </el-button-group>
<el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-plus" @click="processSave"></el-button>
</template> </template>
<!-- 用于打开本地文件--> <!-- 用于打开本地文件-->
<input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" /> <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" />
@ -111,8 +112,9 @@ export default {
componentName: "MyProcessDesigner", componentName: "MyProcessDesigner",
props: { props: {
value: String, // xml value: String, // xml
processId: String, processId: String, // key
processName: String, processName: String, // name
formId: Number, // form
translations: Object, // translations: Object, //
additionalModel: [Object, Array], // model additionalModel: [Object, Array], // model
moddleExtension: Object, // moddle moddleExtension: Object, // moddle
@ -244,6 +246,11 @@ export default {
this.bpmnModeler = null; this.bpmnModeler = null;
}); });
}, },
watch: {
value: function (newValue) { // xmlString
this.createNewDiagram(newValue);
}
},
methods: { methods: {
initBpmnModeler() { initBpmnModeler() {
if (this.bpmnModeler) return; if (this.bpmnModeler) return;
@ -445,6 +452,17 @@ export default {
this.previewType = "json"; this.previewType = "json";
this.previewModelVisible = true; this.previewModelVisible = true;
}); });
},
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
async processSave() {
const { err, xml } = await this.bpmnModeler.saveXML();
//
if (err) {
this.msgError('保存模型失败,请重试!')
return
}
// save
this.$emit('save', xml)
} }
} }
}; };

View File

@ -3,7 +3,8 @@
<el-collapse v-model="activeTab"> <el-collapse v-model="activeTab">
<el-collapse-item name="base"> <el-collapse-item name="base">
<div slot="title" class="panel-tab__title"><i class="el-icon-info"></i>常规</div> <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>
<el-collapse-item name="condition" v-if="elementType === 'Process'" key="message"> <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> <div slot="title" class="panel-tab__title"><i class="el-icon-s-comment"></i>消息与信号</div>
@ -89,7 +90,8 @@ export default {
idEditDisabled: { idEditDisabled: {
type: Boolean, type: Boolean,
default: false default: false
} },
model: Object, //
}, },
provide() { provide() {
return { return {
@ -159,6 +161,7 @@ export default {
// //
initFormOnChanged(element) { initFormOnChanged(element) {
let activatedElement = element; let activatedElement = element;
// debugger
if (!activatedElement) { if (!activatedElement) {
activatedElement = activatedElement =
window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ?? window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ??

View File

@ -1,43 +1,53 @@
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-form size="mini" label-width="90px" @submit.native.prevent> <el-form size="mini" label-width="90px" :model="model" :rules="rules" @submit.native.prevent>
<el-form-item label="ID"> <el-form-item label="流程标识" prop="key">
<el-input <el-input v-model="model.key" placeholder="请输入流标标识"
v-model="elementBaseInfo.id" :disabled="model.id !== undefined && model.id.length > 0"/>
:disabled="idEditDisabled || elementBaseInfo.$type === 'bpmn:Process'"
clearable
@change="updateBaseInfo('id')"
/>
</el-form-item> </el-form-item>
<el-form-item label="名称"> <el-form-item label="流程名称" prop="name">
<el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('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> </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> </el-form>
</div> </div>
</template> </template>
<script> <script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getSimpleForms} from "@/api/bpm/form";
import {getModel} from "@/api/bpm/model";
export default { export default {
name: "ElementBaseInfo", name: "ElementBaseInfo",
props: { props: {
businessObject: Object, businessObject: Object,
type: String, model: Object, //
idEditDisabled: {
type: Boolean,
default: true
}
}, },
data() { data() {
return { 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: { watch: {
@ -50,12 +60,19 @@ export default {
} }
} }
}, },
created() {
//
getSimpleForms().then(response => {
this.forms = response.data
})
},
methods: { methods: {
resetBaseInfo() { resetBaseInfo() {
this.bpmnElement = window?.bpmnInstances?.bpmnElement; this.bpmnElement = window?.bpmnInstances?.bpmnElement;
this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject)); this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject));
}, },
updateBaseInfo(key) { updateBaseInfo(key) {
// elementBaseInfo
const attrObj = Object.create(null); const attrObj = Object.create(null);
attrObj[key] = this.elementBaseInfo[key]; attrObj[key] = this.elementBaseInfo[key];
if (key === "id") { if (key === "id") {

View File

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

View File

@ -67,12 +67,6 @@
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/> @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> <el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<div class="test-form"> <div class="test-form">
@ -83,8 +77,7 @@
</template> </template>
<script> <script>
import {deleteModel, deployModel, createModel, updateModel, getModelPage, getModel} from "@/api/bpm/model"; import {deleteModel, deployModel, getModelPage} from "@/api/bpm/model";
// import VueBpmn from "@/components/bpmn/VueBpmn";
import {DICT_TYPE, getDictDatas} from "@/utils/dict"; import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm} from "@/api/bpm/form"; import {getForm} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator"; import {decodeFields} from "@/utils/formGenerator";
@ -93,8 +86,7 @@ import Parser from '@/components/parser/Parser'
export default { export default {
name: "model", name: "model",
components: { components: {
Parser, Parser
// VueBpmn
}, },
data() { data() {
return { return {
@ -157,55 +149,22 @@ export default {
this.resetForm("queryForm"); this.resetForm("queryForm");
this.handleQuery(); 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() { handleAdd() {
// Model this.$router.push({
this.reset() path:"/bpm/manager/model/edit"
// });
this.showBpmnOpen = true
},
cancel() {
//
this.showBpmnOpen = false
// Model
this.reset()
//
this.getList()
}, },
/** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
// Model this.$router.push({
this.reset() path:"/bpm/manager/model/edit",
// Model query:{
getModel(row.id).then(response => { modelId: row.id
this.bpmnXML = response.data.bpmnXml }
this.bpmnData = response.data });
//
this.showBpmnOpen = true
})
}, },
/** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
const that = this; const that = this;
this.$confirm('是否删除该流程!!', "警告", { this.$confirm('是否删除该流程!!', "警告", {
@ -219,6 +178,7 @@ export default {
}) })
}) })
}, },
/** 部署按钮操作 */
handleDeploy(row) { handleDeploy(row) {
const that = this; const that = this;
this.$confirm('是否部署该流程!!', "提示", { this.$confirm('是否部署该流程!!', "提示", {
@ -248,19 +208,3 @@ export default {
} }
}; };
</script> </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>

View File

@ -1,11 +1,14 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 流程编辑器 --> <!-- 流程设计器负责绘制流程等 -->
<my-process-designer :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm" <my-process-designer :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm"
keyboard ref="processDesigner" @init-finished="initModeler"/> keyboard ref="processDesigner" @init-finished="initModeler"
<!-- 右边工具栏 --> @save="save"/>
<my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel" />
<!-- 流程属性器负责编辑每个流程节点的属性 -->
<my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel"
:model="model" />
</div> </div>
</template> </template>
@ -18,6 +21,7 @@ import CustomContentPadProvider from "@/components/bpmnProcessDesigner/package/d
import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette"; import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette";
// import xmlObj2json from "./utils/xml2json"; // import xmlObj2json from "./utils/xml2json";
import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette"; import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette";
import {createModel, getModel, updateModel} from "@/api/bpm/model";
// //
// import MyProcessPanel from "../package/process-panel/ProcessPanel"; // import MyProcessPanel from "../package/process-panel/ProcessPanel";
@ -32,23 +36,34 @@ export default {
controlDrawerVisible: false, controlDrawerVisible: false,
translationsSelf: translations, translationsSelf: translations,
controlForm: { controlForm: {
processId: "",
processName: "",
simulation: true, simulation: true,
labelEditing: false, labelEditing: false,
labelVisible: false, labelVisible: false,
prefix: "activiti", prefix: "activiti",
headerButtonSize: "mini", headerButtonSize: "mini",
// additionalModel: []
additionalModel: [CustomContentPadProvider, CustomPaletteProvider] additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
}, },
addis: { addis: {
CustomContentPadProvider, CustomContentPadProvider,
CustomPaletteProvider 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: { methods: {
initModeler(modeler) { initModeler(modeler) {
setTimeout(() => { setTimeout(() => {
@ -70,7 +85,34 @@ export default {
// this.xmlString = undefined; // this.xmlString = undefined;
// this.$refs.processDesigner.processRestart(); // 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> </script>

View File

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

View File

@ -12,7 +12,9 @@ import java.util.regex.Pattern;
*/ */
public class ValidationUtils { 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) { public static boolean isMobile(String mobile) {
if (StrUtil.length(mobile) != 11) { if (StrUtil.length(mobile) != 11) {
@ -27,4 +29,9 @@ public class ValidationUtils {
&& PATTERN_URL.matcher(url).matches(); && PATTERN_URL.matcher(url).matches();
} }
public static boolean isXmlNCName(String str) {
return StringUtils.hasText(str)
&& PATTERN_XML_NCNAME.matcher(str).matches();
}
} }

View File

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