2022-01-03 02:41:24 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="my-process-designer">
|
|
|
|
|
<div class="my-process-designer__container">
|
2022-01-18 08:15:43 +08:00
|
|
|
|
<div class="my-process-designer__canvas" ref="bpmn-canvas"></div>
|
2022-01-03 02:41:24 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import BpmnViewer from "bpmn-js/lib/Viewer";
|
|
|
|
|
import DefaultEmptyXML from "./plugins/defaultEmpty";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: "MyProcessViewer",
|
|
|
|
|
componentName: "MyProcessViewer",
|
|
|
|
|
props: {
|
|
|
|
|
value: String, // xml 字符串
|
|
|
|
|
prefix: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: "camunda"
|
2022-01-18 01:41:22 +08:00
|
|
|
|
},
|
|
|
|
|
taskData: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => []
|
2022-01-03 02:41:24 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
2022-01-18 01:41:22 +08:00
|
|
|
|
return {
|
|
|
|
|
xml: '',
|
|
|
|
|
tasks: [],
|
|
|
|
|
};
|
2022-01-03 02:41:24 +08:00
|
|
|
|
},
|
|
|
|
|
mounted() {
|
2022-01-18 01:41:22 +08:00
|
|
|
|
this.xml = this.value;
|
|
|
|
|
this.tasks = this.taskData;
|
|
|
|
|
// 初始化
|
2022-01-03 02:41:24 +08:00
|
|
|
|
this.initBpmnModeler();
|
2022-01-18 01:41:22 +08:00
|
|
|
|
this.createNewDiagram(this.xml);
|
2022-01-03 02:41:24 +08:00
|
|
|
|
this.$once("hook:beforeDestroy", () => {
|
|
|
|
|
if (this.bpmnModeler) this.bpmnModeler.destroy();
|
|
|
|
|
this.$emit("destroy", this.bpmnModeler);
|
|
|
|
|
this.bpmnModeler = null;
|
|
|
|
|
});
|
2022-01-19 13:39:12 +08:00
|
|
|
|
// 初始模型的监听器
|
|
|
|
|
this.initModelListeners();
|
2022-01-03 02:41:24 +08:00
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
value: function (newValue) { // 在 xmlString 发生变化时,重新创建,从而绘制流程图
|
2022-01-18 01:41:22 +08:00
|
|
|
|
this.xml = newValue;
|
|
|
|
|
this.createNewDiagram(this.xml);
|
|
|
|
|
},
|
|
|
|
|
taskData: function (newTaskData) {
|
|
|
|
|
this.tasks = newTaskData;
|
|
|
|
|
this.createNewDiagram(this.xml);
|
2022-01-03 02:41:24 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
initBpmnModeler() {
|
|
|
|
|
if (this.bpmnModeler) return;
|
|
|
|
|
this.bpmnModeler = new BpmnViewer({
|
2022-01-18 08:15:43 +08:00
|
|
|
|
container: this.$refs["bpmn-canvas"],
|
2022-01-18 01:41:22 +08:00
|
|
|
|
bpmnRenderer: {
|
|
|
|
|
}
|
2022-01-03 02:41:24 +08:00
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
/* 创建新的流程图 */
|
|
|
|
|
async createNewDiagram(xml) {
|
|
|
|
|
// 将字符串转换成图显示出来
|
|
|
|
|
let newId = `Process_${new Date().getTime()}`;
|
|
|
|
|
let newName = `业务流程_${new Date().getTime()}`;
|
|
|
|
|
let xmlString = xml || DefaultEmptyXML(newId, newName, this.prefix);
|
|
|
|
|
try {
|
2022-01-18 01:41:22 +08:00
|
|
|
|
// console.log(this.bpmnModeler.importXML);
|
2022-01-03 02:41:24 +08:00
|
|
|
|
let { warnings } = await this.bpmnModeler.importXML(xmlString);
|
|
|
|
|
if (warnings && warnings.length) {
|
|
|
|
|
warnings.forEach(warn => console.warn(warn));
|
|
|
|
|
}
|
2022-01-18 01:41:22 +08:00
|
|
|
|
// 高亮流程图
|
|
|
|
|
await this.highlightDiagram();
|
2022-01-03 02:41:24 +08:00
|
|
|
|
} catch (e) {
|
2022-01-18 01:41:22 +08:00
|
|
|
|
console.error(e);
|
|
|
|
|
// console.error(`[Process Designer Warn]: ${e?.message || e}`);
|
2022-01-03 02:41:24 +08:00
|
|
|
|
}
|
2022-01-18 01:41:22 +08:00
|
|
|
|
},
|
|
|
|
|
/* 高亮流程图 */
|
|
|
|
|
async highlightDiagram() {
|
2022-01-19 12:45:12 +08:00
|
|
|
|
let activityList = this.tasks;
|
|
|
|
|
if (activityList.length === 0) {
|
2022-01-18 01:41:22 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-01-18 08:15:43 +08:00
|
|
|
|
// 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
|
2022-01-19 12:45:12 +08:00
|
|
|
|
// 再次基础上,增加不同审批结果的颜色等等
|
2022-01-18 01:41:22 +08:00
|
|
|
|
let canvas = this.bpmnModeler.get('canvas');
|
2022-01-19 12:45:12 +08:00
|
|
|
|
let todoActivity = activityList.find(m => !m.endTime) // 找到待办的任务
|
|
|
|
|
let endActivity = activityList[activityList.length - 1] // 找到结束任务
|
2022-01-18 08:15:43 +08:00
|
|
|
|
this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
|
2022-01-19 12:45:12 +08:00
|
|
|
|
let activity = activityList.find(m => m.key === n.id) // 找到对应的活动
|
2022-01-18 23:48:44 +08:00
|
|
|
|
if (n.$type === 'bpmn:UserTask') { // 用户任务
|
2022-01-19 12:45:12 +08:00
|
|
|
|
if (!activity) {
|
|
|
|
|
return;
|
2022-01-18 01:41:22 +08:00
|
|
|
|
}
|
2022-01-19 12:45:12 +08:00
|
|
|
|
if (activity.task) {
|
|
|
|
|
const result = activity.task.result;
|
|
|
|
|
if (result === 1) {
|
|
|
|
|
canvas.addMarker(n.id, 'highlight-todo');
|
|
|
|
|
} else if (result === 2) {
|
|
|
|
|
canvas.addMarker(n.id, 'highlight');
|
|
|
|
|
} else if (result === 3) {
|
|
|
|
|
canvas.addMarker(n.id, 'highlight-reject');
|
|
|
|
|
} else if (result === 4) {
|
|
|
|
|
canvas.addMarker(n.id, 'highlight-cancel');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n.outgoing?.forEach(nn => {
|
|
|
|
|
let targetTask = activityList.find(m => m.key === nn.targetRef.id)
|
|
|
|
|
if (targetTask) {
|
|
|
|
|
canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
|
|
|
|
|
} else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
|
|
|
|
|
// canvas.addMarker(nn.id, 'highlight');
|
|
|
|
|
canvas.addMarker(nn.id, activity.endTime ? 'highlight' : 'highlight-todo');
|
|
|
|
|
canvas.addMarker(nn.targetRef.id, activity.endTime ? 'highlight' : 'highlight-todo');
|
|
|
|
|
} else if (nn.targetRef.$type === 'bpmn:EndEvent') {
|
|
|
|
|
if (!todoActivity && endActivity.key === n.id) {
|
|
|
|
|
canvas.addMarker(nn.id, 'highlight');
|
|
|
|
|
canvas.addMarker(nn.targetRef.id, 'highlight');
|
|
|
|
|
}
|
|
|
|
|
if (!activity.endTime) {
|
|
|
|
|
canvas.addMarker(nn.id, 'highlight-todo');
|
|
|
|
|
canvas.addMarker(nn.targetRef.id, 'highlight-todo');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-18 23:48:44 +08:00
|
|
|
|
} else if (n.$type === 'bpmn:ExclusiveGateway') { // 排它网关
|
2022-01-18 08:15:43 +08:00
|
|
|
|
n.outgoing?.forEach(nn => {
|
2022-01-19 12:45:12 +08:00
|
|
|
|
let targetTask = activityList.find(m => m.key === nn.targetRef.id)
|
2022-01-18 08:15:43 +08:00
|
|
|
|
if (targetTask) {
|
|
|
|
|
canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
|
|
|
|
|
}
|
|
|
|
|
})
|
2022-01-18 23:48:44 +08:00
|
|
|
|
} else if (n.$type === 'bpmn:ParallelGateway') { // 并行网关
|
2022-01-19 12:45:12 +08:00
|
|
|
|
if (activity) {
|
|
|
|
|
canvas.addMarker(n.id, activity.endTime ? 'highlight' : 'highlight-todo')
|
2022-01-18 08:15:43 +08:00
|
|
|
|
n.outgoing?.forEach(nn => {
|
2022-01-19 13:39:12 +08:00
|
|
|
|
const targetTask = activityList.find(m => m.key === nn.targetRef.id)
|
2022-01-18 08:15:43 +08:00
|
|
|
|
if (targetTask) {
|
|
|
|
|
canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo')
|
|
|
|
|
canvas.addMarker(nn.targetRef.id, targetTask.endTime ? 'highlight' : 'highlight-todo')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-01-18 23:48:44 +08:00
|
|
|
|
} else if (n.$type === 'bpmn:StartEvent') { // 开始节点
|
2022-01-19 12:45:12 +08:00
|
|
|
|
n.outgoing?.forEach(nn => { // outgoing 例如说【bpmn:SequenceFlow】连线
|
|
|
|
|
let fromTask = activityList.find(m => m.key === nn.targetRef.id)
|
|
|
|
|
if (fromTask) {
|
2022-01-18 08:15:43 +08:00
|
|
|
|
canvas.addMarker(nn.id, 'highlight');
|
|
|
|
|
canvas.addMarker(n.id, 'highlight');
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-18 23:48:44 +08:00
|
|
|
|
} else if (n.$type === 'bpmn:EndEvent') { // 结束节点
|
2022-01-19 12:45:12 +08:00
|
|
|
|
if (endActivity.key !== n.id) { // 保证 endActivity 就是 EndEvent
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 在并行网关后,跟着多个任务,如果其中一个任务完成,endActivity 的 endTime 就会存在值
|
|
|
|
|
// 所以,通过 todoActivity 在做一次判断
|
|
|
|
|
if (endActivity.endTime && !todoActivity) {
|
|
|
|
|
canvas.addMarker(n.id, 'highlight');
|
2022-01-18 08:15:43 +08:00
|
|
|
|
}
|
2022-01-18 01:41:22 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
2022-01-19 13:39:12 +08:00
|
|
|
|
},
|
|
|
|
|
initModelListeners() {
|
|
|
|
|
const EventBus = this.bpmnModeler.get("eventBus");
|
|
|
|
|
const that = this;
|
|
|
|
|
// 注册需要的监听事件, 将. 替换为 - , 避免解析异常
|
|
|
|
|
EventBus.on('element.hover', function(eventObj) {
|
|
|
|
|
let element = eventObj ? eventObj.element : null;
|
|
|
|
|
that.elementHover(element);
|
|
|
|
|
});
|
|
|
|
|
EventBus.on('element.out', function(eventObj) {
|
|
|
|
|
let element = eventObj ? eventObj.element : null;
|
|
|
|
|
that.elementOut(element);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
// 流程图的元素被 hover
|
|
|
|
|
elementHover(element) {
|
|
|
|
|
this.element = element;
|
|
|
|
|
!this.elementOverlayIds && (this.elementOverlayIds = {});
|
|
|
|
|
!this.overlays && (this.overlays = this.bpmnModeler.get("overlays"));
|
|
|
|
|
// 展示信息
|
|
|
|
|
if (!this.elementOverlayIds[element.id] && element.type !== "bpmn:Process") {
|
|
|
|
|
this.elementOverlayIds[element.id] = this.overlays.add(element, {
|
|
|
|
|
position: { left: 0, bottom: 0 },
|
|
|
|
|
html: `<div class="element-overlays">
|
|
|
|
|
<p>Elemet id: ${element.id}</p>
|
|
|
|
|
<p>Elemet type: ${element.type}</p>
|
|
|
|
|
</div>`
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 流程图的元素被 out
|
|
|
|
|
elementOut(element) {
|
|
|
|
|
this.overlays.remove({ element });
|
|
|
|
|
this.elementOverlayIds[element.id] = null;
|
|
|
|
|
},
|
2022-01-03 02:41:24 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
2022-01-18 01:41:22 +08:00
|
|
|
|
|
|
|
|
|
<style>
|
2022-01-18 08:15:43 +08:00
|
|
|
|
|
2022-01-19 12:45:12 +08:00
|
|
|
|
/** 通过 */
|
2022-01-18 08:15:43 +08:00
|
|
|
|
.highlight.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: green !important;
|
|
|
|
|
stroke: green !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
2022-01-18 01:41:22 +08:00
|
|
|
|
}
|
2022-01-18 08:15:43 +08:00
|
|
|
|
.highlight.djs-shape .djs-visual > :nth-child(2) {
|
|
|
|
|
fill: green !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight.djs-shape .djs-visual > path {
|
|
|
|
|
fill: green !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
stroke: green !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: green !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: green !important; /* color elements as green */
|
|
|
|
|
}
|
2022-01-18 01:41:22 +08:00
|
|
|
|
|
|
|
|
|
/deep/.highlight.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: green !important;
|
|
|
|
|
stroke: green !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight.djs-shape .djs-visual > :nth-child(2) {
|
|
|
|
|
fill: green !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight.djs-shape .djs-visual > path {
|
|
|
|
|
fill: green !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
stroke: green !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: green !important;
|
|
|
|
|
}
|
2022-01-19 12:45:12 +08:00
|
|
|
|
|
|
|
|
|
/** 处理中 */
|
|
|
|
|
.highlight-todo.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: orange !important;
|
|
|
|
|
stroke-dasharray: 4px !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: orange !important;
|
|
|
|
|
stroke: orange !important;
|
|
|
|
|
stroke-dasharray: 4px !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-18 01:41:22 +08:00
|
|
|
|
/deep/.highlight-todo.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: orange !important;
|
|
|
|
|
stroke-dasharray: 4px !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
marker-end: url(#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr);
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: orange !important;
|
|
|
|
|
stroke: orange !important;
|
|
|
|
|
stroke-dasharray: 4px !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
2022-01-18 08:15:43 +08:00
|
|
|
|
|
2022-01-19 12:45:12 +08:00
|
|
|
|
/** 不通过 */
|
|
|
|
|
.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: red !important;
|
|
|
|
|
stroke: red !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
|
|
|
|
|
fill: red !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-reject.djs-shape .djs-visual > path {
|
|
|
|
|
fill: red !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
stroke: red !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-reject.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: red !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: red !important; /* color elements as green */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/deep/.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: red !important;
|
|
|
|
|
stroke: red !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
|
|
|
|
|
fill: red !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-reject.djs-shape .djs-visual > path {
|
|
|
|
|
fill: red !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
stroke: red !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-reject.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: red !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 已取消 */
|
|
|
|
|
.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: grey !important;
|
|
|
|
|
stroke: grey !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
|
|
|
|
|
fill: grey !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-cancel.djs-shape .djs-visual > path {
|
|
|
|
|
fill: grey !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
stroke: grey !important;
|
|
|
|
|
}
|
|
|
|
|
.highlight-cancel.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: grey !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.highlight-cancel:not(.djs-connection) .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: grey !important; /* color elements as green */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/deep/.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
|
|
|
|
|
fill: grey !important;
|
|
|
|
|
stroke: grey !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
|
|
|
|
|
fill: grey !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-cancel.djs-shape .djs-visual > path {
|
|
|
|
|
fill: grey !important;
|
|
|
|
|
fill-opacity: 0.2 !important;
|
|
|
|
|
stroke: grey !important;
|
|
|
|
|
}
|
|
|
|
|
/deep/.highlight-cancel.djs-connection > .djs-visual > path {
|
|
|
|
|
stroke: grey !important;
|
|
|
|
|
}
|
2022-01-18 01:41:22 +08:00
|
|
|
|
</style>
|