From 3e359e4550d018035406a97c190b98f83839ba7f Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期五, 13 十二月 2024 10:41:05 +0800 Subject: [PATCH] 1、工作流重做更新 2、@import修改为@use以应对sass升级弃用@import 3、进入系统后自动redirect到home页 --- src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue | 959 +++++++++++++++++++++-------------------------------------- 1 files changed, 337 insertions(+), 622 deletions(-) diff --git a/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue b/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue index 485b979..34a54c8 100644 --- a/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue +++ b/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue @@ -1,664 +1,379 @@ <template> - <div class="my-process-designer"> - <div class="my-process-designer__container"> - <div class="my-process-designer__canvas" style="height: 760px" ref="bpmnCanvas"></div> + <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 DefaultEmptyXML from './plugins/defaultEmpty' -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' -import { formatDate } from '@/utils/formatTime' -import { isEmpty } from '@/utils/is' - -defineOptions({ name: 'MyProcessViewer' }) +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({ - value: { - // BPMN XML 字符串 + xml: { type: String, - default: '' + required: true }, - prefix: { - // 使用哪个引擎 - type: String, - default: 'camunda' - }, - activityData: { - // 活动的数据。传递时,可高亮流程 - type: Array, - default: () => [] - }, - processInstanceData: { - // 流程实例的数据。传递时,可展示流程发起人等信息 + view: { type: Object, - default: () => {} - }, - taskData: { - // 任务实例的数据。传递时,可展示 UserTask 审核相关的信息 - type: Array, - default: () => [] + require: true } }) -provide('configGlobal', props) +const processCanvas = ref() +const bpmnViewer = ref<BpmnViewer | null>(null) +const customDefs = ref() +const defaultZoom = ref(1) // 默认缩放比例 +const isLoading = ref(false) // 是否加载中 -const emit = defineEmits(['destroy']) +const processInstance = ref<any>({}) // 流程实例 +const tasks = ref([]) // 流程任务 -let bpmnModeler +const dialogVisible = ref(false) // 弹窗可见性 +const dialogTitle = ref<string | undefined>(undefined) // 弹窗标题 +const selectActivityType = ref<string | undefined>(undefined) // 选中 Task 的活动编号 +const selectTasks = ref<any[]>([]) // 选中的任务数组 -const xml = ref('') -const activityLists = ref<any[]>([]) -const processInstance = ref<any>(undefined) -const taskList = ref<any[]>([]) -const bpmnCanvas = ref() -// const element = ref() -const elementOverlayIds = ref<any>(null) -const overlays = ref<any>(null) - -const initBpmnModeler = () => { - if (bpmnModeler) return - bpmnModeler = new BpmnViewer({ - container: bpmnCanvas.value, - bpmnRenderer: {} - }) +/** Zoom:恢复 */ +const processReZoom = () => { + defaultZoom.value = 1 + bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto') } -/* 创建新的流程图 */ -const createNewDiagram = async (xml) => { - // 将字符串转换成图显示出来 - let newId = `Process_${new Date().getTime()}` - let newName = `业务流程_${new Date().getTime()}` - let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix) - try { - let { warnings } = await bpmnModeler.importXML(xmlString) - if (warnings && warnings.length) { - warnings.forEach((warn) => console.warn(warn)) - } - // 高亮流程图 - await highlightDiagram() - const canvas = bpmnModeler.get('canvas') - canvas.zoom('fit-viewport', 'auto') - } catch (e) { - console.error(e) - // console.error(`[Process Designer Warn]: ${e?.message || e}`); +/** 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) } -/* 高亮流程图 */ -// TODO 芋艿:如果多个 endActivity 的话,目前的逻辑可能有一定的问题。https://www.jdon.com/workflow/multi-events.html -const highlightDiagram = async () => { - const activityList = activityLists.value - if (activityList.length === 0) { +/** 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、marker-start 暂时也注释了!!! +const addCustomDefs = () => { + if (!bpmnViewer.value) { return } - // 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现 - // 再次基础上,增加不同审批结果的颜色等等 - let canvas = bpmnModeler.get('canvas') - let todoActivity: any = activityList.find((m: any) => !m.endTime) // 找到待办的任务 - let endActivity: any = activityList[activityList.length - 1] // 获得最后一个任务 - let findProcessTask = false //是否已经高亮了进行中的任务 - //进行中高亮之后的任务 key 集合,用于过滤掉 taskList 进行中后面的任务,避免进行中后面的数据 Hover 还有数据 - let removeTaskDefinitionKeyList = [] - // debugger - bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach((n: any) => { - let activity: any = activityList.find((m: any) => m.key === n.id) // 找到对应的活动 - if (!activity) { - 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) } - if (n.$type === 'bpmn:UserTask') { - // 用户任务 - // 处理用户任务的高亮 - const task: any = taskList.value.find((m: any) => m.id === activity.taskId) // 找到活动对应的 taskId - if (!task) { - return - } - // 进行中的任务已经高亮过了,则不高亮后面的任务了 - if (findProcessTask) { - removeTaskDefinitionKeyList.push(n.id) - return - } - // 高亮任务 - canvas.addMarker(n.id, getResultCss(task.status)) - //标记是否高亮了进行中任务 - if (task.status === 1) { - findProcessTask = true - } - // 如果非通过,就不走后面的线条了 - if (task.status !== 2) { - return - } - // 处理 outgoing 出线 - const outgoing = getActivityOutgoing(activity) - outgoing?.forEach((nn: any) => { - // debugger - let targetActivity: any = activityList.find((m: any) => m.key === nn.targetRef.id) - // 如果目标活动存在,则根据该活动是否结束,进行【bpmn:SequenceFlow】连线的高亮设置 - if (targetActivity) { - canvas.addMarker(nn.id, targetActivity.endTime ? 'highlight' : 'highlight-todo') - } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') { - // TODO 芋艿:这个流程,暂时没走到过 - 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') { - // TODO 芋艿:这个流程,暂时没走到过 - 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') - } + } +} + +/** 高亮流程 */ +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') } - }) - } else if (n.$type === 'bpmn:ExclusiveGateway') { - // 排它网关 - // 设置【bpmn:ExclusiveGateway】排它网关的高亮 - canvas.addMarker(n.id, getActivityHighlightCss(activity)) - // 查找需要高亮的连线 - let matchNN: any = undefined - let matchActivity: any = undefined - n.outgoing?.forEach((nn: any) => { - let targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id) - if (!targetActivity) { - return - } - // 特殊判断 endEvent 类型的原因,ExclusiveGateway 可能后续连有 2 个路径: - // 1. 一个是 UserTask => EndEvent - // 2. 一个是 EndEvent - // 在选择路径 1 时,其实 EndEvent 可能也存在,导致 1 和 2 都高亮,显然是不正确的。 - // 所以,在 matchActivity 为 EndEvent 时,需要进行覆盖~~ - if (!matchActivity || matchActivity.type === 'endEvent') { - matchNN = nn - matchActivity = targetActivity - } - }) - if (matchNN && matchActivity) { - canvas.addMarker(matchNN.id, getActivityHighlightCss(matchActivity)) } - } else if (n.$type === 'bpmn:ParallelGateway') { - // 并行网关 - // 设置【bpmn:ParallelGateway】并行网关的高亮 - canvas.addMarker(n.id, getActivityHighlightCss(activity)) - n.outgoing?.forEach((nn: any) => { - // 获得连线是否有指向目标。如果有,则进行高亮 - const targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id) - if (targetActivity) { - canvas.addMarker(nn.id, getActivityHighlightCss(targetActivity)) // 高亮【bpmn:SequenceFlow】连线 - // 高亮【...】目标。其中 ... 可以是 bpm:UserTask、也可以是其它的。当然,如果是 bpm:UserTask 的话,其实不做高亮也没问题,因为上面有逻辑做了这块。 - canvas.addMarker(nn.targetRef.id, getActivityHighlightCss(targetActivity)) - } - }) - } else if (n.$type === 'bpmn:StartEvent') { - // 开始节点 - canvas.addMarker(n.id, 'highlight') - n.outgoing?.forEach((nn) => { - // outgoing 例如说【bpmn:SequenceFlow】连线 - // 获得连线是否有指向目标。如果有,则进行高亮 - let targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id) - if (targetActivity) { - canvas.addMarker(nn.id, 'highlight') // 高亮【bpmn:SequenceFlow】连线 - canvas.addMarker(n.id, 'highlight') // 高亮【bpmn:StartEvent】开始节点(自己) - } - }) - } else if (n.$type === 'bpmn:EndEvent') { - // 结束节点 - if (!processInstance.value || processInstance.value.status === 1) { - return + }) + } + 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') } - canvas.addMarker(n.id, getResultCss(processInstance.value.status)) - } else if (n.$type === 'bpmn:ServiceTask') { - //服务任务 - if (activity.startTime > 0 && activity.endTime === 0) { - //进入执行,标识进行色 - canvas.addMarker(n.id, getResultCss(1)) - } - if (activity.endTime > 0) { - // 执行完成,节点标识完成色, 所有outgoing标识完成色。 - canvas.addMarker(n.id, getResultCss(2)) - const outgoing = getActivityOutgoing(activity) - outgoing?.forEach((out) => { - canvas.addMarker(out.id, getResultCss(2)) - }) - } - } else if (n.$type === 'bpmn:SequenceFlow') { - let targetActivity = activityList.find((m: any) => m.key === n.targetRef.id) - if (targetActivity) { - canvas.addMarker(n.id, getActivityHighlightCss(targetActivity)) - } - } - }) - if (!isEmpty(removeTaskDefinitionKeyList)) { - taskList.value = taskList.value.filter( - (item) => !removeTaskDefinitionKeyList.includes(item.taskDefinitionKey) + }) + } + + // 特殊:处理 end 节点的高亮。因为 end 在拒绝、取消时,被后端计算成了 finishedTaskActivityIds 里 + if ( + [BpmProcessInstanceStatus.CANCEL, BpmProcessInstanceStatus.REJECT].includes( + processInstance.value.status ) - } -} - -const getActivityHighlightCss = (activity) => { - return activity.endTime ? 'highlight' : 'highlight-todo' -} - -const getResultCss = (status) => { - if (status === 1) { - // 审批中 - return 'highlight-todo' - } else if (status === 2) { - // 已通过 - return 'highlight' - } else if (status === 3) { - // 不通过 - return 'highlight-reject' - } else if (status === 4) { - // 已取消 - return 'highlight-cancel' - } else if (status === 5) { - // 退回 - return 'highlight-return' - } else if (status === 6) { - // 委派 - return 'highlight-todo' - } else if (status === 7) { - // 审批通过中 - return 'highlight-todo' - } else if (status === 0) { - // 待审批 - return 'highlight-todo' - } - return '' -} - -const getActivityOutgoing = (activity) => { - // 如果有 outgoing,则直接使用它 - if (activity.outgoing && activity.outgoing.length > 0) { - return activity.outgoing - } - // 如果没有,则遍历获得起点为它的【bpmn:SequenceFlow】节点们。原因是:bpmn-js 的 UserTask 拿不到 outgoing - const flowElements = bpmnModeler.getDefinitions().rootElements[0].flowElements - const outgoing: any[] = [] - flowElements.forEach((item: any) => { - if (item.$type !== 'bpmn:SequenceFlow') { - return - } - if (item.sourceRef.id === activity.key) { - outgoing.push(item) - } - }) - return outgoing -} -const initModelListeners = () => { - const EventBus = bpmnModeler.get('eventBus') - // 注册需要的监听事件 - EventBus.on('element.hover', function (eventObj) { - let element = eventObj ? eventObj.element : null - elementHover(element) - }) - EventBus.on('element.out', function (eventObj) { - let element = eventObj ? eventObj.element : null - elementOut(element) - }) -} -// 流程图的元素被 hover -const elementHover = (element) => { - element.value = element - !elementOverlayIds.value && (elementOverlayIds.value = {}) - !overlays.value && (overlays.value = bpmnModeler.get('overlays')) - // 展示信息 - // console.log(activityLists.value, 'activityLists.value') - // console.log(element.value, 'element.value') - const activity = activityLists.value.find((m) => m.key === element.value.id) - // console.log(activity, 'activityactivityactivityactivity') - if (!activity) { - return - } - if (!elementOverlayIds.value[element.value.id] && element.value.type !== 'bpmn:Process') { - let html = `<div class="element-overlays"> - <p>Elemet id: ${element.value.id}</p> - <p>Elemet type: ${element.value.type}</p> - </div>` // 默认值 - if (element.value.type === 'bpmn:StartEvent' && processInstance.value) { - html = `<p>发起人:${processInstance.value.startUser.nickname}</p> - <p>部门:${processInstance.value.startUser.deptName}</p> - <p>创建时间:${formatDate(processInstance.value.createTime)}` - } else if (element.value.type === 'bpmn:UserTask') { - let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId - if (!task) { - return + ) { + 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') } - let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS) - let dataResult = '' - optionData.forEach((element) => { - if (element.value == task.status) { - dataResult = element.label - } - }) - html = `<p>审批人:${task.assigneeUser.nickname}</p> - <p>部门:${task.assigneeUser.deptName}</p> - <p>结果:${dataResult}</p> - <p>创建时间:${formatDate(task.createTime)}</p>` - // html = `<p>审批人:${task.assigneeUser.nickname}</p> - // <p>部门:${task.assigneeUser.deptName}</p> - // <p>结果:${getIntDictOptions( - // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, - // task.status - // )}</p> - // <p>创建时间:${formatDate(task.createTime)}</p>` - if (task.endTime) { - html += `<p>结束时间:${formatDate(task.endTime)}</p>` - } - if (task.reason) { - html += `<p>审批建议:${task.reason}</p>` - } - } else if (element.value.type === 'bpmn:ServiceTask' && processInstance.value) { - if (activity.startTime > 0) { - html = `<p>创建时间:${formatDate(activity.startTime)}</p>` - } - if (activity.endTime > 0) { - html += `<p>结束时间:${formatDate(activity.endTime)}</p>` - } - console.log(html) - } else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) { - let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS) - let dataResult = '' - optionData.forEach((element) => { - if (element.value == processInstance.value.status) { - dataResult = element.label - } - }) - html = `<p>结果:${dataResult}</p>` - // html = `<p>结果:${getIntDictOptions( - // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, - // processInstance.value.status - // )}</p>` - if (processInstance.value.endTime) { - html += `<p>结束时间:${formatDate(processInstance.value.endTime)}</p>` - } - } - // console.log(html, 'html111111111111111') - elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(element.value, { - position: { left: 0, bottom: 0 }, - html: `<div class="element-overlays">${html}</div>` }) } } -// 流程图的元素被 out -const elementOut = (element) => { - toRaw(overlays.value).remove({ element }) - elementOverlayIds.value[element.id] = null -} +watch( + () => props.xml, + (newXml) => { + importXML(newXml) + }, + { immediate: true } +) +watch( + () => props.view, + (newView) => { + setProcessStatus(newView) + }, + { immediate: true } +) + +/** mounted:初始化 */ onMounted(() => { - xml.value = props.value - activityLists.value = props.activityData - // 初始化 - initBpmnModeler() - createNewDiagram(xml.value) - // 初始模型的监听器 - initModelListeners() + importXML(props.xml) + setProcessStatus(props.view) }) +/** unmount:销毁 */ onBeforeUnmount(() => { - // this.$once('hook:beforeDestroy', () => { - // }) - if (bpmnModeler) bpmnModeler.destroy() - emit('destroy', bpmnModeler) - bpmnModeler = null + clearViewer() }) - -watch( - () => props.value, - (newValue) => { - xml.value = newValue - createNewDiagram(xml.value) - } -) -watch( - () => props.activityData, - (newActivityData) => { - activityLists.value = newActivityData - createNewDiagram(xml.value) - } -) -watch( - () => props.processInstanceData, - (newProcessInstanceData) => { - processInstance.value = newProcessInstanceData - createNewDiagram(xml.value) - } -) -watch( - () => props.taskData, - (newTaskListData) => { - taskList.value = newTaskListData - createNewDiagram(xml.value) - } -) </script> - -<style lang="scss"> -/** 处理中 */ -.highlight-todo.djs-connection > .djs-visual > path { - stroke: #1890ff !important; - stroke-dasharray: 4px !important; - fill-opacity: 0.2 !important; -} - -.highlight-todo.djs-shape .djs-visual > :nth-child(1) { - fill: #1890ff !important; - stroke: #1890ff !important; - stroke-dasharray: 4px !important; - fill-opacity: 0.2 !important; -} - -:deep(.highlight-todo.djs-connection > .djs-visual > path) { - stroke: #1890ff !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: #1890ff !important; - stroke: #1890ff !important; - stroke-dasharray: 4px !important; - fill-opacity: 0.2 !important; -} - -/** 通过 */ -.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:not(.djs-connection) .djs-visual > :nth-child(1) { - fill: green !important; /* color elements as green */ -} - -: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; -} - -.djs-element.highlight > .djs-visual > path { - stroke: green !important; -} - -/** 不通过 */ -.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; - marker-end: url(#sequenceflow-end-white-success) !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; -} - -/** 回退 */ -.highlight-return.djs-shape .djs-visual > :nth-child(1) { - fill: #e6a23c !important; - stroke: #e6a23c !important; - fill-opacity: 0.2 !important; -} - -.highlight-return.djs-shape .djs-visual > :nth-child(2) { - fill: #e6a23c !important; -} - -.highlight-return.djs-shape .djs-visual > path { - fill: #e6a23c !important; - fill-opacity: 0.2 !important; - stroke: #e6a23c !important; -} - -.highlight-return.djs-connection > .djs-visual > path { - stroke: #e6a23c !important; -} - -.highlight-return:not(.djs-connection) .djs-visual > :nth-child(1) { - fill: #e6a23c !important; /* color elements as green */ -} - -:deep(.highlight-return.djs-shape .djs-visual > :nth-child(1)) { - fill: #e6a23c !important; - stroke: #e6a23c !important; - fill-opacity: 0.2 !important; -} - -:deep(.highlight-return.djs-shape .djs-visual > :nth-child(2)) { - fill: #e6a23c !important; -} - -:deep(.highlight-return.djs-shape .djs-visual > path) { - fill: #e6a23c !important; - fill-opacity: 0.2 !important; - stroke: #e6a23c !important; -} - -:deep(.highlight-return.djs-connection > .djs-visual > path) { - stroke: #e6a23c !important; -} - -.element-overlays { - width: 200px; - padding: 8px; - color: #fafafa; - background: rgb(0 0 0 / 60%); - border-radius: 4px; - box-sizing: border-box; -} -</style> -- Gitblit v1.9.3