基于Jeecgboot3.6.3的vue3版本的流程ProcessViewer的修改

news2024/9/20 0:58:58

因为这个项目license问题无法开源,更多技术支持与服务请加入我的知识星球。

1、因为之前ProcessViewer是vue2的组件版本,平时显示也还正常,但在历史记录的时候老是出现下面的问题。

     就是第一次进去在panel点击流程图的时候不会出现,拖拉一下就会出现,看了一下代码也没有任何问题,但就是这个情况,或者放大缩小一下也可以出现,找了半天找不到原因。

2、在没有办法只好修改成vue3看看效果,修改成如下:

<template>
  <div class="process-viewer">
    <div class="process-canvas" style="height: 100%;" ref="processCanvas" v-show="!isLoading" />
    <!-- 自定义箭头样式,用于已完成状态下流程连线箭头 -->
    <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;"></path>
      </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;"></path>
      </marker>
    </defs>
    <!-- 自定义箭头样式,用于失败状态下流程连线箭头 -->
    <defs ref="customFailDefs">
      <marker id="sequenceflow-end-white-fail" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
        <path class="fail-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path>
      </marker>
      <marker id="conditional-flow-marker-white-fail" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto">
        <path class="fail-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;"></path>
      </marker>
    </defs>
    <!-- 已完成节点悬浮弹窗 -->
    <el-dialog class="comment-dialog" :title="dlgTitle || '审批记录'" v-model="dialogVisible">
      <el-row>
        <el-table :data="taskCommentList" border header-cell-class-name="table-header-gray">
          <el-table-column label="序号" header-align="center" align="center" type="index" width="55px" />
          <el-table-column label="候选办理" prop="candidate" width="150px" align="center" />
          <el-table-column label="实际办理" prop="assigneeName" width="100px" align="center" />
          <el-table-column label="处理时间" prop="createTime" width="140px" align="center" />
          <el-table-column label="办结时间" prop="endTime" width="140px" align="center" />
          <el-table-column label="耗时" prop="duration" width="100px" align="center" />
          <el-table-column label="审批意见" align="center">
            <template #default="scope">
              {{scope.row.commentList&&scope.row.commentList[0]?scope.row.commentList[0].fullMessage:''}}
            </template>
          </el-table-column>
        </el-table>
      </el-row>
    </el-dialog>
    <div style="position: absolute; top: 0; left: 0; width: 100%;">
      <el-row type="flex" justify="end">
        <el-button-group key="scale-control">
          <el-button :plain="true" :disabled="defaultZoom <= 0.3" icon="ZoomOut" @click="processZoomOut()" />
          <el-button style="width: 90px;">{{ Math.floor(defaultZoom * 10 * 10) + "%" }}</el-button>
          <el-button :plain="true" :disabled="defaultZoom >= 3.9" icon="ZoomIn" @click="processZoomIn()" />
          <el-button icon="ScaleToOriginal" @click="processReZoom()" />
          <slot />
        </el-button-group>
      </el-row>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, toRaw, watch, onBeforeUnmount } from 'vue';
import '@/package/theme/index.scss';
import BpmnViewer from 'bpmn-js/lib/Viewer';
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';

defineOptions({ name: 'ProcessViewer' })

const props = defineProps({
  xml: {
    type: String
  },
  finishedInfo: {
    type: Object
  },
  // 所有节点审批记录
  allCommentList: {
    type: Array
  }
})

const processCanvas = ref(null)
const customDefs = ref(null)
const dialogVisible = ref(false)
const dlgTitle = ref(undefined)
const defaultZoom = ref(1)
// 是否正在加载流程图
const isLoading = ref(false)
const bpmnViewer = ref(undefined)
// 已完成流程元素
const processNodeInfo = ref(undefined)
// 当前任务id
const selectTaskId = ref(undefined)
// 任务节点审批记录
const taskCommentList = ref([])
// 已完成任务悬浮延迟Timer
const hoverTimer = ref(null)
const SysFlowTaskOperationType =ref('')


