| | |
| | | <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 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> |
| | | </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> |
| | | </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> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | |
| | | 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 type { Canvas, ModdleElement } from 'bpmn'; |
| | | 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 loading = ref(false); |
| | | const bpmnVisible = ref(true); |
| | | const historyList = ref([]); |
| | | const init = (instanceId) => { |
| | | |
| | | const init = (businessKey) => { |
| | | loading.value = true; |
| | | bpmnVisible.value = true; |
| | | nextTick(() => { |
| | | nextTick(async () => { |
| | | if (modeler.value) modeler.value.destroy(); |
| | | modeler.value = new BpmnViewer({ |
| | | container: canvas.value, |
| | |
| | | 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); |
| | | const resp = await processApi.getHistoryList(businessKey); |
| | | xml.value = resp.data.xml; |
| | | taskList.value = resp.data.taskList; |
| | | historyList.value = resp.data.historyList; |
| | | await createDiagram(xml.value); |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const initXml = (xmlStr: string) => { |
| | | loading.value = true; |
| | | bpmnVisible.value = true; |
| | | nextTick(async () => { |
| | | if (modeler.value) modeler.value.destroy(); |
| | | modeler.value = new BpmnViewer({ |
| | | container: canvas.value, |
| | | additionalModules: [ |
| | | { |
| | | //禁止滚轮滚动 |
| | | zoomScroll: ['value', ''] |
| | | }, |
| | | ZoomScrollModule, |
| | | MoveCanvasModule |
| | | ] as ModuleDeclaration[] |
| | | }); |
| | | createDiagram(defaultXML); |
| | | xml.value = xmlStr; |
| | | await createDiagram(xml.value); |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | |
| | | loading.value = false; |
| | | addEventBusListener(); |
| | | } catch (err) { |
| | | //console.error(err.message, err.warnings) |
| | | console.log(err); |
| | | } |
| | | }; |
| | | const addEventBusListener = () => { |
| | |
| | | <p>开始时间:${data.startTime || ''}</p> |
| | | <p>结束时间:${data.endTime || ''}</p> |
| | | <p>审批耗时:${data.runDuration || ''}</p> |
| | | <p>流程版本:v${data.version || ''}</p> |
| | | </div>` |
| | | }); |
| | | }; |
| | |
| | | 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); |
| | | gateway(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); |
| | | gateway(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); |
| | | gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed); |
| | | }); |
| | | } |
| | | }); |
| | |
| | | } |
| | | 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; |
| | | } |
| | | }); |
| | | canvas.addMarker(n.id, 'startEvent'); |
| | | if (n.outgoing) { |
| | | 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'); |
| | | } |
| | | }); |
| | | } |
| | | } else if (n.$type === 'bpmn:EndEvent') { |
| | | canvas.addMarker(n.id, 'endEvent'); |
| | | const completeTask = taskList.value.find((m) => m.key === n.id); |
| | | if (completeTask) { |
| | | canvas.addMarker(completeTask.key, 'highlight'); |
| | |
| | | } |
| | | }); |
| | | }; |
| | | const getway = (id, targetRefType, targetRefId, canvas, completed) => { |
| | | const gateway = (id, targetRefType, targetRefId, canvas, completed) => { |
| | | if (targetRefType === 'bpmn:ExclusiveGateway') { |
| | | canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo'); |
| | | canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo'); |
| | |
| | | canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo'); |
| | | } |
| | | }; |
| | | defineExpose({ |
| | | init, |
| | | initXml |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | <style lang="scss" scoped> |
| | | .canvas { |
| | | width: 100%; |
| | | height: 100%; |
| | |
| | | font-size: 12px; |
| | | } |
| | | .un-complete { |
| | | border: 1px dashed #000; |
| | | border: 1px solid #000; |
| | | } |
| | | .in-progress { |
| | | background-color: rgb(255, 237, 204); |
| | |
| | | } |
| | | .complete { |
| | | background-color: rgb(204, 230, 204); |
| | | border: 1px dashed green; |
| | | border: 1px solid green; |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | .bpmn-el-container { |
| | | height: 500px; |
| | | height: calc(100vh - 350px); |
| | | } |
| | | .flow-containers { |
| | | width: 100%; |
| | |
| | | .load { |
| | | margin-right: 10px; |
| | | } |
| | | .el-form-item__label { |
| | | :deep(.el-form-item__label) { |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .djs-palette { |
| | | :deep(.djs-palette) { |
| | | left: 0 !important; |
| | | top: 0; |
| | | border-top: none; |
| | | } |
| | | |
| | | .djs-container svg { |
| | | :deep(.djs-container svg) { |
| | | min-height: 650px; |
| | | } |
| | | |
| | | .highlight.djs-shape .djs-visual > :nth-child(1) { |
| | | :deep(.startEvent.djs-shape .djs-visual > :nth-child(1)) { |
| | | fill: #77df6d !important; |
| | | } |
| | | :deep(.endEvent.djs-shape .djs-visual > :nth-child(1)) { |
| | | fill: #ee7b77 !important; |
| | | } |
| | | :deep(.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) { |
| | | :deep(.highlight.djs-shape .djs-visual > :nth-child(2)) { |
| | | fill: green !important; |
| | | } |
| | | .highlight.djs-shape .djs-visual > path { |
| | | :deep(.highlight.djs-shape .djs-visual > path) { |
| | | fill: green !important; |
| | | fill-opacity: 0.2 !important; |
| | | stroke: green !important; |
| | | } |
| | | .highlight.djs-connection > .djs-visual > path { |
| | | :deep(.highlight.djs-connection > .djs-visual > path) { |
| | | stroke: green !important; |
| | | } |
| | | .highlight-todo.djs-connection > .djs-visual > path { |
| | | stroke: orange !important; |
| | | |
| | | // 边框滚动动画 |
| | | @keyframes path-animation { |
| | | from { |
| | | stroke-dashoffset: 100%; |
| | | } |
| | | |
| | | to { |
| | | stroke-dashoffset: 0%; |
| | | } |
| | | } |
| | | |
| | | :deep(.highlight-todo.djs-connection > .djs-visual > path) { |
| | | animation: path-animation 60s; |
| | | animation-timing-function: linear; |
| | | animation-iteration-count: infinite; |
| | | stroke-dasharray: 4px !important; |
| | | stroke: orange !important; |
| | | fill-opacity: 0.2 !important; |
| | | marker-end: url(#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr); |
| | | marker-end: url('#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr'); |
| | | } |
| | | .highlight-todo.djs-shape .djs-visual > :nth-child(1) { |
| | | |
| | | :deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) { |
| | | animation: path-animation 60s; |
| | | animation-timing-function: linear; |
| | | animation-iteration-count: infinite; |
| | | stroke-dasharray: 4px !important; |
| | | stroke: orange !important; |
| | | fill: orange !important; |
| | | stroke: orange !important; |
| | | stroke-dasharray: 4px !important; |
| | | fill-opacity: 0.2 !important; |
| | | } |
| | | } |
| | | .verlays { |
| | | :deep(.verlays) { |
| | | width: 250px; |
| | | background: rgb(102, 102, 102); |
| | | border-radius: 4px; |
| | | border: 1px solid #ebeef5; |
| | | //padding: 12px; |
| | | color: #fff; |
| | | padding: 15px 10px; |
| | | p { |
| | |
| | | margin: 0; |
| | | padding: 0; |
| | | } |
| | | cursor: pointer; |
| | | } |
| | | </style> |