一、流程审批介绍
对审批表单进行改造,核心操作功能如下:
1、根据当前节点信息获取当前的formkey,通过key查询对应的自定义表单进行展示。
2、根据当前节点信息获取当前节点的隐藏和展示字段设置信息,根据设置信息设置表单。
二、流程审核前端功能改造
审核FormCheck改造
1、新增VDataCheck审核表单组件
<VDataCheck
ref="checkRef"
:record="record"
v-if="record.businessKey.indexOf('customize') != -1"
@finishResponse="finishResponse"
></VDataCheck>
2、点击审核方法,调用VDataCheck组件中的onOk方法。
同理:表单驳回和任务委派同样增加对表单的编辑及改造。
新增Check审核表单组件
<template>
<div>
<!-- <a-skeleton active v-show="!initStatus" :paragraph="{ rows: 10 }" /> -->
<!-- <a-spin size="large" v-show="!initStatus" /> -->
<k-form-build
ref="kfb"
:value="jsonData"
:dynamicData="dynamicData"
v-show="visable"
/>
</div>
</template>
<script>
import {
findVFormInfo,
saveOrUpdate,
findVDataInfo,
findVFormData,
rejectAnyNod,
delegateTask,
} from "@/api/customize/vdata";
import { findDictionaryList } from "@/api/system/dictionary";
import { findFormParams } from "@/api/activiti/processTask";
export default {
// 声明当前子组件接收父组件传递的属性
props: {
record: {
type: Object,
default: null,
},
},
data() {
return {
jsonData: {},
form_data: {},
dynamicData: {},
form: {},
initStatus: false,
visable: false,
menu_id: "",
table_id: "",
hideFieldKey: "",
};
},
mounted() {
//查询菜单配置信息
let data = this.record.businessKey.split(":");
this.initData(data[2]);
this.menu_id = data[2];
},
updated() {
//查询编辑项内容
let data = this.record.businessKey.split(":");
this.initFormData(data[2], data[3]);
},
methods: {
initData(menu_id) {
findVFormInfo({ menu_id: menu_id, formKey: this.record.formKey }).then(
(response) => {
this.form_data = response.data.data;
this.table_id = this.form_data.id;
this.dynamicData = response.data.object;
this.jsonData = JSON.parse(this.form_data.table_content);
}
);
},
initFormData(menu_id, id) {
findVDataInfo({
table_id: this.record.formKey,
menu_id: menu_id,
id: id,
}).then((response) => {
this.form = response.data;
this.$refs.kfb.setData(response.data);
this.initStatus = true;
this.$refs.kfb.disableAll();
findFormParams({
nodeType: "nodeType",
taskId: this.record.id,
}).then((response) => {
this.visable = true;
let data = response.data.data;
if (data.writeFieldKey != undefined) {
this.$refs.kfb.enable(data.writeFieldKey.split(","));
}
if (data.hideFieldKey != undefined) {
this.$refs.kfb.hide(data.hideFieldKey.split(","));
this.hideFieldKey = data.hideFieldKey;
}
});
});
},
currentUserOrganize(value) {},
onOk(data) {
return new Promise((resolve) => {
// 使用getData函数获取数据
this.$refs.kfb
.getData()
.then((values) => {
for (let k in values) {
if (Array.isArray(values[k]) && values[k].length > 0) {
if (
Object.prototype.toString.call(values[k][0]) ==
"[object String]"
) {
values[k] = values[k].join(",");
} else if (
Object.prototype.toString.call(values[k][0]) ==
"[object Object]"
) {
for (let j in values[k]) {
if (
Array.isArray(values[k][j]) &&
values[k][j].length > 0
) {
values[k][j] = values[k][j].join(",");
}
}
}
}
}
saveOrUpdate({
taskId: data.id,
approve_opinion: data.approve_opinion,
nodeData: data.nodeData,
// businessKey: data.businessKey,
// assignee: data.assignee,
// assignee_name: data.assignee_name,
// originalAssignee: data.originalAssignee,
// executionId: data.executionId,
// formKey: data.formKey,
// procdef_name: data.procdef_name,
// processDefinitionId: data.processDefinitionId,
// processInstanceId: data.processInstanceId,
// start_user_name: data.start_user_name,
// suspensionState: data.suspensionState,
// taskDefinitionKey: data.taskDefinitionKey,
// tenantId: data.tenantId,
submitType: "3",
...values,
id: this.form.id,
table_id: this.table_id,
menu_id: this.menu_id,
hideFieldKey: this.hideFieldKey,
}).then((response) => {
this.$emit("finishResponse", response);
resolve(true);
});
})
.catch(() => {});
});
},
onRejectAnyNode(data) {
return new Promise((resolve) => {
// 使用getData函数获取数据
this.$refs.kfb
.getData()
.then((values) => {
for (let k in values) {
if (Array.isArray(values[k]) && values[k].length > 0) {
if (
Object.prototype.toString.call(values[k][0]) ==
"[object String]"
) {
values[k] = values[k].join(",");
} else if (
Object.prototype.toString.call(values[k][0]) ==
"[object Object]"
) {
for (let j in values[k]) {
if (
Array.isArray(values[k][j]) &&
values[k][j].length > 0
) {
values[k][j] = values[k][j].join(",");
}
}
}
}
}
rejectAnyNod({
taskId: data.id,
flowElementId: data.flowElementId,
...values,
id: this.form.id,
table_id: this.table_id,
menu_id: this.menu_id,
hideFieldKey: this.hideFieldKey,
}).then((response) => {
this.$emit("finishResponse", response);
resolve(true);
});
})
.catch(() => {});
});
},
onDelegateTask(data) {
return new Promise((resolve) => {
// 使用getData函数获取数据
this.$refs.kfb
.getData()
.then((values) => {
for (let k in values) {
if (Array.isArray(values[k]) && values[k].length > 0) {
if (
Object.prototype.toString.call(values[k][0]) ==
"[object String]"
) {
values[k] = values[k].join(",");
} else if (
Object.prototype.toString.call(values[k][0]) ==
"[object Object]"
) {
for (let j in values[k]) {
if (
Array.isArray(values[k][j]) &&
values[k][j].length > 0
) {
values[k][j] = values[k][j].join(",");
}
}
}
}
}
delegateTask({
taskId: data.taskId,
userId: data.userId,
userName: data.userName,
...values,
id: this.form.id,
table_id: this.table_id,
menu_id: this.menu_id,
hideFieldKey: this.hideFieldKey,
}).then((response) => {
this.$emit("finishResponse", response);
resolve(true);
});
})
.catch(() => {});
});
},
onCancel() {
return new Promise((resolve) => {
resolve(true);
});
},
},
};
</script>
businessKey内容分析:
business:customize:自定义模块menu_id:当前数据id
String businessKey = “business:customize:”+pd.get(“menu_id”)+”:”+id;
根据formKey查询当前自定义表单信息
form_data:查询当前自定义数据表的信息,含有k-form-design设计内容。
table_id:form_data自定义数据表的主键id。
dynamicData:动态字段-如下拉框、单选框、多选框绑定的数据字典信息,此处dynamicData需要在jsonData初始化之前渲染。
findVFormInfo({ menu_id: menu_id, formKey: this.record.formKey }).then(
(response) => {
this.form_data = response.data.data;
this.table_id = this.form_data.id;
this.dynamicData = response.data.object;
this.jsonData = JSON.parse(this.form_data.table_content);
}
);
对应后台方法如下:
/**
* @title findVFormInfo
* @description 查询自定义表单信息
* @author Administrator
* @updateTime 2021/10/3 0003 10:21
*/
@GetMapping("/findVFormInfo")
public void findVFormInfo(HttpServletRequest request, HttpServletResponse response) throws Exception {
PageData pd = new PageData(request);
Vform vform = null;
if(Verify.verifyIsNotNull(pd.get("formKey"))){
vform = vformService.getById(pd.get("formKey").toString());
}else{
Vmenu vmenu = vmenuService.getById(pd.get("menu_id").toString());
vform = vformService.getById(vmenu.getTable_id());
}
//查询主表信息和主表字段信息-组织动态参数
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("table_id",vform.getId());
List<Vfield> vfields = vfieldService.list(queryWrapper);
PageData dynamicData = new PageData();
for (Vfield vfield:vfields) {
if("select,checkbox,radio".indexOf(vfield.getField_type())!=-1&&vfield.getDynamic().equals("true")){
Dictionary dictionary = new Dictionary();
dictionary.setParent_code(vfield.getDynamicKey());
List<Dictionary> list = dictionaryService.findList(dictionary);
List<PageData> dynamicList = new ArrayList<PageData>();
list.forEach(item->{
PageData p = new PageData();
p.put("label",item.getName());
p.put("value",item.getId());
dynamicList.add(p);
});
dynamicData.put(vfield.getDynamicKey(),dynamicList);
}
}
vform.setVfields(vfields);
//查询组织关联子表的动态参数
QueryWrapper formWrapper = new QueryWrapper();
formWrapper.eq("main_id",vform.getId());
List<Vform> listChild = vformService.list(formWrapper);
for (Vform vformChild:listChild) {
QueryWrapper qWrapper = new QueryWrapper();
qWrapper.eq("table_id",vformChild.getId());
List<Vfield> vfieldsChild = vfieldService.list(qWrapper);
for (Vfield vfield:vfieldsChild) {
if("select,checkbox,radio".indexOf(vfield.getField_type())!=-1&&vfield.getDynamic().equals("true")){
Dictionary dictionary = new Dictionary();
dictionary.setParent_code(vfield.getDynamicKey());
List<Dictionary> list = dictionaryService.findList(dictionary);
List<PageData> dynamicList = new ArrayList<PageData>();
list.forEach(item->{
PageData p = new PageData();
p.put("label",item.getName());
p.put("value",item.getId());
dynamicList.add(p);
});
dynamicData.put(vfield.getDynamicKey(),dynamicList);
}
}
vform.setVfields(vfieldsChild);
}
Json json = new Json();
json.setData(vform);
json.setObject(dynamicData);
json.setSuccess(true);
this.writeJson(response,json);
}
查询表单数据和writeFieldKey/hideFieldKey
1、数据渲染:this.$refs.kfb.setData(response.data);
2、设置表单不可编辑:this.$refs.kfb.disableAll(); 自己写的disableAll,默认组件是没有的,组件设置表单不可编辑模式如下:
如果设置::disabled=”true”,就无法控制可编辑项了。
3、查询writeFieldKey和hideFieldKey
设置表单可编辑项:this.$refs.kfb.enable(data.writeFieldKey.split(“,”));
设置表单隐藏项:this.$refs.kfb.hide(data.hideFieldKey.split(“,”));
findVDataInfo({
table_id: this.record.formKey,
menu_id: menu_id,
id: id,
}).then((response) => {
this.form = response.data;
this.$refs.kfb.setData(response.data);
this.initStatus = true;
this.$refs.kfb.disableAll();
findFormParams({
nodeType: "nodeType",
taskId: this.record.id,
}).then((response) => {
this.visable = true;
let data = response.data.data;
if (data.writeFieldKey != undefined) {
this.$refs.kfb.enable(data.writeFieldKey.split(","));
}
if (data.hideFieldKey != undefined) {
this.$refs.kfb.hide(data.hideFieldKey.split(","));
this.hideFieldKey = data.hideFieldKey;
}
});
});
查询findFormParams参数后台方法
此方法主要查询:writeFieldKey和hideFieldKey,如果需要查询参数,则需要解析xml,具体方法如下:
1、根据流程对象获取当前bpmnModel,再根据bpmnModel获取xmlContenxt,通过XmlUtil.readXML解析xml,再通过XmlUtil.getNodeListByXPath(“//*[name()=’bpmn2:userTask’]”, document);获取流程图中所有的userTask节点。
//根据流程定义id获取bpmnModel对象
bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
//创建转换对象
BpmnXMLConverter converter = new BpmnXMLConverter();
//把bpmnModel对象转换成字符
byte[] bytes = converter.convertToXML(bpmnModel);
String xmlContenxt = new String(bytes);
System.out.println(xmlContenxt);
Document document = XmlUtil.readXML(xmlContenxt);
NodeList node = XmlUtil.getNodeListByXPath("//*[name()='bpmn2:userTask']", document);
2、通过获取的节点信息,进一步对xml内容进行解析,获取对应的formKey和writeFieldKey/hideFieldKey
完整代码如下:
/**
* @title findFormParams
* @description 查询表单参数
* @author Administrator
* @updateTime 2022/5/9 0009 21:42
*/
@PostMapping("/findFormParams")
public void findFormParams(@RequestBody PageData pd,HttpServletRequest request,HttpServletResponse response) throws Exception {
List<PageData> list = new ArrayList<PageData>();
//获取当前节点信息
BpmnModel bpmnModel = null;
if (pd.get("nodeType").equals("startNode")) {//未启动
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(pd.getString("processDefinitionKey")).latestVersion().singleResult();
//根据流程定义id获取bpmnModel对象
bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
//创建转换对象
BpmnXMLConverter converter = new BpmnXMLConverter();
//把bpmnModel对象转换成字符
byte[] bytes = converter.convertToXML(bpmnModel);
String xmlContenxt = new String(bytes);
System.out.println(xmlContenxt);
Document document = XmlUtil.readXML(xmlContenxt);
NodeList node = XmlUtil.getNodeListByXPath("//*[name()='findFormParams']", document);
String formKey = "";
String writeFieldKey = "";
String hideFieldKey = "";
for (int i = 0; i < node.getLength(); i++) {
if(Verify.verifyIsNotNull(node.item(i).getAttributes().getNamedItem("activiti:formKey"))){
formKey = node.item(i).getAttributes().getNamedItem("activiti:formKey").getNodeValue();
}
// System.out.println(node.item(i).getNodeName() + "---" + node.item(i).getAttributes().getNamedItem("id") + "---" + node.item(i).getAttributes().getNamedItem("name") + "---" + node.item(i).getAttributes().getNamedItem("activiti:formKey").getNodeValue());
NodeList childList = node.item(i).getChildNodes();
for (int j = 0; j < childList.getLength(); j++) {
// System.out.println(childList.item(j).getNodeName());
if (childList.item(j).getNodeName() != "extensionElements") {
continue;
}
NodeList cls = childList.item(j).getChildNodes();
for (int m = 0; m < cls.getLength(); m++) {
if (cls.item(m).getNodeName() != "activiti:field") {
continue;
}
if(Verify.verifyIsNotNull(cls.item(m).getAttributes().getNamedItem("writeFieldKey"))){
writeFieldKey = cls.item(m).getAttributes().getNamedItem("writeFieldKey").getNodeValue();
}
if(Verify.verifyIsNotNull(cls.item(m).getAttributes().getNamedItem("hideFieldKey").getNodeValue())){
hideFieldKey = cls.item(m).getAttributes().getNamedItem("hideFieldKey").getNodeValue();
}
System.out.println(cls.item(m).getAttributes().getNamedItem("elementText"));
}
}
}
pd.put("formKey",formKey);
pd.put("writeFieldKey",writeFieldKey);
pd.put("hideFieldKey",hideFieldKey);
} else {//已启动
Task task = taskService.createTaskQuery().taskId(pd.get("taskId").toString()).singleResult();
pd.put("model_id", task.getProcessDefinitionId().split(":")[0]);
//根据流程定义id获取bpmnModel对象
bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
//创建转换对象
BpmnXMLConverter converter = new BpmnXMLConverter();
//把bpmnModel对象转换成字符
byte[] bytes = converter.convertToXML(bpmnModel);
String xmlContenxt = new String(bytes);
System.out.println(xmlContenxt);
Document document = XmlUtil.readXML(xmlContenxt);
NodeList node = XmlUtil.getNodeListByXPath("//*[name()='bpmn2:userTask']", document);
String formKey = "";
String writeFieldKey = "";
String hideFieldKey = "";
for (int i = 0; i < node.getLength(); i++) {
System.out.println(task.getTaskDefinitionKey()+":::"+node.item(i).getAttributes().getNamedItem("id").getNodeValue());
if(task.getTaskDefinitionKey().equals(node.item(i).getAttributes().getNamedItem("id").getNodeValue())){
if(Verify.verifyIsNotNull(node.item(i).getAttributes().getNamedItem("activiti:formKey"))){
formKey = node.item(i).getAttributes().getNamedItem("activiti:formKey").getNodeValue();
}
// System.out.println(node.item(i).getNodeName() + "---" + node.item(i).getAttributes().getNamedItem("id") + "---" + node.item(i).getAttributes().getNamedItem("name") + "---" + node.item(i).getAttributes().getNamedItem("activiti:formKey").getNodeValue());
NodeList childList = node.item(i).getChildNodes();
for (int j = 0; j < childList.getLength(); j++) {
if (childList.item(j).getNodeName() != "extensionElements") {
continue;
}
NodeList cls = childList.item(j).getChildNodes();
for (int m = 0; m < cls.getLength(); m++) {
if (cls.item(m).getNodeName() != "activiti:field") {
continue;
}
if(Verify.verifyIsNotNull(cls.item(m).getAttributes().getNamedItem("writeFieldKey"))){
writeFieldKey = cls.item(m).getAttributes().getNamedItem("writeFieldKey").getNodeValue();
}
if(Verify.verifyIsNotNull(cls.item(m).getAttributes().getNamedItem("hideFieldKey"))){
hideFieldKey = cls.item(m).getAttributes().getNamedItem("hideFieldKey").getNodeValue();
}
// System.out.println(cls.item(m).getAttributes().getNamedItem("elementText"));
}
}
}
}
pd.put("formKey",formKey);
pd.put("writeFieldKey",writeFieldKey);
pd.put("hideFieldKey",hideFieldKey);
}
Json json = new Json();
json.setData(pd);
json.setSuccess(true);
json.setMsg("数据获取成功。");
this.writeJson(response,json);
}
新增流程审核、流程驳回、流程委派
为什么称为新增呢?因为之前的方法没有关联表单,仅仅是单纯的流程走向,本次因为设计到对表单的编辑,在执行流程操作之前需要先进行流程表单的存储。此处介绍流程审批,流程驳回和流程委派模式一致。
saveOrUpdate({
taskId: data.id,
approve_opinion: data.approve_opinion,
nodeData: data.nodeData,
submitType: "3",
...values,
id: this.form.id,
table_id: this.table_id,
menu_id: this.menu_id,
hideFieldKey: this.hideFieldKey,
}).then((response) => {
this.$emit("finishResponse", response);
resolve(true);
});
})
后端代码位置,具体细节可以查看源码分析: