一、流程审批介绍
对审批表单进行改造,核心操作功能如下:
1、根据当前节点信息获取当前的formkey,通过key查询对应的自定义表单进行展示。
2、根据当前节点信息获取当前节点的隐藏和展示字段设置信息,根据设置信息设置表单。
二、流程审核前端功能改造
审核FormCheck改造
1、新增VDataCheck审核表单组件
<VDataCheckref="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-buildref="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);
});
})
后端代码位置,具体细节可以查看源码分析:
