1、引入包
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M4</version>
</dependency>
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0.M4</version>
<type>pom</type>
</dependency>
2、spring boot 配置文件:spring 下追加:如果不加 好像没有历史表
activiti:
historyLevel: audit
db-history-used: true
3 、修改表结构
alter table ACT_RE_DEPLOYMENT add PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL;
alter table ACT_RE_DEPLOYMENT add VERSION_ varchar(255) DEFAULT NULL;
配置文件中加一笔:
流程发布:
其中xmlBPMN:
<?xml version="1.0" encoding="UTF-8"?><definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="m1605802588480" name="" targetNamespace="http://www.activiti.org/test"> <process id="Process_1" isExecutable="true"> <startEvent id="StartEvent_1y45yut" name="开始"> <outgoing>Flow_0jfbnmb</outgoing> </startEvent> <userTask id="Activity_1w1vj9r" name="username1" activiti:formKey="kkk" activiti:assignee="1" activiti:candidateUsers="candidateUsers" activiti:candidateGroups="candidateGroups"> <extensionElements> <activiti:formData> <activiti:formProperty id="formProperty_3slq4mu" /> </activiti:formData> <activiti:properties> <activiti:property /> </activiti:properties> </extensionElements> <incoming>Flow_0jfbnmb</incoming> <outgoing>Flow_1nolku9</outgoing> </userTask> <sequenceFlow id="Flow_0jfbnmb" sourceRef="StartEvent_1y45yut" targetRef="Activity_1w1vj9r" /> <endEvent id="Event_0ahqy2x" name="e"> <incoming>Flow_1nolku9</incoming> </endEvent> <sequenceFlow id="Flow_1nolku9" sourceRef="Activity_1w1vj9r" targetRef="Event_0ahqy2x" /> </process> <bpmndi:BPMNDiagram id="BpmnDiagram_1"> <bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process_1"> <bpmndi:BPMNEdge id="Flow_1nolku9_di" bpmnElement="Flow_1nolku9"> <omgdi:waypoint x="400" y="120" /> <omgdi:waypoint x="542" y="120" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="Flow_0jfbnmb_di" bpmnElement="Flow_0jfbnmb"> <omgdi:waypoint x="188" y="120" /> <omgdi:waypoint x="300" y="120" /> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="StartEvent_1y45yut_di" bpmnElement="StartEvent_1y45yut"> <omgdc:Bounds x="152" y="102" width="36" height="36" /> <bpmndi:BPMNLabel> <omgdc:Bounds x="160" y="145" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="Activity_1w1vj9r_di" bpmnElement="Activity_1w1vj9r"> <omgdc:Bounds x="300" y="80" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="Event_0ahqy2x_di" bpmnElement="Event_0ahqy2x"> <omgdc:Bounds x="542" y="102" width="36" height="36" /> <bpmndi:BPMNLabel> <omgdc:Bounds x="557" y="145" width="7" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram></definitions>
启动流程:
完成任务:
工具流程实例查询:
流程定义:
前端:采取bpmn.js
bpmn.js汉化
放一排图标到顶部:
找到保存的方法?
- 初始化一个空白设计页面
搞个空的流程,结果属性面板没显示?算了 先这样
- 向后传递 如何设置名字
- 设置用户
- 查看设计好的工作流
- 我发起的入围
- 待我执行的任务
- 我完成的流程
- 流程归档
- 和表单关联
import导入包
新的工作流设计器:
bpmnDesign.js
import React, { useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';
import BpmnModeler from 'bpmn-js/lib/Modeler';
// 以下为bpmn工作流绘图工具的样式
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css' // bpmnlint
import lintModule from 'bpmn-js-bpmnlint';
import * as bpmnlintConfig from './packed-config';
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-embedded.css'
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右边工具栏样式
//為右邊屬性
import propertiesPanelModule from 'bpmn-js-properties-panel'
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import {Button, Col, Divider, Input, Modal, Row, Space, List, message, Table, Form} from 'antd';
import customTranslateModule from '../BpmnEditor/Modeler/customTranslate';
import getDefaultXml from '../BpmnEditor/sources/xml';
import {Link} from "react-router-dom";
import {
SearchOutlined,
ZoomInOutlined,
ZoomOutOutlined,
SaveOutlined,
ExportOutlined,
VideoCameraOutlined,
UploadOutlined,
TagOutlined,
CheckOutlined,
IssuesCloseOutlined
} from '@ant-design/icons';
import {addByString, getDefinitions} from "../api/workflow";
import { getUserList } from "../api/Security";
import {withRouter} from 'react-router-dom';
import WorkflowDesign from "./workflowDesign";
export default function BpmnDesign() {
// let bpmnModeler=null;
//const [mybpmnModeler, setMybpmnModeler] = useState(null); //所有用户列表 供选择人使用
const modelerRef = useRef(null);
const [users, setUsers] = useState([]); //所有用户列表 供选择人使用
//
//控制可见性
const [modalvisiable, setModalvisiable] = useState(false); //modal可见性
// const [selectedRowKeys,setSelectedRowKeys] = useState([]); //选择的人
const [selectedUserId,setSelectedUserId] = useState(-1); //选择的人
const [selectedUserName,setSelectedName] = useState(""); //选择的人的名字
const [selectElement,setSelectElement] = useState(null); //选择usertask节点
const [activeNodeEle, setActiveNode] = useState(null); //代替上一行的内容
const [lintActive,setLintActive]= useState(false);
useEffect(()=>{
initBpmn();
},[])
// var customTranslateModule = {
// translate: [ 'value', customTranslate ]
// };
const columns = [
{
title: 'id',
dataIndex: 'userid',
key: 'userid',
},
{
title: '用户名',
dataIndex: 'name',
key: 'name',
},
{
title: '启用状态',
dataIndex: 'userFlag',
key: 'userFlag',
}
];
const initBpmn=()=>{
modelerRef.current =
new BpmnModeler({
container:"#canvas",
//添加控制板
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
// 右边的属性栏
customTranslateModule,
propertiesProviderModule,
propertiesPanelModule,
lintModule
],
linting: {
bpmnlint: bpmnlintConfig,
active:false //如果要检查 要配置这里
},
moddleExtensions: {
camunda: camundaModdleDescriptor
},
height:'100vh'
})
const logo = document.querySelector('.bjs-powered-by');
const bpmnContainer = document.querySelector('.bjs-container');
if (bpmnContainer && logo) {
bpmnContainer.removeChild(logo);
}
renderDiagram();
getUsers(); //获取全部用户信息 为选择用户做准备
addListener(); //模型上追加监听器
}
const initActiveNode = () => {
const canvas = modelerRef.current.get('canvas');
const rootElement = canvas.getRootElement();
setActiveNode(rootElement);
};
// const createBpmnDiagram= async ()=>{
// let xmlStr2="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:camunda=\"http://camunda.org/schema/1.0/bpmn\" xmlns:activiti=\"http://activiti.org/bpmn\" xmlns:tns=\"http://www.activiti.org/test\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" id=\"m1605802588480\" name=\"\" targetNamespace=\"http://www.activiti.org/test\">\\n <process id=\"Process_1\" isExecutable=\"true\">\\n <startEvent id=\"StartEvent_1y45yut\" name=\"开始\">\\n <outgoing>Flow_0jfbnmb</outgoing>\\n </startEvent>\\n <userTask id=\"Activity_1w1vj9r\" name=\"username1\" activiti:formKey=\"kkk\" activiti:assignee=\"1\" activiti:candidateUsers=\"candidateUsers\" activiti:candidateGroups=\"candidateGroups\">\\n <extensionElements>\\n <activiti:formData>\\n <activiti:formProperty id=\"formProperty_3slq4mu\" />\\n </activiti:formData>\\n <activiti:properties>\\n <activiti:property />\\n </activiti:properties>\\n </extensionElements>\\n <incoming>Flow_0jfbnmb</incoming>\\n <outgoing>Flow_1nolku9</outgoing>\\n </userTask>\\n <sequenceFlow id=\"Flow_0jfbnmb\" sourceRef=\"StartEvent_1y45yut\" targetRef=\"Activity_1w1vj9r\" />\\n <endEvent id=\"Event_0ahqy2x\" name=\"e\">\\n <incoming>Flow_1nolku9</incoming>\\n </endEvent>\\n <sequenceFlow id=\"Flow_1nolku9\" sourceRef=\"Activity_1w1vj9r\" targetRef=\"Event_0ahqy2x\" />\\n </process>\\n <bpmndi:BPMNDiagram id=\"BpmnDiagram_1\">\\n <bpmndi:BPMNPlane id=\"BpmnPlane_1\" bpmnElement=\"Process_1\">\\n <bpmndi:BPMNEdge id=\"Flow_0jfbnmb_di\" bpmnElement=\"Flow_0jfbnmb\">\\n <omgdi:waypoint x=\"188\" y=\"120\" />\\n <omgdi:waypoint x=\"300\" y=\"120\" />\\n </bpmndi:BPMNEdge>\\n <bpmndi:BPMNEdge id=\"Flow_1nolku9_di\" bpmnElement=\"Flow_1nolku9\">\\n <omgdi:waypoint x=\"400\" y=\"120\" />\\n <omgdi:waypoint x=\"542\" y=\"120\" />\\n </bpmndi:BPMNEdge>\\n <bpmndi:BPMNShape id=\"StartEvent_1y45yut_di\" bpmnElement=\"StartEvent_1y45yut\">\\n <omgdc:Bounds x=\"152\" y=\"102\" width=\"36\" height=\"36\" />\\n <bpmndi:BPMNLabel>\\n <omgdc:Bounds x=\"160\" y=\"145\" width=\"22\" height=\"14\" />\\n </bpmndi:BPMNLabel>\\n </bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"Activity_1w1vj9r_di\" bpmnElement=\"Activity_1w1vj9r\">\\n <omgdc:Bounds x=\"300\" y=\"80\" width=\"100\" height=\"80\" />\\n </bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"Event_0ahqy2x_di\" bpmnElement=\"Event_0ahqy2x\">\\n <omgdc:Bounds x=\"542\" y=\"102\" width=\"36\" height=\"36\" />\\n <bpmndi:BPMNLabel>\\n <omgdc:Bounds x=\"557\" y=\"145\" width=\"7\" height=\"14\" />\\n </bpmndi:BPMNLabel>\\n </bpmndi:BPMNShape>\\n </bpmndi:BPMNPlane>\\n </bpmndi:BPMNDiagram>\\n</definitions>\n";
// mybpmnModeler.importXML(xmlStr2);
// const diagramXML = getDefaultXml();
// //this.renderDiagram(diagramXML);
// try {
// //const result = await bpmnModeler.importXML(xmlStr2);
// } catch (e) {
// console.error(e);
// }
// }
const renderDiagram = () => {
//let XML="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:camunda=\"http://camunda.org/schema/1.0/bpmn\" xmlns:activiti=\"http://activiti.org/bpmn\" xmlns:tns=\"http://www.activiti.org/test\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" id=\"m1605802588480\" name=\"\" targetNamespace=\"http://www.activiti.org/test\">\\n <process id=\"Process_1\" isExecutable=\"true\">\\n <startEvent id=\"StartEvent_1y45yut\" name=\"开始\">\\n <outgoing>Flow_0jfbnmb</outgoing>\\n </startEvent>\\n <userTask id=\"Activity_1w1vj9r\" name=\"username1\" activiti:formKey=\"kkk\" activiti:assignee=\"1\" activiti:candidateUsers=\"candidateUsers\" activiti:candidateGroups=\"candidateGroups\">\\n <extensionElements>\\n <activiti:formData>\\n <activiti:formProperty id=\"formProperty_3slq4mu\" />\\n </activiti:formData>\\n <activiti:properties>\\n <activiti:property />\\n </activiti:properties>\\n </extensionElements>\\n <incoming>Flow_0jfbnmb</incoming>\\n <outgoing>Flow_1nolku9</outgoing>\\n </userTask>\\n <sequenceFlow id=\"Flow_0jfbnmb\" sourceRef=\"StartEvent_1y45yut\" targetRef=\"Activity_1w1vj9r\" />\\n <endEvent id=\"Event_0ahqy2x\" name=\"e\">\\n <incoming>Flow_1nolku9</incoming>\\n </endEvent>\\n <sequenceFlow id=\"Flow_1nolku9\" sourceRef=\"Activity_1w1vj9r\" targetRef=\"Event_0ahqy2x\" />\\n </process>\\n <bpmndi:BPMNDiagram id=\"BpmnDiagram_1\">\\n <bpmndi:BPMNPlane id=\"BpmnPlane_1\" bpmnElement=\"Process_1\">\\n <bpmndi:BPMNEdge id=\"Flow_0jfbnmb_di\" bpmnElement=\"Flow_0jfbnmb\">\\n <omgdi:waypoint x=\"188\" y=\"120\" />\\n <omgdi:waypoint x=\"300\" y=\"120\" />\\n </bpmndi:BPMNEdge>\\n <bpmndi:BPMNEdge id=\"Flow_1nolku9_di\" bpmnElement=\"Flow_1nolku9\">\\n <omgdi:waypoint x=\"400\" y=\"120\" />\\n <omgdi:waypoint x=\"542\" y=\"120\" />\\n </bpmndi:BPMNEdge>\\n <bpmndi:BPMNShape id=\"StartEvent_1y45yut_di\" bpmnElement=\"StartEvent_1y45yut\">\\n <omgdc:Bounds x=\"152\" y=\"102\" width=\"36\" height=\"36\" />\\n <bpmndi:BPMNLabel>\\n <omgdc:Bounds x=\"160\" y=\"145\" width=\"22\" height=\"14\" />\\n </bpmndi:BPMNLabel>\\n </bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"Activity_1w1vj9r_di\" bpmnElement=\"Activity_1w1vj9r\">\\n <omgdc:Bounds x=\"300\" y=\"80\" width=\"100\" height=\"80\" />\\n </bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"Event_0ahqy2x_di\" bpmnElement=\"Event_0ahqy2x\">\\n <omgdc:Bounds x=\"542\" y=\"102\" width=\"36\" height=\"36\" />\\n <bpmndi:BPMNLabel>\\n <omgdc:Bounds x=\"557\" y=\"145\" width=\"7\" height=\"14\" />\\n </bpmndi:BPMNLabel>\\n </bpmndi:BPMNShape>\\n </bpmndi:BPMNPlane>\\n </bpmndi:BPMNDiagram>\\n</definitions>\n";
const XML = getDefaultXml();
modelerRef.current.importXML(XML, (err) => {
if (err) {
Modal.error({ title: 'XML解析失败' });
return false;
}
initActiveNode();
return true;
});
};
const getUsers=()=>{
getUserList().then(
(res) => {
setUsers(res.data);
},
(error) => {
console.log("get UserList failed!");
}
);
}
//追加监听器
const addListener=()=>{
addModelerListener();
addEventBusListener();
}
const addEventBusListener=()=>{
const bpmnjs = modelerRef.current;
const eventBus = bpmnjs.get('eventBus');
eventBus.on('element.click', function(e) {
elementClick(e);
});
}
const addModelerListener = () => {
const bpmnjs = modelerRef.current;
//const events = ['element.click', 'element.changed'];
const events = ['element.click'];
events.forEach((eventType) => {
bpmnjs.on(eventType, (e) => {
const { element } = e;
const { type } = element;
if (type !== 'label') {
setActiveNode(element);
}
});
});
};
// const addModelerListener=()=>{
// // bpmnModeler.on('element.click', e => {
// // console.log('modelerListener', e);
// // });
// }
const elementClick=(e)=> {
const bpmnjs = modelerRef.current;
// let modeling = bpmnjs.get('modeling');
if (e.element.businessObject.$type === 'bpmn:StartEvent') {
console.log(
'这是一个开始节点',
e.element.businessObject.id,
e.element.businessObject.$type,
e.element.businessObject.name
);
// 修改节点ID
// modeling.updateProperties(e.element, {
// id: 'StartEvent_ops_coffee'
// });
}
if (e.element.businessObject.$type === 'bpmn:UserTask') {
console.log(
'这是一个用户节点',
e.element.businessObject.id,
e.element.businessObject.$type,
e.element.businessObject.name
);
//this.selectElement = e.element;
setSelectElement(e.element); //选中的元素
//this.setColumnDialogVisible = true;
//console.log(this.selectElement);
//修改节点名称
// modeling.updateProperties(e.element, {
// name: this.user.id
// });
}else{
setSelectElement(null);
}
}
const container = {
width: "100%",
height:"100%"
};
const bpmnDesignClass = {
minHeight: "100vh",
};
const properties_panel = {
// position:"absolute",
// right:"0",
// top:"0",
// width:"300px"
};
//get xml
const getBpmnXML = async () => {
const bpmnModeler = modelerRef.current;
return new Promise((resolve, reject) => {
bpmnModeler.saveXML({ format: true }, (err, xml) => {
if (err) {
reject(err);
}
resolve(xml);
});
});
};
const onSave = async () => {
const xml = await getBpmnXML();
addByString(xml).then(
(res) => {
message.success('保存成功');
},
(error) => {
message.error('failed');
console.log("get response failed!");
}
);
};
const saveDiagram = () => {
//verify by lint?
const bpmnModeler = modelerRef.current;
const linting = bpmnModeler.get('linting');
const { _button, _active } = linting;
if (!_active) {
linting.toggle();
message.success('开启流程检查!');
return;
}
const { innerText } = _button;
const errorTexts = Number(innerText.split(' Errors')[0]);
if (errorTexts > 0) {
message.success('流程有错误,无法保存,请检查!');
return;
}
onSave().then(
console.log("saveDiagram!")
);
};
//选择雇员
const SearchEmp= () => {
if(selectElement==null){
setModalvisiable(false);
setSelectedUserId(-1);
}else {
setModalvisiable(true);
setSelectedUserId(-1);
}
};
const returnDefList=()=>{
// BrowserRouter.context.routes.push('/main/tool/processDef');
//history.push('/main/tool/processDef')
}
const cancelModal=()=>{
setModalvisiable(false);
}
const onSelectChange = selectedRowKeys => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
setSelectedRowKeys(selectedRowKeys );
};
// const rowSelection = {
// selectedRowKeys,
// onChange: onSelectChange,
// };
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
onSelect: (record, selected, selectedRows) => {
if(selected){
// this.setState({
// selectedUserId: record.userid,
// });
setSelectedUserId( record.userid);
//setSelectedName( record.name);
}
},
};
//选择确定
const selConfirm = ()=>{
const bpmnModeler = modelerRef.current;
//选择的用户
// if(selectedRowKeys.length==0){return}
// let uId=selectedRowKeys[0];
//
// let t=users.filter(item=>item.userid=uId);
//
// if (t.length==0) return;
if (selectedUserId==-1){
message.info("请选择一个用户")
return;
}
let modeling = bpmnModeler.get('modeling');
modeling.updateProperties(selectElement, {
name: selectedUserId+"",
//执行人
assignee: selectedUserId+"" ,
//候选人
// candidateUsers:'candidateUsers',
//候选组
// candidateGroups:'candidateGroups'
});
setModalvisiable(false);
};
const checkBPMN=()=>{
const bpmnModeler = modelerRef.current;
const linting = bpmnModeler.get('linting');
const { _button, _active } = linting;
if (!_active) {
linting.toggle();
message.success('开启流程检查!');
return;
}
}
const closeCheckBPMN=()=>{
const bpmnModeler = modelerRef.current;
const linting = bpmnModeler.get('linting');
const { _button, _active } = linting;
if (_active) {
linting.toggle();
message.success('关闭流程检查!');
return;
}
}
return(
<>
<div className={bpmnDesignClass}>
<Row justify={"center"}>
<Button icon={<SearchOutlined/>} key={"SearchEmp"}
onClick={() => SearchEmp()}
/>
<Button icon={<ZoomInOutlined />} key={"zoomin"}
/>
<Button icon={<ZoomOutOutlined />} key={"zoomout"}/>
<Button icon={<CheckOutlined />} key={"CheckOutlined"}
onClick={() => checkBPMN()}
/>
<Button icon={ <IssuesCloseOutlined />} key={"IssuesCloseOutlined"}
onClick={() => closeCheckBPMN()}
/>
<Button icon={<SaveOutlined />} key={"SaveOutlined"}
onClick={() => saveDiagram()}
/>
<Link to={"/main/tool/processDef"}>
<Button icon={<ExportOutlined />} key={"ExportOutlined"}/>
</Link>
</Row>
<Row>
<Col span={18}>
<div id="canvas" className={container}>
</div>
</Col>
<Col span={6}>
<div
className={properties_panel}
id="js-properties-panel"
style={{ height: '100%' }}
/>
</Col>
</Row>
</div>
<Modal
width={600}
title="选择审批人"
visible={modalvisiable}
cancelText='返回'
onCancel={cancelModal}
footer={null}
>
<Table
//rowKey={record => record.userid}
// rowSelection={rowSelection}
rowSelection={{
type: "radio",
...rowSelection,
}}
rowKey="userid"
columns={columns}
dataSource={users} />
<Row>
<Col span={16}></Col>
<Col span={3}><Button type="primary" onClick={selConfirm}>确定</Button></Col>
<Col span={3}><Button onClick={cancelModal}>取消</Button></Col>
<Col span={2}></Col>
</Row>
</Modal>
</>
)
}
lint 使用:ps:基于bpmn-js的流程设计器校验实现
使用目的:校验
注意点:
linting: {
bpmnlint: bpmnlintConfig,
active:false //如果要检查 要配置这里
},
lint配置文件.bpmnlintrc:
{
"extends": "bpmnlint:recommended",
"rules": {
"label-required": "off"
}
}
基于bpmn.js、React、Midway.js的在线流程编辑、数据管理工具(1)
查看流程:
调整--带参数
查看流程界面读取参数
根据参数查询后台流程定义数据
根据后台数据渲染页面
启动工作流:
/**
*
* @param id act_re_procdef.id
* @param businessKey 业务 businessKey,自定义
* @return
*/
@ApiOperation(value = "启动流程")
@GetMapping("/startProcessInstanceById")
public R startProcessInstanceById(String id, String businessKey) {
int userId=UserUtil.getUserId();
Authentication.setAuthenticatedUserId(userId+"");
ProcessInstance processInstance = runtimeService.startProcessInstanceById(id, businessKey);
return R.strData(processInstance.getId());
}
查看流程实例:
select * from ACT_HI_PROCINST t
删除流程实例:
@ApiOperation(value = "删除流程")
@GetMapping("/deleteProcessInstance")
public R deleteProcessInstance(String processInstanceId,String deleteReason) {
ProcessInstance result = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (result != null) {
runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
return R.strData("删除成功");
}else {
throw new ReturnException("未找到该流程");
}
}
查询我的发起的流程:
/**
* 获取流程实例
*/
@ApiOperation(value = "查询流程信息by user")
@GetMapping("/getProcessInstancesByUserId")
public R getProcessInstancesByUserId() {
//act_hi_procinst
int userId=UserUtil.getUserId();
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().startedBy(userId+"")
.list();
List<Map<String, Object>> collect = list.stream().map(processInstance -> {
//获取资源
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(processInstance.getDeploymentId()).singleResult();
return new CreateMap.Build()
.setAttribute("id", processInstance.getId())
.setAttribute("deploymentId", processInstance.getDeploymentId())
.setAttribute("description", processInstance.getDescription())
.setAttribute("name", processInstance.getName())
.setAttribute("businessKey", processInstance.getBusinessKey())
.setAttribute("processDefinitionKey", processInstance.getProcessDefinitionKey())
.setAttribute("processDefinitionId", processInstance.getProcessDefinitionId())
.setAttribute("processDefinitionName", processInstance.getProcessDefinitionName())
//是否完成
.setAttribute("isEnded", processInstance.isEnded())
//是否挂起
.setAttribute("isSuspended", processInstance.isSuspended())
.setAttribute("resourceName", processDefinition.getResourceName())
.build();
}).collect(Collectors.toList());
return R.ok(collect);
}
根据名字查询我的任务:
/**
* 查询我的待办任务
*/
@GetMapping("/getTaskByAssignee")
public R getTaskByAssignee(String assignee) {
List<Task> zhangsanTask = taskService.createTaskQuery()
.taskAssignee(assignee)
//.taskName("zhangsan")
.list();
List<Map<String, Object>> collect = zhangsanTask.stream().map(tk -> {
return new CreateMap.Build()
.setAttribute("id", tk.getId())
.setAttribute("processInstanceId", tk.getProcessInstanceId())
.setAttribute("name", tk.getName())
.setAttribute("assignee", tk.getAssignee())
//是否挂起
.setAttribute("isSuspended", tk.isSuspended())
.build();
}).collect(Collectors.toList());
return R.ok(collect);
}
{
"code": "200",
"data": [
{
"processInstanceId": "0325fddf-c4dd-11eb-90ed-505bc2b03111",
"isSuspended": false,
"name": "admin",
"id": "032cb4a4-c4dd-11eb-90ed-505bc2b03111",
"assignee": "21001"
}
]
}
完成任务:
根据processInstanceId 查询流程定义信息
查看流程实例:
import React, { useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';
// 以下为bpmn工作流绘图工具的样式
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css' // bpmnlint
import BpmnViewer from 'bpmn-js/lib/Viewer';
import {getDef, getHighlight} from "../api/workflow";
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-embedded.css'
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右边工具栏样式
//為右邊屬性
import propertiesPanelModule from 'bpmn-js-properties-panel'
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import {Button, Col, Divider, Input, Modal, Row, Space, List, message, Table, Form} from 'antd';
import customTranslateModule from '../BpmnEditor/Modeler/customTranslate';
import './ViewProcInst.css';
export default function ViewProcInst(prop) {
const modelerRef = useRef(null);
useEffect(()=>{ //读取外部
let procId=prop.procId;
let procInstId=prop.procInstId;
initBpmn(procId,procInstId);
},[])
const initBpmn=(procId,procInstId)=>{
modelerRef.current =
new BpmnViewer({
container:"#canvas",
additionalModules: [
customTranslateModule,
],
height:'100vh'
})
const logo = document.querySelector('.bjs-powered-by');
const bpmnContainer = document.querySelector('.bjs-container');
if (bpmnContainer && logo) {
bpmnContainer.removeChild(logo);
}
renderDiagram(procId,procInstId);
//setColor2(procInstId) //不能这样写 因为异步的原因!!!
}
const renderDiagram = (procId,procInstId) => {
let xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:camunda=\"http://camunda.org/schema/1.0/bpmn\" xmlns:activiti=\"http://activiti.org/bpmn\" xmlns:tns=\"http://www.activiti.org/test\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" id=\"m1605802588480\" name=\"\" targetNamespace=\"http://www.activiti.org/test\">\\n <process id=\"Process_1\" isExecutable=\"true\">\\n <startEvent id=\"StartEvent_1y45yut\" name=\"开始\">\\n <outgoing>Flow_0jfbnmb</outgoing>\\n </startEvent>\\n <userTask id=\"Activity_1w1vj9r\" name=\"username1\" activiti:formKey=\"kkk\" activiti:assignee=\"1\" activiti:candidateUsers=\"candidateUsers\" activiti:candidateGroups=\"candidateGroups\">\\n <extensionElements>\\n <activiti:formData>\\n <activiti:formProperty id=\"formProperty_3slq4mu\" />\\n </activiti:formData>\\n <activiti:properties>\\n <activiti:property />\\n </activiti:properties>\\n </extensionElements>\\n <incoming>Flow_0jfbnmb</incoming>\\n <outgoing>Flow_1nolku9</outgoing>\\n </userTask>\\n <sequenceFlow id=\"Flow_0jfbnmb\" sourceRef=\"StartEvent_1y45yut\" targetRef=\"Activity_1w1vj9r\" />\\n <endEvent id=\"Event_0ahqy2x\" name=\"e\">\\n <incoming>Flow_1nolku9</incoming>\\n </endEvent>\\n <sequenceFlow id=\"Flow_1nolku9\" sourceRef=\"Activity_1w1vj9r\" targetRef=\"Event_0ahqy2x\" />\\n </process>\\n <bpmndi:BPMNDiagram id=\"BpmnDiagram_1\">\\n <bpmndi:BPMNPlane id=\"BpmnPlane_1\" bpmnElement=\"Process_1\">\\n <bpmndi:BPMNEdge id=\"Flow_0jfbnmb_di\" bpmnElement=\"Flow_0jfbnmb\">\\n <omgdi:waypoint x=\"188\" y=\"120\" />\\n <omgdi:waypoint x=\"300\" y=\"120\" />\\n </bpmndi:BPMNEdge>\\n <bpmndi:BPMNEdge id=\"Flow_1nolku9_di\" bpmnElement=\"Flow_1nolku9\">\\n <omgdi:waypoint x=\"400\" y=\"120\" />\\n <omgdi:waypoint x=\"542\" y=\"120\" />\\n </bpmndi:BPMNEdge>\\n <bpmndi:BPMNShape id=\"StartEvent_1y45yut_di\" bpmnElement=\"StartEvent_1y45yut\">\\n <omgdc:Bounds x=\"152\" y=\"102\" width=\"36\" height=\"36\" />\\n <bpmndi:BPMNLabel>\\n <omgdc:Bounds x=\"160\" y=\"145\" width=\"22\" height=\"14\" />\\n </bpmndi:BPMNLabel>\\n </bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"Activity_1w1vj9r_di\" bpmnElement=\"Activity_1w1vj9r\">\\n <omgdc:Bounds x=\"300\" y=\"80\" width=\"100\" height=\"80\" />\\n </bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"Event_0ahqy2x_di\" bpmnElement=\"Event_0ahqy2x\">\\n <omgdc:Bounds x=\"542\" y=\"102\" width=\"36\" height=\"36\" />\\n <bpmndi:BPMNLabel>\\n <omgdc:Bounds x=\"557\" y=\"145\" width=\"7\" height=\"14\" />\\n </bpmndi:BPMNLabel>\\n </bpmndi:BPMNShape>\\n </bpmndi:BPMNPlane>\\n </bpmndi:BPMNDiagram>\\n</definitions>\n";
// let xml = getDefaultXml();
getDef(procId).then(
(res) => {
xml=res.data;
console.log("======xmlxmlxml======:"+xml);
modelerRef.current.importXML(xml, (err) => {
if (err) {
Modal.error({ title: 'XML解析失败' });
return false;
}
setColor2(procInstId)
return true;
});
},
(error) => {
console.log("get response failed!");
}
);
};
const setColor2=(procInstId)=> {
debugger;
// access viewer components
const canvas = modelerRef.current.get('canvas');
// 获取到全部节点
const allShapes = modelerRef.current.get('elementRegistry').getAll();
//获取高亮的shapeId
getHighlight(procInstId).then((res) => {
debugger;
const { highPoint } = res.data;
//循环节点添加颜色
allShapes.forEach(element => {
const shapeId = element.businessObject.id;
if (element.businessObject.$type == 'bpmn:UserTask') {
highPoint.forEach(utid => {
if (shapeId == utid) {
console.log("===========utid======:"+utid+"=====shapeId:======="+shapeId)
canvas.addMarker(shapeId, 'highlight');
}
});
}
});
});
}
const container = {
width: "100%",
height:"100%"
};
const bpmnDesignClass = {
minHeight: "100vh",
};
return(
<>
<div id="canvas" className={container}>
</div>
</>
)
}
ps:React中使用CSS样式的五种方法,主流推荐CSS Modules和Styled Components
后端java:
/**
* 高亮显示路程历史
*
* @param instanceId 流程实例id
//* @param username 用户信息
* @return
*/
@GetMapping("/getHighlight")
public R getHighlight(
@RequestParam("instanceId") String instanceId
//,@RequestParam("username") String username
) {
int userId=UserUtil.getUserId();
try {
// 获取一条流程实例历史
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(instanceId)
.singleResult();
// 根据流程定义 key 获取 BMPN
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 获取流程
Process process = bpmnModel.getProcesses().get(0);
// 获取所有流程 FlowElement 的信息,就是所有bpmn的节点
Collection<FlowElement> flowElements = process.getFlowElements();
/**
* 这个 map 中的 key 就是 开始节点和结束节点的编号拼起来的字符串,
* value 就是开始节点和结束节点的连线,到时候我们根据开始节点和结束节点
* 就可以获取到需要高亮的连线
*/
Map<String, String> map = new HashMap<String, String>();
for (FlowElement flowElement : flowElements) {
// 判断是否是线条
if (flowElement instanceof SequenceFlow) {
SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
String ref = sequenceFlow.getSourceRef();
String targetRef = sequenceFlow.getTargetRef();
/**
* 保存开始节点和结束节点,与它们之间连线的对应关系
* key: 开始节点 编号 + 结束节点 编号
* value: 连线编号
*/
map.put(ref + targetRef, sequenceFlow.getId());
}
}
/**
* 获取已经完成的全部流程历史节点
*/
List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
.list();
/**
* 将各个历史节的开始节点和结束几点的编号两两对应起来,
* 就可以从上面的 map 中获取到需要高亮的连线
*/
Set<String> keyList = new HashSet<String>();
for (HistoricActivityInstance i : list) {
for (HistoricActivityInstance j : list) {
if (i != j) {
keyList.add(i.getActivityId() + j.getActivityId());
}
}
}
// 获取高亮连线 id
Set<String> highLine = new HashSet<String>();
// 根据已经完成的(开始节点编号+结束节点编号)组成的 key 获取需要高亮的连线编号
keyList.forEach(s -> highLine.add(map.get(s)));
// 获取已经完成的节点
List<HistoricActivityInstance> listFinished = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
.finished()
.list();
// 已经完成的节点高亮
Set<String> highPoint = new HashSet<>();
// 保存已经完成的流程节点编号
listFinished.forEach(s -> highPoint.add(s.getActivityId()));
// 获取代办节点
List<HistoricActivityInstance> listUnFinished = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
.unfinished()
.list();
// 代办的节点高亮
Set<String> waitingToDo = new HashSet<>();
// 保存需要代办的节点编号
listUnFinished.forEach(s -> waitingToDo.add(s.getActivityId()));
// 获取当前用户完成的任务
List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery()
// .taskAssignee(username)
.taskAssignee(userId+"")
.processInstanceId(instanceId)
.finished()
.list();
// 当前用户完成的高亮
Set<String> iDo = new HashSet<String>();
// 保存用户完成的节点编号
taskInstanceList.forEach(s -> iDo.add(s.getTaskDefinitionKey()));
Map<String, Object> reMap = new HashMap<String, Object>();
// 高亮已经完成的节点
reMap.put("highPoint", highPoint);
// 高亮连线节点编号
reMap.put("highLine", highLine);
// 高亮代办节点编号
reMap.put("waitingToDo", waitingToDo);
// 高亮当前用户完成的节点编号
reMap.put("iDo", iDo);
return R.ok(reMap);
} catch (Exception e) {
return R.error("");
}
}
我完成的流程接口相应信息:(/report/act/history/myFinishTask)
{
"code": "200",
"data": [
{
"processInstanceId": "d71f0601-c8f1-11eb-995e-505bc2b03111",
"taskDefinitionKey": "Activity_1igvbr0",
"name": "21001",
"id": "d72ce8b6-c8f1-11eb-995e-505bc2b03111",
"assignee": "21001",
"procDefName": "testProc001"
},
{
"processInstanceId": "db0aa52e-c995-11eb-aed6-505bc2b03111",
"taskDefinitionKey": "task001_xj",
"name": "21001",
"id": "db149043-c995-11eb-aed6-505bc2b03111",
"assignee": "21001",
"procDefName": "Process_100xjname"
}
]
}