From 97b4eef342effb892622d2834658dbf489254590 Mon Sep 17 00:00:00 2001 From: LiuHao <liuhaoai545@gmail.com> Date: 星期五, 08 三月 2024 16:02:08 +0800 Subject: [PATCH] update 使用bpmnjs流程预览 --- src/components/BpmnView/index.vue | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 361 insertions(+), 0 deletions(-) diff --git a/src/components/BpmnView/index.vue b/src/components/BpmnView/index.vue new file mode 100644 index 0000000..ee0eaa6 --- /dev/null +++ b/src/components/BpmnView/index.vue @@ -0,0 +1,361 @@ +<template> + <el-dialog v-if="bpmnVisible" v-model="bpmnVisible" title="娴佺▼杩涘害" append-to-body width="90%" @opened="init(undefined)"> + <div v-loading="loading" class="bpmnDialogContainers"> + <el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto"> + <div class="header-div"> + <div> + <el-tooltip effect="dark" content="鑷�傚簲灞忓箷" placement="bottom"> + <el-button size="small" icon="Rank" @click="fitViewport" /> + </el-tooltip> + <el-tooltip effect="dark" content="鏀惧ぇ" placement="bottom"> + <el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" /> + </el-tooltip> + <el-tooltip effect="dark" content="缂╁皬" placement="bottom"> + <el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" /> + </el-tooltip> + </div> + <div> + <div class="tips-label"> + <div class="un-complete">鏈畬鎴�</div> + <div class="in-progress">杩涜涓�</div> + <div class="complete">宸插畬鎴�</div> + </div> + </div> + </div> + </el-header> + <div class="flow-containers"> + <el-container class="bpmn-el-container" style="align-items: stretch"> + <el-main style="padding: 0"> + <div ref="canvas" class="canvas" /> + </el-main> + </el-container> + </div> + </div> + </el-dialog> +</template> + +<script lang="ts" setup> +import BpmnViewer from 'bpmn-js/lib/Viewer'; +import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'; +import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll'; +import { ModuleDeclaration } from 'didi'; +import { Canvas, ModdleElement } from 'bpmn'; +import defaultXML from '@/components/BpmnDesign/assets/defaultXML'; +import EventBus from 'diagram-js/lib/core/EventBus'; +import Overlays from 'diagram-js/lib/features/overlays/Overlays'; +import processApi from '@/api/workflow/processInstance/index'; + +const canvas = ref<HTMLElement>(); +const modeler = ref<BpmnViewer>(); +const taskList = ref([]); +const zoom = ref(1); +const xml = ref(''); +const loading = ref(false); +const bpmnVisible = ref(true); +const historyList = ref([]); +const init = (instanceId) => { + loading.value = true; + bpmnVisible.value = true; + nextTick(() => { + if (modeler.value) modeler.value.destroy(); + modeler.value = new BpmnViewer({ + container: canvas.value, + additionalModules: [ + { + //绂佹婊氳疆婊氬姩 + zoomScroll: ['value', ''] + }, + ZoomScrollModule, + MoveCanvasModule + ] as ModuleDeclaration[] + }); + processApi.getHistoryList(instanceId).then((resp) => { + xml.value = resp.data.xml; + taskList.value = resp.data.taskList; + historyList.value = resp.data.historyList; + // createDiagram(xml); + console.log(resp); + }); + createDiagram(defaultXML); + loading.value = false; + }); +}; + +const createDiagram = async (data) => { + try { + await modeler.value.importXML(data); + fitViewport(); + fillColor(); + loading.value = false; + addEventBusListener(); + } catch (err) { + //console.error(err.message, err.warnings) + } +}; +const addEventBusListener = () => { + const eventBus = modeler.value.get<EventBus>('eventBus'); + const overlays = modeler.value.get<Overlays>('overlays'); + eventBus.on<ModdleElement>('element.hover', (e) => { + let data = historyList.value.find((t) => t.taskDefinitionKey === e.element.id); + if (e.element.type === 'bpmn:UserTask' && data) { + setTimeout(() => { + genNodeDetailBox(e, overlays, data); + }, 10); + } + }); + eventBus.on('element.out', (e) => { + overlays.clear(); + }); +}; +const genNodeDetailBox = (e, overlays, data) => { + overlays.add(e.element.id, { + position: { top: e.element.height, left: 0 }, + html: `<div class="verlays"> + <p>瀹℃壒浜哄憳: ${data.nickName || ''}<p/> + <p>鑺傜偣鐘舵�侊細${data.status || ''}</p> + <p>寮�濮嬫椂闂达細${data.startTime || ''}</p> + <p>缁撴潫鏃堕棿锛�${data.endTime || ''}</p> + <p>瀹℃壒鑰楁椂锛�${data.runDuration || ''}</p> + </div>` + }); +}; +// 璁╁浘鑳借嚜閫傚簲灞忓箷 +const fitViewport = () => { + zoom.value = modeler.value.get<Canvas>('canvas').zoom('fit-viewport'); + const bbox = document.querySelector<SVGGElement>('.flow-containers .viewport').getBBox(); + const currentViewBox = modeler.value.get('canvas').viewbox(); + const elementMid = { + x: bbox.x + bbox.width / 2 - 65, + y: bbox.y + bbox.height / 2 + }; + modeler.value.get<Canvas>('canvas').viewbox({ + x: elementMid.x - currentViewBox.width / 2, + y: elementMid.y - currentViewBox.height / 2, + width: currentViewBox.width, + height: currentViewBox.height + }); + zoom.value = (bbox.width / currentViewBox.width) * 1.8; +}; +// 鏀惧ぇ缂╁皬 +const zoomViewport = (zoomIn = true) => { + zoom.value = modeler.value.get<Canvas>('canvas').zoom(); + zoom.value += zoomIn ? 0.1 : -0.1; + modeler.value.get<Canvas>('canvas').zoom(zoom.value); +}; +//涓婅壊 +const fillColor = () => { + const canvas = modeler.value.get<Canvas>('canvas'); + bpmnNodeList(modeler.value._definitions.rootElements[0].flowElements, canvas); +}; +//閫掑綊涓婅壊 +const bpmnNodeList = (flowElements, canvas) => { + flowElements.forEach((n) => { + if (n.$type === 'bpmn:UserTask') { + const completeTask = taskList.value.find((m) => m.key === n.id); + if (completeTask) { + canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + n.outgoing?.forEach((nn) => { + const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id); + if (targetTask) { + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo'); + } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') { + canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + nn.targetRef.outgoing.forEach((e) => { + getway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed); + }); + } else if (nn.targetRef.$type === 'bpmn:ParallelGateway') { + canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + nn.targetRef.outgoing.forEach((e) => { + getway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed); + }); + } else if (nn.targetRef.$type === 'bpmn:InclusiveGateway') { + canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + nn.targetRef.outgoing.forEach((e) => { + getway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed); + }); + } + }); + } + } else if (n.$type === 'bpmn:ExclusiveGateway') { + n.outgoing.forEach((nn) => { + const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id); + if (targetTask) { + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo'); + } + }); + } else if (n.$type === 'bpmn:ParallelGateway') { + n.outgoing.forEach((nn) => { + const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id); + if (targetTask) { + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo'); + } + }); + } else if (n.$type === 'bpmn:InclusiveGateway') { + n.outgoing.forEach((nn) => { + const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id); + if (targetTask) { + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo'); + } + }); + } else if (n.$type === 'bpmn:SubProcess') { + const completeTask = taskList.value.find((m) => m.key === n.id); + if (completeTask) { + canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo'); + } + bpmnNodeList(n.flowElements, canvas); + } else if (n.$type === 'bpmn:StartEvent') { + n.outgoing.forEach((nn) => { + const completeTask = taskList.value.find((m) => m.key === nn.targetRef.id); + if (completeTask) { + canvas.addMarker(nn.id, 'highlight'); + canvas.addMarker(n.id, 'highlight'); + return; + } + }); + } else if (n.$type === 'bpmn:EndEvent') { + const completeTask = taskList.value.find((m) => m.key === n.id); + if (completeTask) { + canvas.addMarker(completeTask.key, 'highlight'); + canvas.addMarker(n.id, 'highlight'); + return; + } + } + }); +}; +const getway = (id, targetRefType, targetRefId, canvas, completed) => { + if (targetRefType === 'bpmn:ExclusiveGateway') { + canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo'); + canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo'); + } + if (targetRefType === 'bpmn:ParallelGateway') { + canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo'); + canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo'); + } + if (targetRefType === 'bpmn:InclusiveGateway') { + canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo'); + canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo'); + } +}; +</script> + +<style lang="scss"> +.canvas { + width: 100%; + height: 100%; +} + +.header-div { + display: flex; + padding: 10px 0; + justify-content: space-between; + + .tips-label { + display: flex; + div { + margin-right: 10px; + padding: 5px; + font-size: 12px; + } + .un-complete { + border: 1px dashed #000; + } + .in-progress { + background-color: rgb(255, 237, 204); + border: 1px dashed orange; + } + .complete { + background-color: rgb(204, 230, 204); + border: 1px dashed green; + } + } +} + +.view-mode { + .el-header, + .el-aside, + .djs-palette, + .bjs-powered-by { + display: none; + } + .el-loading-mask { + background-color: initial; + } + .el-loading-spinner { + display: none; + } +} +.bpmn-el-container { + height: 500px; +} +.flow-containers { + width: 100%; + height: 100%; + overflow-y: auto; + .canvas { + width: 100%; + height: 100%; + } + .load { + margin-right: 10px; + } + .el-form-item__label { + font-size: 13px; + } + + .djs-palette { + left: 0 !important; + top: 0; + border-top: none; + } + + .djs-container svg { + min-height: 650px; + } + + .highlight.djs-shape .djs-visual > :nth-child(1) { + fill: green !important; + stroke: green !important; + fill-opacity: 0.2 !important; + } + .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-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); + } + .highlight-todo.djs-shape .djs-visual > :nth-child(1) { + fill: orange !important; + stroke: orange !important; + stroke-dasharray: 4px !important; + fill-opacity: 0.2 !important; + } +} +.verlays { + width: 250px; + background: rgb(102, 102, 102); + border-radius: 4px; + border: 1px solid #ebeef5; + //padding: 12px; + color: #fff; + padding: 15px 10px; + p { + line-height: 28px; + margin: 0; + padding: 0; + } +} +</style> -- Gitblit v1.9.3