const processReZoom = () => {
  defaultZoom.value = 1;
  bpmnViewer.value.get('canvas').zoom('fit-viewport', 'auto');
}
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);
}
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;
}
// 添加自定义箭头
const addCustomDefs = () => {
  const canvas = bpmnViewer.value.get('canvas');
  const svg = canvas._svg;
  svg.appendChild(customDefs.value);
}
// 任务悬浮弹窗
const onSelectElement = (element) => {
  selectTaskId.value = undefined;
  dlgTitle.value = undefined;

  if (processNodeInfo.value == null || processNodeInfo.value.finishedTaskSet == null) return;

  if (element == null || processNodeInfo.value.finishedTaskSet.indexOf(element.id) === -1) {
    return;
  }
  if (props.allCommentList == null) {//临时解决,以后可以传入
    return;
  }

  selectTaskId.value = element.id;
  dlgTitle.value = element.businessObject ? element.businessObject.name : undefined;
  // 计算当前悬浮任务审批记录,如果记录为空不显示弹窗
  console.log("props.allCommentList",props.allCommentList)
  taskCommentList.value = (toRaw(props.allCommentList)).filter(item => {
    console.log("item",item)
    console.log("selectTaskId.value",selectTaskId.value)
    return item.activityId === selectTaskId.value;
  });
  console.log("taskCommentList.value ",taskCommentList.value)
  dialogVisible.value = true;
}
// 显示流程图
const importXML = async (xml) => {
  clearViewer();
  if (xml != null && xml !== '') {
    try {
      bpmnViewer.value = new BpmnViewer({
        additionalModules: [
          // 支持移动拖动整个画布
          MoveCanvasModule
        ],
        container: processCanvas.value,
      });
      // 任务节点悬浮事件
      bpmnViewer.value.on('element.click', ({ element }) => {
        onSelectElement(element);
      });

      // 注册需要的监听事件
      bpmnViewer.value.on('element.hover', ({ element }) => {
        elementHover(element);
      });
      bpmnViewer.value.on('element.out', ({ element }) => {
        elementOut(element);
      });

      isLoading.value = true;
      await bpmnViewer.value.importXML(xml);
      addCustomDefs();
    } catch (e) {
      console.error(e);
      clearViewer();
    } finally {
      isLoading.value = false;
      setProcessStatus(processNodeInfo.value);
    }
  }
}
const elementHover = (element) => {
  if (element === null) {
    return;
  }
  if (toRaw(props.allCommentList) == null) { //临时解决,以后可以传入
    return;
  }
  const overlays = bpmnViewer.value.get("overlays");
  const commentAllList = toRaw(props.allCommentList)
  let { finishedTaskSet } = processNodeInfo.value;
  if (element.type !== "bpmn:Process" && finishedTaskSet.indexOf(element.id) > -1) {
    let html = ``; // 默认值
    if (element.type === "bpmn:StartEvent") {
      commentAllList.forEach(comment => {
        if (comment.activityId === element.id) {
          html += `<p>发起人:${comment.assigneeName}</p>
                  <p>创建时间:${comment.createTime}</p>`;
        }
      });
    } else if (element.type === "bpmn:UserTask") {
      commentAllList.forEach(comment => {
        if (comment.activityId === element.id && comment.commentList[0]) {
          html += `<p>审批人:${comment.assigneeName}</p>
                  <p>审批意见:${comment.commentList && comment.commentList[0] ? comment.commentList[0].fullMessage : ""}</p>
                  <p>审批时间:${comment.endTime}</p>
                  <p>-----------------------------------------</p>`;
        }
      });
    } else if (element.type === "bpmn:EndEvent") {
      commentAllList.forEach(comment => {
        if (comment.activityId === element.id) {
          html = `<p>结束时间:${comment.endTime}</p>`;
        }
      });
    }
    overlays.add(element, {
      position: { left: 0, bottom: 0 },
      html: `<div class="element-overlays">${html} </div>`
    });
  }
}

const elementOut = (element) => {
  if (element === null) {
    return;
  }
  const overlays = bpmnViewer.value.get("overlays");
  overlays.remove({ element });
}

// 设置流程图元素状态
const setProcessStatus = (pNodeInfo) => {
  processNodeInfo.value = pNodeInfo;
  if (isLoading.value || processNodeInfo.value == null || bpmnViewer.value == null) return;
  let { finishedSequenceFlowSet, finishedTaskSet, unfinishedTaskSet } = processNodeInfo.value;
  const canvas = bpmnViewer.value.get('canvas');
  const elementRegistry = bpmnViewer.value.get('elementRegistry');
  if (Array.isArray(finishedSequenceFlowSet)) {
    finishedSequenceFlowSet.forEach(item => {
      if (item != null && item != 'newSequenceFlowId') { //去掉之前收回创建的id
        canvas.addMarker(item, 'success');
        let element = elementRegistry.get(item);
        const conditionExpression = element.businessObject.conditionExpression;
        if (conditionExpression) {
          canvas.addMarker(item, 'condition-expression');
        }
      }
    });
  }
  if (Array.isArray(finishedTaskSet)) {
    finishedTaskSet.forEach(item => {
      canvas.addMarker(item, 'success');
    });
  }
  if (Array.isArray(unfinishedTaskSet)) {
    unfinishedTaskSet.forEach(item => {
      canvas.addMarker(item, 'current');
    });
  }
}

