Files
supervision-test-frontend-repo/src/views/super/bpm/process/components/BpmGraphic.vue
T
2026-04-09 15:09:34 +08:00

293 lines
9.1 KiB
Vue

<template>
<div>
<div class="jee-bpm-graphic-containers">
<div class="jee-bpm-graphic-canvas" :id="containerId"></div>
</div>
<bpm-node-info-modal @register="registerModal" @notify="handleModalVisible"></bpm-node-info-modal>
</div>
</template>
<script>
import { watch, ref, onMounted } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import BpmNodeInfoModal from './BpmNodeInfoModal.vue';
import { useModal } from '/@/components/Modal';
import inherits from 'inherits';
import Viewer from 'bpmn-js/lib/Viewer';
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
import { append as svgAppend, attr as svgAttr, create as svgCreate } from 'tiny-svg';
import { query as domQuery } from 'min-dom';
function CustomViewer(options) {
Viewer.call(this, options);
}
inherits(CustomViewer, Viewer);
CustomViewer.prototype._modules = [].concat(Viewer.prototype._modules, [ZoomScrollModule, MoveCanvasModule]);
export default {
name: 'BpmGraphic',
props: {
// 流程实例ID
instanceId: {
type: String,
required: false,
},
center:{
type: Boolean,
default: false,
required: false
}
},
components: {
BpmNodeInfoModal,
},
emits: ['task'],
setup(props, { emit }) {
const url = {
getProcessInfo: '/act/designer/api/getProcessXmlByInstanceId',
getInstanceInfo: '/act/task/getFlowMsgByProcInstId',
getNodePositionInfo: '/act/task/getNodePositionInfo',
};
const [registerModal, { openModal, closeModal }] = useModal();
const containerId = 'jee-bpm-graphic-canvas';
let bpmViewer = null;
onMounted(() => {
newViewer();
});
let taskList = [];
let currentTaskId = '';
let currentNodeList = [];
let historyNodeList = [];
let historyLineList = [];
let delayHandler = '';
watch(
() => props.instanceId,
(val) => {
if (val) {
init();
}
},
{ immediate: true }
);
async function init() {
let params = { processInstanceId: props.instanceId };
// 1.加载流程设计xml
let xml = await defHttp.get({ url: url.getProcessInfo, params }, { isTransformResponse: false });
//console.log('xml', xml);
// 2.加载流程实例信息
let instanceInfo = await defHttp.get({ url: url.getInstanceInfo, params }, { isTransformResponse: false });
//console.log('instanceInfo', instanceInfo);
// 2.加载节点信息 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
let nodeInfo = await defHttp.get({ url: url.getNodePositionInfo, params }, { isTransformResponse: false });
//console.log('nodeInfo', nodeInfo);
if (nodeInfo.success) {
taskList = nodeInfo.result.hisTasks;
emit('task', taskList);
}
// console.log('taskList', nodeInfo.result)
try {
// 3.解析流程实例信息
if (instanceInfo.success) {
historyNodeList = instanceInfo.result.highLightedActivitiIdList;
currentNodeList = instanceInfo.result.runningActivitiIdList;
historyLineList = instanceInfo.result.highLightedFlowIds;
}
// 4.绘制流程
newViewer();
const result = await bpmViewer.importXML(xml);
const { warnings } = result;
console.log('bpm graphic warnings', warnings);
// 5.调整图片位置
const canvas = bpmViewer.get('canvas');
if(props.center == true){
canvas.zoom('fit-viewport', true);
}
// 6.创建箭头标记
createArrow();
// 7.设置节点、线的颜色
setColor();
// 8.节点事件
addEvent();
} catch (err) {
console.log(err.message, err.warnings);
}
}
function setColor() {
// access viewer components
const canvas = bpmViewer.get('canvas');
// 获取到全部节点
const allShapes = bpmViewer.get('elementRegistry').getAll();
//循环节点添加class
allShapes.forEach((element) => {
const shapeId = element.businessObject.id;
// const shapeAttrs = element.businessObject.$attrs
//console.info('123element', element)
let type = element.type;
if (type == 'bpmn:ExclusiveGateway' || type == 'bpmn:InclusiveGateway' || type == 'bpmn:ParallelGateway') {
canvas.addMarker(shapeId, 'jee-bpm-gateway');
}
// add marker
if (element.businessObject.$type != 'bpmn:Group') {
if (element.businessObject.$type == 'bpmn:SequenceFlow') {
if (historyLineList.includes(shapeId)) {
canvas.addMarker(shapeId, 'jee-bpm-history-line');
}
} else {
if (historyNodeList.includes(shapeId) && !currentNodeList.includes(shapeId)) {
canvas.addMarker(shapeId, 'jee-bpm-history-node');
}
if (currentNodeList.includes(shapeId)) {
canvas.addMarker(shapeId, 'jee-bpm-current-node');
}
}
}
});
}
// 自定义箭头标记-默认箭头是黑色的
function createArrow() {
const marker = svgCreate('marker');
svgAttr(marker, {
id: 'active-arrow',
viewBox: '0 0 20 20',
refX: '11',
refY: '10',
markerWidth: '10',
markerHeight: '10',
orient: 'auto',
fill: '#408af1',
});
const path = svgCreate('path');
svgAttr(path, {
d: 'M 1 5 L 11 10 L 1 15 Z',
style: 'stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;',
});
const defs = domQuery('defs');
svgAppend(marker, path);
svgAppend(defs, marker);
}
//添加节点事件
function addEvent() {
const eventBus = bpmViewer.get('eventBus');
eventBus.on('element.hover', (e) => {
const { element } = e;
if (!element.parent) {
// 这里关闭modal
delayClose();
currentTaskId = '';
//console.log('鼠标移至空白处', element);
return;
}
if (!e || element.type === 'bpmn:Process') {
return false;
} else {
let temp = element.id;
let type = element.type;
if (currentTaskId != temp && 'bpmn:UserTask' == type && historyNodeList.indexOf(temp) >= 0) {
/**
* 满足3个条件才弹框显示节点信息
* 1.当前节点不是鼠标选中的节点,防止多次调用
* 2.必须是任务节点
* 3.必须是处理过的节点
*/
currentTaskId = temp;
//console.log('准备开启modal', e);
showNodeInfo();
}
}
});
}
function showNodeInfo() {
closeModal();
openModal(true, {
dataList: taskList,
taskId: currentTaskId,
});
}
function handleModalVisible(flag) {
//console.log('handleModalVisible', flag)
if (flag == true) {
clearTimeout(delayHandler);
}
}
function delayClose() {
//console.log('delayClose')
delayHandler = setTimeout(() => {
//console.log('准备关闭modal');
if (currentTaskId) {
showNodeInfo();
} else {
closeModal();
}
}, 500);
}
function newViewer() {
if (bpmViewer == null) {
let dom = document.getElementById(containerId);
bpmViewer = new CustomViewer({
container: dom,
});
}
}
return {
registerModal,
containerId,
handleModalVisible,
};
},
};
</script>
<style lang="less">
.jee-bpm-graphic-containers {
width: 100%;
height: calc(100vh - 250px);
}
.jee-bpm-graphic-canvas {
width: 100%;
height: 100%;
}
.jee-bpm-graphic-canvas .bjs-powered-by {
display: none;
}
/**网关样式*/
.jee-bpm-gateway .djs-visual path {
stroke: none !important;
}
/**走过的分支线样式 */
.jee-bpm-history-line .djs-visual > :nth-child(1) {
stroke: #408af1 !important;
}
.jee-bpm-history-line path {
marker-end: url(#active-arrow) !important;
stroke-width: 2px!important;
}
/**走过的节点样式 */
.jee-bpm-history-node .djs-visual > :nth-child(1) {
fill: #51a2f13b !important;
stroke: #408af1 !important;
}
/**当前节点样式 */
.jee-bpm-current-node .djs-visual > :nth-child(1) {
fill: #f9ca6d !important;
stroke: #cd9423 !important;
}
/* .jee-bpm-current-node .djs-visual > text {
fill: #fff !important;
}*/
</style>