工作流动态表单vue(bpmn camunda)+springboot(activiti7)
整理一下这么多天集成activiti7的一些过程主要参考两位大佬的文章1.后台,基于spring boot2.前端,vue,bpmnjs
目录
1.后台参考,spring boot,activiti7(虽然是7,因为用的shiro,没有使用spring security,新特性都没用,用过activiti5,6的话都不用多看,常用的区别不大)
2.前端参考,vue,bpmnjs(用的camunda解析器,和activiti有区别,问题主要集中在这)
主要参考两位大佬的文章,代码直接拿着跑,然后集成(Ctrl+c+v)进了公司项目
如果需要表单,业务也明确,建议用外部表单,动态表单功能比较受限.
1.后台参考,spring boot,activiti7(虽然是7,因为用的shiro,没有使用spring security,新特性都没用,用过activiti5,6的话都不用多看,常用的区别不大)
2.前端参考,vue,bpmnjs(用的camunda解析器,和activiti有区别,问题主要集中在这)
如果是刚接触工作流,可以看看这两篇,很详细,都是直接上代码
camunda建议看官方文档,直接网页翻译对着看,说真的没找到特别好的文章,如果有好的希望好心人给我也发一份共同学习
一个小问题,Activiti7的M4版本缺失字段Bug,这是Oracle
貌似好多版本都有问题,不知道会不会变成Activiti的特色
alter table ACT_RE_DEPLOYMENT add PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL;
alter table ACT_RE_DEPLOYMENT add VERSION_ varchar(255) DEFAULT NULL;
效果
查询任务时可以编辑表单信息,保存表单数据是单独自定义的表,历史任务id和运行任务id一样,所以查一张表就行
前端代码
前端用的bpmnjs,其实搭配camunda比较合适
直接以字符串形式部署,把camunda标签,属性换成对应activiti了
async addByString() {
try {
const result = await this.bpmnModeler.saveXML({ format: true });
const xml = result.xml;
//xml转json,用json处理后在转xml
const $x2js = new x2js();
const jsonObj = $x2js.xml2js(xml);
this.getFormProperty(jsonObj);
let newXml = $x2js.js2xml(jsonObj);
act1.addByString({
xmlBPMN: newXml,
deployName: 'addByString1'
});
} catch (err) {
console.log(err);
}
},
开始是直接字符串替换,最好不要只替换camunda,会把命名空间里面也替换
var newXml2 = xml.replace(/camunda:/ig, 'activiti:');
newXml2 = newXml2.replace(/FormField/ig, 'formProperty');
console.log(newXml2);
后面因为需要表单,用activiti没法解析,找到一个用json做转换的,参考bpmn camunda版转为activiti版
改了一下别人的方法,增加了activiti的属性(本来想在后台把这个方法重写一下,懒得动,先这样吧)
getFormProperty(json) {
for (let e in json) {
if (e == 'extensionElements' && json.extensionElements.formData && json.extensionElements.formData.formField) {
let formProperty = JSON.parse(JSON.stringify(json.extensionElements.formData.formField));
if (this.isArrayFn(formProperty)) {
formProperty.forEach(x => {
x.__prefix = 'activiti';
//借用camunda属性 添加activiti的 不然activiti获取不到
//对应activiti 表单 name
x._name = x._label;
//对应activiti 表单 defaultExpression
if (x._defaultValue) {
x._default = x._defaultValue;
}
});
} else {
formProperty.__prefix = 'activiti';
formProperty._name = formProperty._label;
if (formProperty._defaultValue) {
formProperty._default = formProperty._defaultValue;
}
}
json.extensionElements.formProperty = formProperty;
delete json.extensionElements.formData;
} else if (e.includes('camunda:')) {
let str = e.replace('camunda:', 'activiti:');
json[str] = json[e];
delete json[e];
} else if (typeof json[e] == 'object') {
this.getFormProperty(json[e]);
}
}
},
后台代码
开始的时候activiti后台解析总是过不了,直接用xml字符串上传部署,比较方便调试
后台方法,这个disableSchemaValidation()方法不会检查Schema,标签或者属性有的是camunda的也不会报错(比如上面表单的label...)
@PostMapping("/addByString")
public R addByString(
@RequestParam("xmlBPMN") String xmlBPMN,
@RequestParam("deployName") String deployName
) {
Deployment deployment = repositoryService.createDeployment()
// 这里由于是 xml 的字符串没有资源名称
.addString("xmlStr.bpmn", xmlBPMN)
.disableSchemaValidation() //禁用架构验证
.name(deployName)
.deploy();
return R.strData(deployment.getId());
}
表单设置好了,查询任务时,根据taskId获取用户任务,activiti7没有了FormService,只能这样
构建表单
@ApiOperation(value = "构建表单")
@GetMapping("/getForm")
public R getForm(String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (!Optional.ofNullable(task).isPresent()) {
throw new ReturnException("请更新任务");
}
//获取task对应的表单内容 需要TaskDefinitionKey
UserTask userTask = (UserTask) repositoryService.getBpmnModel(task.getProcessDefinitionId())
.getFlowElement(task.getTaskDefinitionKey());
if (!Optional.ofNullable(userTask).isPresent()) {
throw new ReturnException("非用户任务");
}
//外部表单
//String formKey = userTask.getFormKey();
List<FormProperty> formProperties = userTask.getFormProperties();
if (CollectionUtils.isEmpty(formProperties)) {
throw new ReturnException("无表单");
}
List<Map<String, Object>> collect = formProperties.stream().map(formProperty -> {
return CreateMap.build()
.setAttribute("id", formProperty.getId())
.setAttribute("type", formProperty.getType())
//在camunda叫做label 前端转json加了一个name属性 不然取不到值
.setAttribute("name", formProperty.getName())
//在camunda叫做defaultValue activiti表单的default 前端转json加了一个default属性 后台对应defaultExpression
.setAttribute("defaultValue", formProperty.getDefaultExpression())
//type = enum 枚举类型会用
// .setAttribute("formValues", formProperty.getFormValues())
//下面没有值 camunda和activiti表单有区别
// .setAttribute("variable", formProperty.getVariable())
// .setAttribute("expression", formProperty.getExpression())
// .setAttribute("datePattern", formProperty.getDatePattern())
.build();
}).collect(Collectors.toList());
return R.okList(collect);
}
取出表单填写的值
@ApiOperation(value = "构建表单,取出表单填写的值")
@GetMapping("/getFormData")
public R getFormData(String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (!Optional.ofNullable(task).isPresent()) {
throw new ReturnException("请更新任务");
}
//获取task对应的表单内容 需要TaskDefinitionKey
UserTask userTask = (UserTask) repositoryService.getBpmnModel(task.getProcessDefinitionId())
.getFlowElement(task.getTaskDefinitionKey());
if (!Optional.ofNullable(userTask).isPresent()) {
throw new ReturnException("非用户任务");
}
List<FormProperty> formProperties = userTask.getFormProperties();
if (CollectionUtils.isEmpty(formProperties)) {
throw new ReturnException("无表单");
}
//查询表单数据
List<ActivitiFormdata> formdataList = activitiFormdataDao.findAllByTaskId(taskId);
if (CollectionUtils.isEmpty(formdataList)) {
//还没有保存表单
formProperties.forEach(formProperty -> {
ActivitiFormdata one = new ActivitiFormdata();
one.setFormPropertyId(formProperty.getId());
one.setType(formProperty.getType());
if ("enum".equals(formProperty.getType())) {
Map<String, String> map = formProperty.getFormValues().stream()
.collect(Collectors.toMap(FormValue::getId, FormValue::getName));
one.setFormValues(map);
}
one.setName(formProperty.getName());
one.setDefaultValue(formProperty.getDefaultExpression());
one.setTaskId(taskId);
formdataList.add(one);
});
} else {
formProperties.forEach(formProperty -> {
formdataList.stream()
.filter(one -> formProperty.getId().equals(one.getFormPropertyId()))
.forEach(one -> {
one.setFormPropertyId(formProperty.getId());
one.setType(formProperty.getType());
if ("enum".equals(formProperty.getType())) {
Map<String, String> map = formProperty.getFormValues().stream()
.collect(Collectors.toMap(FormValue::getId, FormValue::getName));
one.setFormValues(map);
}
one.setName(formProperty.getName());
one.setDefaultValue(formProperty.getDefaultExpression());
});
});
}
return R.okList(formdataList);
}
填写表单,根据返回的表单类型判断渲染就行,自定义类型还没搞,不知道怎么用,貌似activiti没有自定义的这种
吐槽:
公司的开发4组之前买了一个工作流,集成到项目被客户否了,正好我们经理想给报表模块做个审核流程,然后任务分给了准备离职的我(天天写bug没意思,想去搞点别的),本来没想搞到公司项目(直接拿人家项目跑一下就行,要用的时候再说),经理要集成到他的报表项目,没法子只能先集成到我项目里了,也算是从头到尾搞了一遍,差不多两周时间,中间也弄了些其他的.
之所以后台用activiti,一个是之前接触过,二是没想到前端部分也叫我弄,前端没写过多少,不熟,还好别人都写好了,直接粘贴,看着改,对比camunda和activiti的xml花了比较久,如果是新项目,前端用bpmn的话,后台用camunda会方便一点吧,camunda是从activiti分出来的,很多地方都一样
第一次写博客,从毕业论文之后就没写过这种东西了
最后,言多必失,好好工作,写bug也得认真,吐槽的话就留给自己或者好友吧
轻喷
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
更多推荐
所有评论(0)