From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 379 insertions(+), 0 deletions(-)
diff --git a/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue b/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue
new file mode 100644
index 0000000..34a54c8
--- /dev/null
+++ b/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue
@@ -0,0 +1,379 @@
+<template>
+ <div class="process-viewer">
+ <div style="height: 100%" ref="processCanvas" v-show="!isLoading"> </div>
+ <!-- 鑷畾涔夌澶存牱寮忥紝鐢ㄤ簬宸插畬鎴愮姸鎬佷笅娴佺▼杩炵嚎绠ご -->
+ <defs ref="customDefs">
+ <marker
+ id="sequenceflow-end-white-success"
+ viewBox="0 0 20 20"
+ refX="11"
+ refY="10"
+ markerWidth="10"
+ markerHeight="10"
+ orient="auto"
+ >
+ <path
+ class="success-arrow"
+ d="M 1 5 L 11 10 L 1 15 Z"
+ style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1"
+ />
+ </marker>
+ <marker
+ id="conditional-flow-marker-white-success"
+ viewBox="0 0 20 20"
+ refX="-1"
+ refY="10"
+ markerWidth="10"
+ markerHeight="10"
+ orient="auto"
+ >
+ <path
+ class="success-conditional"
+ d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
+ style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1"
+ />
+ </marker>
+ </defs>
+
+ <!-- 瀹℃壒璁板綍 -->
+ <el-dialog :title="dialogTitle || '瀹℃壒璁板綍'" v-model="dialogVisible" width="1000px">
+ <el-row>
+ <el-table
+ :data="selectTasks"
+ size="small"
+ border
+ header-cell-class-name="table-header-gray"
+ >
+ <el-table-column
+ label="搴忓彿"
+ header-align="center"
+ align="center"
+ type="index"
+ width="50"
+ />
+ <el-table-column
+ label="瀹℃壒浜�"
+ min-width="100"
+ align="center"
+ v-if="selectActivityType === 'bpmn:UserTask'"
+ >
+ <template #default="scope">
+ {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="鍙戣捣浜�"
+ prop="assigneeUser.nickname"
+ min-width="100"
+ align="center"
+ v-else
+ />
+ <el-table-column label="閮ㄩ棬" min-width="100" align="center">
+ <template #default="scope">
+ {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
+ </template>
+ </el-table-column>
+ <el-table-column
+ :formatter="dateFormatter"
+ align="center"
+ label="寮�濮嬫椂闂�"
+ prop="createTime"
+ min-width="140"
+ />
+ <el-table-column
+ :formatter="dateFormatter"
+ align="center"
+ label="缁撴潫鏃堕棿"
+ prop="endTime"
+ min-width="140"
+ />
+ <el-table-column align="center" label="瀹℃壒鐘舵��" prop="status" min-width="90">
+ <template #default="scope">
+ <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
+ </template>
+ </el-table-column>
+ <el-table-column
+ align="center"
+ label="瀹℃壒寤鸿"
+ prop="reason"
+ min-width="120"
+ v-if="selectActivityType === 'bpmn:UserTask'"
+ />
+ <el-table-column align="center" label="鑰楁椂" prop="durationInMillis" width="100">
+ <template #default="scope">
+ {{ formatPast2(scope.row.durationInMillis) }}
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-row>
+ </el-dialog>
+
+ <!-- Zoom锛氭斁澶с�佺缉灏� -->
+ <div style="position: absolute; top: 0; left: 0; width: 100%">
+ <el-row type="flex" justify="end">
+ <el-button-group key="scale-control" size="default">
+ <el-button
+ size="default"
+ :plain="true"
+ :disabled="defaultZoom <= 0.3"
+ :icon="ZoomOut"
+ @click="processZoomOut()"
+ />
+ <el-button size="default" style="width: 90px">
+ {{ Math.floor(defaultZoom * 10 * 10) + '%' }}
+ </el-button>
+ <el-button
+ size="default"
+ :plain="true"
+ :disabled="defaultZoom >= 3.9"
+ :icon="ZoomIn"
+ @click="processZoomIn()"
+ />
+ <el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
+ </el-button-group>
+ </el-row>
+ </div>
+ </div>
+</template>
+
+<script lang="ts" setup>
+import '../theme/index.scss'
+import BpmnViewer from 'bpmn-js/lib/Viewer'
+import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'
+import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
+import { DICT_TYPE } from '@/utils/dict'
+import { dateFormatter, formatPast2 } from '@/utils/formatTime'
+import { BpmProcessInstanceStatus } from '@/utils/constants'
+
+const props = defineProps({
+ xml: {
+ type: String,
+ required: true
+ },
+ view: {
+ type: Object,
+ require: true
+ }
+})
+
+const processCanvas = ref()
+const bpmnViewer = ref<BpmnViewer | null>(null)
+const customDefs = ref()
+const defaultZoom = ref(1) // 榛樿缂╂斁姣斾緥
+const isLoading = ref(false) // 鏄惁鍔犺浇涓�
+
+const processInstance = ref<any>({}) // 娴佺▼瀹炰緥
+const tasks = ref([]) // 娴佺▼浠诲姟
+
+const dialogVisible = ref(false) // 寮圭獥鍙鎬�
+const dialogTitle = ref<string | undefined>(undefined) // 寮圭獥鏍囬
+const selectActivityType = ref<string | undefined>(undefined) // 閫変腑 Task 鐨勬椿鍔ㄧ紪鍙�
+const selectTasks = ref<any[]>([]) // 閫変腑鐨勪换鍔℃暟缁�
+
+/** Zoom锛氭仮澶� */
+const processReZoom = () => {
+ defaultZoom.value = 1
+ bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto')
+}
+
+/** Zoom锛氭斁澶� */
+const processZoomIn = (zoomStep = 0.1) => {
+ let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100
+ if (newZoom > 4) {
+ throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4')
+ }
+ defaultZoom.value = newZoom
+ bpmnViewer.value?.get('canvas').zoom(defaultZoom.value)
+}
+
+/** Zoom锛氱缉灏� */
+const processZoomOut = (zoomStep = 0.1) => {
+ let newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100
+ if (newZoom < 0.2) {
+ throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2')
+ }
+ defaultZoom.value = newZoom
+ bpmnViewer.value?.get('canvas').zoom(defaultZoom.value)
+}
+
+/** 娴佺▼鍥鹃瑙堟竻绌� */
+const clearViewer = () => {
+ if (processCanvas.value) {
+ processCanvas.value.innerHTML = ''
+ }
+ if (bpmnViewer.value) {
+ bpmnViewer.value.destroy()
+ }
+ bpmnViewer.value = null
+}
+
+/** 娣诲姞鑷畾涔夌澶� */
+// TODO 鑺嬭壙锛氳嚜瀹氫箟绠ご涓嶇敓鏁堬紝鏈夌偣濂囨�紒锛侊紒锛佺浉鍏崇殑 marker-end銆乵arker-start 鏆傛椂涔熸敞閲婁簡锛侊紒锛�
+const addCustomDefs = () => {
+ if (!bpmnViewer.value) {
+ return
+ }
+ const canvas = bpmnViewer.value?.get('canvas')
+ const svg = canvas?._svg
+ svg.appendChild(customDefs.value)
+}
+
+/** 鑺傜偣閫変腑 */
+const onSelectElement = (element: any) => {
+ // 娓呯┖鍘熼�変腑
+ selectActivityType.value = undefined
+ dialogTitle.value = undefined
+ if (!element || !processInstance.value?.id) {
+ return
+ }
+
+ // UserTask 鐨勬儏鍐�
+ const activityType = element.type
+ selectActivityType.value = activityType
+ if (activityType === 'bpmn:UserTask') {
+ dialogTitle.value = element.businessObject ? element.businessObject.name : undefined
+ selectTasks.value = tasks.value.filter((item: any) => item?.taskDefinitionKey === element.id)
+ dialogVisible.value = true
+ } else if (activityType === 'bpmn:EndEvent' || activityType === 'bpmn:StartEvent') {
+ dialogTitle.value = '瀹℃壒淇℃伅'
+ selectTasks.value = [
+ {
+ assigneeUser: processInstance.value.startUser,
+ createTime: processInstance.value.startTime,
+ endTime: processInstance.value.endTime,
+ status: processInstance.value.status,
+ durationInMillis: processInstance.value.durationInMillis
+ }
+ ]
+ dialogVisible.value = true
+ }
+}
+
+/** 鍒濆鍖� BPMN 瑙嗗浘 */
+const importXML = async (xml: string) => {
+ // 娓呯┖娴佺▼鍥�
+ clearViewer()
+
+ // 鍒濆鍖栨祦绋嬪浘
+ if (xml != null && xml !== '') {
+ try {
+ bpmnViewer.value = new BpmnViewer({
+ additionalModules: [MoveCanvasModule],
+ container: processCanvas.value
+ })
+ // 澧炲姞鐐瑰嚮浜嬩欢
+ bpmnViewer.value.on('element.click', ({ element }) => {
+ onSelectElement(element)
+ })
+
+ // 鍒濆鍖� BPMN 瑙嗗浘
+ isLoading.value = true
+ await bpmnViewer.value.importXML(xml)
+ // 鑷畾涔夋垚鍔熺殑绠ご
+ addCustomDefs()
+ } catch (e) {
+ clearViewer()
+ } finally {
+ isLoading.value = false
+ // 楂樹寒娴佺▼
+ setProcessStatus(props.view)
+ }
+ }
+}
+
+/** 楂樹寒娴佺▼ */
+const setProcessStatus = (view: any) => {
+ // 璁剧疆鐩稿叧鍙橀噺
+ if (!view || !view.processInstance) {
+ return
+ }
+ processInstance.value = view.processInstance
+ tasks.value = view.tasks
+ if (isLoading.value || !bpmnViewer.value) {
+ return
+ }
+ const {
+ unfinishedTaskActivityIds,
+ finishedTaskActivityIds,
+ finishedSequenceFlowActivityIds,
+ rejectedTaskActivityIds
+ } = view
+ const canvas = bpmnViewer.value.get('canvas')
+ const elementRegistry = bpmnViewer.value.get('elementRegistry')
+
+ // 宸插畬鎴愯妭鐐�
+ if (Array.isArray(finishedSequenceFlowActivityIds)) {
+ finishedSequenceFlowActivityIds.forEach((item: any) => {
+ if (item != null) {
+ canvas.addMarker(item, 'success')
+ const element = elementRegistry.get(item)
+ const conditionExpression = element.businessObject.conditionExpression
+ if (conditionExpression) {
+ canvas.addMarker(item, 'condition-expression')
+ }
+ }
+ })
+ }
+ if (Array.isArray(finishedTaskActivityIds)) {
+ finishedTaskActivityIds.forEach((item: any) => canvas.addMarker(item, 'success'))
+ }
+
+ // 鏈畬鎴愯妭鐐�
+ if (Array.isArray(unfinishedTaskActivityIds)) {
+ unfinishedTaskActivityIds.forEach((item: any) => canvas.addMarker(item, 'primary'))
+ }
+
+ // 琚嫆缁濊妭鐐�
+ if (Array.isArray(rejectedTaskActivityIds)) {
+ rejectedTaskActivityIds.forEach((item: any) => {
+ if (item != null) {
+ canvas.addMarker(item, 'danger')
+ }
+ })
+ }
+
+ // 鐗规畩锛氬鐞� end 鑺傜偣鐨勯珮浜�傚洜涓� end 鍦ㄦ嫆缁濄�佸彇娑堟椂锛岃鍚庣璁$畻鎴愪簡 finishedTaskActivityIds 閲�
+ if (
+ [BpmProcessInstanceStatus.CANCEL, BpmProcessInstanceStatus.REJECT].includes(
+ processInstance.value.status
+ )
+ ) {
+ const endNodes = elementRegistry.filter((element: any) => element.type === 'bpmn:EndEvent')
+ endNodes.forEach((item: any) => {
+ canvas.removeMarker(item.id, 'success')
+ if (processInstance.value.status === BpmProcessInstanceStatus.CANCEL) {
+ canvas.addMarker(item.id, 'cancel')
+ } else {
+ canvas.addMarker(item.id, 'danger')
+ }
+ })
+ }
+}
+
+watch(
+ () => props.xml,
+ (newXml) => {
+ importXML(newXml)
+ },
+ { immediate: true }
+)
+
+watch(
+ () => props.view,
+ (newView) => {
+ setProcessStatus(newView)
+ },
+ { immediate: true }
+)
+
+/** mounted锛氬垵濮嬪寲 */
+onMounted(() => {
+ importXML(props.xml)
+ setProcessStatus(props.view)
+})
+
+/** unmount锛氶攢姣� */
+onBeforeUnmount(() => {
+ clearViewer()
+})
+</script>
--
Gitblit v1.8.0