watch(
  () => props.xml,
  (newXml) => {
    importXML(newXml)
  },
  { immediate: true }
)


watch(
  () => props.finishedInfo,
  (newInfo) => {
    setProcessStatus(newInfo)
  },
  { immediate: true }
)

onBeforeUnmount(() => {
  clearViewer()
})

</script>

<style scoped>
  .comment-dialog :deep() .el-dialog__body {
    padding: 0px;
  }
  .element-overlays {
    position: relative;
    box-sizing: border-box;
    padding: 8px;
    background: rgba(0, 0, 0, 0.6);
    border-radius: 4px;
    color: #fafafa;
    width: 320px;
  }

</style>

3、修改后进行测试,果然正常了,不知道是什么原因引起之前的问题,目前还不得而知。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1947713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React开发者并不存在

根本就没有所谓的React开发者 — 永远不要这样称呼自己。 这是许多软件开发者犯的一个巨大错误&#xff0c;浪费了你大量时间。 专注于工具而非概念。忽视了大局。 React只是一个JavaScript工具。JavaScript只是一个计算工具。计算只是一个解决问题的工具。 当我刚开始编码时&a…

VulnHub:cengbox1

靶机下载地址&#xff0c;下载完成后&#xff0c;用VirtualBox打开靶机并修改网络为桥接即可搭建成功。 信息收集 主机发现和端口扫描 扫描攻击机&#xff08;192.168.31.218&#xff09;同网段存活主机确认目标机ip&#xff0c;并对目标机进行全面扫描。 nmap 192.168.31.…

【QT】事件分发器 事件过滤器

qt 系统 - 事件分发器 and 事件过滤器 一、事件分发器1. 事件分发器概念2. 事件分发器工作原理 二、事件过滤器 一、事件分发器 1. 事件分发器概念 在 Qt 中&#xff0c;事件分发器(Event Dispatcher) 是一个核心概念&#xff0c;用于处理 GUI 应用程序中的事件。事件分发器负…

Java黑色界面陪玩高端小程序源码陪练APP源码H5公众号源码电竞系统

&#x1f680;【电竞新纪元】解锁高端陪玩小程序源码 & 陪练APP秘籍&#xff0c;H5公众号全攻略&#xff01; &#x1f3ae; 开篇&#xff1a;电竞热潮下的新机遇 Hey游戏迷们&#xff01;随着电竞行业的蓬勃发展&#xff0c;你是否也想在这股浪潮中分得一杯羹&#xff1…

opencascade AIS_Manipulator源码学习

前言 AIS_Manipulator 是 OpenCASCADE 库中的一个类&#xff0c;用于在3D空间中对其他交互对象或一组对象进行局部变换。该类提供了直观的操控方式&#xff0c;使用户可以通过鼠标进行平移、缩放和旋转等操作。 详细功能 交互对象类&#xff0c;通过鼠标操控另一个交互对象…

新手网站利用好网站地图,提高网站的pr和权重

新手网站利用好网站地图&#xff0c;提高网站的pr和权重 它能引导蜘蛛更好的抓取收录我们网站的资源&#xff0c;从而提高网站的pr和权重。但网站地图的更新是个费神费力的工作&#xff0c;对于站长&#xff0c;尤其是精力有限的草根站长&#xff0c;怎样才能将有限的时间发挥出…

DDoS 究竟在攻击什么?

分布式拒绝服务&#xff08;DDoS&#xff09;攻击是一种常见的网络攻击形式&#xff0c;攻击者通过向目标服务端发送大量的请求&#xff0c;使目标服务端无法进行网络连接&#xff0c;无法正常提供服务。 DDoS 攻击通常是由大量的分布在全球各地的 “僵尸” 计算机&#xff08…

c++ 求解质因数(细节详解)

定义 这里先来了解几个定义&#xff08;如已了解&#xff0c;可直接看下一个板块&#xff09; 因数&#xff1a;又称为约数&#xff0c;如果整数a除以整数b&#xff08;b0&#xff09;的商正好是是整数而没有余数&#xff0c;我们就说b是a的因数 质数&#xff1a;又称为素数…

免费HTTPS代理的风险与网站推荐

在当今的网络环境中&#xff0c;免费HTTPS代理服务因其“零成本”的特点吸引了不少用户。然而&#xff0c;免费往往意味着某些隐性的代价。作为专业的测评团队&#xff0c;我们深入探索了市场上的免费HTTPS代理服务&#xff0c;并在此提醒大家注意其中的风险&#xff0c;同时推…

【企业级开发模型】Git分支设计模型 | 企业级项目挂历实战_准备工作开发场景实操

目录 3.Git分支设计模型 3.1master分支 3.2release分支 3.3develop分支 3.4feature分支 3.5hotfix分支 4.企业级项目挂历实战_准备工作&开发场景实操学习文档 3.Git分支设计模型 对于我们开发人员来说&#xff0c;对于不同的场景/环境&#xff0c;来设计分支模型。…

Redis与MySQL数据一致性问题的策略模式及解决方案

目录 一、策略模式 1、旁路缓存模式&#xff08;Cache Aside Pattern&#xff09; 2、读写穿透&#xff08;Read-Through/Write-Through&#xff09; 3、异步缓存写入&#xff08;Write Behind&#xff09; 二、一致性解决方案 1、缓存延迟双删 2、删除重试机制 3、读取…

30.【C语言】函数系列下

1.嵌套调用 *定义&#xff1a;函数之间的互相调用 *例&#xff1a; int function1(int a, int b) {function2(b);//嵌套函数的调用return a; } //注意&#xff1a;不能将function2定义在function1的里面&#xff0c;这不叫嵌套函数的调用 void function2(int c) {} #include…

聊一聊 Node.js(Express)的 req.body、req.params 和 req.query 区别和应用场景

在Node.js的Express框架中&#xff0c;处理客户端发送到服务器的数据时&#xff0c;我们主要使用req.body、req.params和req.query三个属性。这些属性虽然都是请求对象(req)的一部分&#xff0c;但它们的数据来源和用途却各不相同。本文将为大家详细解读它们的区别和使用方法。…

AI绘画SD中 ControlNet 组件 IP-Adapter 实现风格迁移,AI绘画垫图神器!

大家好&#xff0c;我是画画的小强 今天给大家介绍一下AI绘画SD中ControlNet 的 IP-Adapter 组件&#xff0c;该组件可以方便快捷的帮我们对图片的风格进行迁移&#xff0c;简而言之就是可以参考你放置的图片风格来生成其他图片。 它的效果和reference only有点类似&#xff…

LabVIEW 实现用户授权与管理多项测试项目

在使用 LabVIEW 开发测试软件时&#xff0c;用户授权和项目管理是一个重要的功能。为了确保系统安全性、灵活性和可扩展性&#xff0c;可以设计一个用户管理系统&#xff0c;允许管理员增加或减少用户的测试项目权限。以下是一个详细的实现方案&#xff0c;包括用户授权管理、项…

vue字段判断是否可以鼠标悬浮或者点击跳转

通过字段判断是否可以鼠标悬浮展示颜色 是否点击 <span :class"[converBond.stkindustry ! null ? hoverSpan:,]"click"converBond.stkindustry ! null ?goToIndustry(converBond.stkindustryname,converBond.stkindustry):false">{{converBon…

【星地多网融合调度平台】——打造全方位、立体化的应急通信网络

在复杂的应急场景下&#xff0c;信息的快速传递与指挥调度显得尤为重要。星地多网融合调度指挥箱&#xff0c;凭借其强大的多网融合能力&#xff0c;确保了指挥中心与前线救援队伍之间的信息流畅&#xff0c;无论是位置追踪、应急通信&#xff0c;还是全方位视频监控&#xff0…

数仓架构解析(第45天)

系列文章目录 经典数仓架构传统离线大数据架构 文章目录 系列文章目录烂橙子-终生成长社群群主&#xff0c;前言1. 经典数仓架构2. 传统离线大数据架构 烂橙子-终生成长社群群主&#xff0c; 采取邀约模式&#xff0c;不支持付费进入。 前言 经典数仓架构 传统离线大数据架…

细说MCU用单路DAC模块设计和输出锯齿波的实现方法

目录 一、STM32G474RE的DAC模块 二、配置 1.配置DAC 2.选择时钟源和Debug 3.配置系统时钟 三、代码修改 1.启动DAC 2.给DAC的数据输出寄存器赋值 3.运行并观察输出 一、STM32G474RE的DAC模块 有些MCU本身就带有数/模转换器(Digital to Analog Converter,DAC)模块&am…

netty入门-3 EventLoop和EventLoopGroup,简单的服务器实现

文章目录 EventLoop和EventLoopGroup服务器与客户端基本使用增加非NIO工人NioEventLoop 处理普通任务与定时任务 结语 EventLoop和EventLoopGroup 二者大概是什么这里不再赘述&#xff0c;前一篇已简述过。 不理解也没关系。 下面会简单使用&#xff0c;看了就能明白是什么 这…