news 2026/5/14 19:53:54

告别后端画图!用bpmn.js在Vue里优雅展示Activiti 6.0高亮流程图(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别后端画图!用bpmn.js在Vue里优雅展示Activiti 6.0高亮流程图(附完整代码)

基于bpmn.js的Activiti 6.0流程可视化方案设计与实现

在当今企业级应用开发中,流程引擎扮演着越来越重要的角色。Activiti作为一款成熟的开源工作流引擎,其6.0版本带来了诸多改进,但也移除了原有的流程图跟踪组件Diagram Viewer,这给开发者带来了新的挑战。本文将详细介绍如何利用bpmn.js在Vue框架中实现Activiti 6.0流程图的动态渲染与高亮展示,为开发者提供一套完整的解决方案。

1. 技术选型与架构设计

传统Activiti流程展示方案通常采用后端生成静态图片的方式,这种方式存在几个明显缺陷:

  • 字体兼容性问题:不同操作系统环境下字体渲染不一致
  • 性能瓶颈:高并发场景下图片生成压力大
  • 交互性差:无法实现动态交互效果
  • 维护成本高:每次流程变更都需要重新生成图片

相比之下,基于bpmn.js的前端渲染方案具有以下优势:

对比维度后端图片方案bpmn.js前端方案
渲染方式静态图片动态SVG
交互能力支持缩放、高亮、悬浮提示等
性能表现服务器压力大客户端渲染,减轻服务器负担
兼容性字体依赖严重纯前端实现,跨平台一致
开发效率修改需重新部署热更新,即时生效

技术栈组成

// package.json核心依赖 { "dependencies": { "bpmn-js": "^8.7.1", // 核心流程图渲染库 "vue": "^2.6.11", // 前端框架 "element-ui": "^2.15.1", // UI组件库 "xml-js": "^1.6.11" // XML处理工具 } }

2. 环境搭建与基础配置

2.1 项目初始化

首先创建一个标准的Vue项目,并安装必要依赖:

# 创建Vue项目 vue create bpmn-viewer-demo # 进入项目目录 cd bpmn-viewer-demo # 安装核心依赖 npm install bpmn-js@8.7.1 vue@2.6.11 element-ui@2.15.1 xml-js@1.6.11

2.2 基础组件结构

创建流程图展示组件BpmnViewer.vue,包含以下核心功能区域:

  1. 工具栏区域:缩放控制、视图重置等操作按钮
  2. 画布区域:流程图渲染容器
  3. 详情面板:节点详细信息展示
<template> <div class="bpmn-container"> <!-- 工具栏 --> <div class="toolbar"> <el-button-group> <el-tooltip content="缩小"> <el-button icon="el-icon-zoom-out" @click="zoomOut"/> </el-tooltip> <el-button>{{zoomLevel}}%</el-button> <el-tooltip content="放大"> <el-button icon="el-icon-zoom-in" @click="zoomIn"/> </el-tooltip> <el-tooltip content="重置视图"> <el-button icon="el-icon-refresh" @click="resetView"/> </el-tooltip> </el-button-group> </div> <!-- 流程图画布 --> <div id="bpmn-canvas"></div> <!-- 节点详情 --> <div class="detail-panel" v-if="currentNode"> <h3>{{currentNode.name}}</h3> <el-descriptions :column="1" border> <el-descriptions-item label="审批人">{{currentNode.assignee}}</el-descriptions-item> <el-descriptions-item label="状态">{{currentNode.status}}</el-descriptions-item> <el-descriptions-item label="审批意见">{{currentNode.comment}}</el-descriptions-item> </el-descriptions> </div> </div> </template>

3. 核心功能实现

3.1 流程图渲染与高亮

流程图渲染的核心流程包括:

  1. 初始化bpmn.js Viewer实例
  2. 从后端获取流程XML定义
  3. 解析并渲染流程图
  4. 根据执行状态添加高亮标记
import BpmnViewer from 'bpmn-js'; export default { data() { return { bpmnViewer: null, zoomLevel: 100, currentNode: null }; }, mounted() { this.initViewer(); this.loadProcessDiagram(); }, methods: { initViewer() { this.bpmnViewer = new BpmnViewer({ container: '#bpmn-canvas', height: '600px', width: '100%' }); }, async loadProcessDiagram() { try { // 从后端获取流程定义XML const { xml, highlights } = await this.fetchProcessData(); // 处理特殊节点(如排他网关) const processedXml = this.preprocessXml(xml); // 渲染流程图 await this.bpmnViewer.importXML(processedXml); // 添加高亮标记 this.applyHighlights(highlights); // 自适应视图 this.bpmnViewer.get('canvas').zoom('fit-viewport'); } catch (error) { console.error('流程图加载失败:', error); } }, preprocessXml(xml) { // 处理排他网关显示标记 return xml.replace( /<bpmndi:BPMNShape[^>]*bpmnElement="([^"]*)"[^>]*>/g, (match, id) => { if (id.includes('Gateway')) { return match.replace('bpmnElement="', 'isMarkerVisible="true" bpmnElement="'); } return match; } ); }, applyHighlights(highlights) { const canvas = this.bpmnViewer.get('canvas'); // 已完成节点高亮 highlights.executedNodes.forEach(nodeId => { canvas.addMarker(nodeId, 'highlight-executed'); }); // 当前活动节点高亮 highlights.activeNodes.forEach(nodeId => { canvas.addMarker(nodeId, 'highlight-active'); }); // 已流转连线高亮 highlights.completedFlows.forEach(flowId => { canvas.addMarker(flowId, 'highlight-flow'); }); } } };

3.2 交互功能实现

为提升用户体验,我们需要实现以下交互功能:

  1. 缩放控制:支持流程图的放大、缩小和重置
  2. 节点悬停:显示节点详细信息
  3. 视图导航:快速定位到特定节点
methods: { // 缩放控制 zoomIn() { this.zoomLevel = Math.min(this.zoomLevel + 10, 400); this.bpmnViewer.get('canvas').zoom(this.zoomLevel / 100); }, zoomOut() { this.zoomLevel = Math.max(this.zoomLevel - 10, 20); this.bpmnViewer.get('canvas').zoom(this.zoomLevel / 100); }, resetView() { this.zoomLevel = 100; this.bpmnViewer.get('canvas').zoom('fit-viewport'); }, // 节点交互 setupNodeInteractions() { const eventBus = this.bpmnViewer.get('eventBus'); // 节点悬停事件 eventBus.on('element.hover', event => { if (event.element.type === 'bpmn:UserTask') { this.fetchNodeDetails(event.element.id); } }); eventBus.on('element.out', () => { this.currentNode = null; }); }, async fetchNodeDetails(nodeId) { try { const response = await axios.get(`/api/process/nodes/${nodeId}`); this.currentNode = response.data; } catch (error) { console.error('获取节点详情失败:', error); } } }

4. 样式优化与视觉效果

良好的视觉效果可以显著提升用户体验。我们通过CSS实现以下效果:

  1. 节点高亮样式:区分已完成、进行中和待处理节点
  2. 连线高亮:突出显示已流转的路径
  3. 动画效果:为当前节点添加呼吸灯效果
/* 基础样式 */ #bpmn-canvas { border: 1px solid #eee; background: white; margin-top: 10px; } /* 高亮样式 */ .highlight-active .djs-visual > :nth-child(1) { fill: #FFF3E0 !important; stroke: #FFA726 !important; stroke-width: 2px; animation: pulse 2s infinite; } .highlight-executed .djs-visual > :nth-child(1) { fill: #E8F5E9 !important; stroke: #66BB6A !important; } .highlight-flow .djs-visual > :nth-child(1) { stroke: #66BB6A !important; stroke-width: 2px; } /* 呼吸灯动画 */ @keyframes pulse { 0% { stroke-width: 2px; } 50% { stroke-width: 4px; } 100% { stroke-width: 2px; } } /* 详情面板样式 */ .detail-panel { margin-top: 20px; padding: 15px; border: 1px solid #ebeef5; border-radius: 4px; background: #fafafa; }

5. 后端接口设计与数据格式

前端需要后端提供以下关键数据:

  1. 流程定义的XML字符串
  2. 已执行节点ID列表
  3. 当前活动节点ID列表
  4. 已流转的连线ID列表

推荐返回数据结构

{ "modelXml": "<bpmn2:definitions>...</bpmn2:definitions>", "highlights": { "executedNodes": ["task1", "task2"], "activeNodes": ["task3"], "completedFlows": ["flow1", "flow2"] }, "processInfo": { "name": "请假审批流程", "version": 1 } }

Java后端示例代码

@RestController @RequestMapping("/api/process") public class ProcessController { @Autowired private RepositoryService repositoryService; @Autowired private HistoryService historyService; @GetMapping("/diagram/{instanceId}") public ResponseEntity<ProcessDiagramDTO> getProcessDiagram( @PathVariable String instanceId) { // 获取流程定义 ProcessInstance instance = runtimeService.createProcessInstanceQuery() .processInstanceId(instanceId) .singleResult(); String definitionId = instance.getProcessDefinitionId(); ProcessDefinition definition = repositoryService.getProcessDefinition(definitionId); // 获取历史活动记录 List<HistoricActivityInstance> activities = historyService .createHistoricActivityInstanceQuery() .processInstanceId(instanceId) .orderByHistoricActivityInstanceStartTime().asc() .list(); // 构建高亮数据 ProcessHighlights highlights = buildHighlights(definitionId, activities); // 获取BPMN XML BpmnModel model = repositoryService.getBpmnModel(definitionId); String xml = convertModelToXml(model); // 返回结果 ProcessDiagramDTO result = new ProcessDiagramDTO(); result.setModelXml(xml); result.setHighlights(highlights); result.setProcessName(definition.getName()); return ResponseEntity.ok(result); } private ProcessHighlights buildHighlights(String definitionId, List<HistoricActivityInstance> activities) { // 实现高亮数据构建逻辑 // ... } private String convertModelToXml(BpmnModel model) { // 实现BPMN模型转XML逻辑 // ... } }

6. 性能优化与异常处理

在实际生产环境中,我们需要考虑以下优化点:

  1. XML缓存:避免每次请求都重新生成流程定义XML
  2. 懒加载:对于大型流程图,可采用分片加载策略
  3. 错误边界:妥善处理各种异常情况

优化后的前端代码

{ methods: { async loadProcessDiagram() { this.loading = true; try { // 检查本地缓存 const cacheKey = `process-${this.instanceId}`; const cached = localStorage.getItem(cacheKey); if (cached) { const { xml, highlights, timestamp } = JSON.parse(cached); // 检查缓存是否过期(1小时) if (Date.now() - timestamp < 3600000) { await this.renderDiagram(xml, highlights); return; } } // 无缓存或已过期,从服务器获取 const response = await axios.get(`/api/process/${this.instanceId}`); const { modelXml, highlights } = response.data; // 更新缓存 localStorage.setItem(cacheKey, JSON.stringify({ xml: modelXml, highlights, timestamp: Date.now() })); await this.renderDiagram(modelXml, highlights); } catch (error) { this.$notify.error({ title: '加载失败', message: '流程图加载失败,请稍后重试' }); console.error('流程图加载错误:', error); } finally { this.loading = false; } }, async renderDiagram(xml, highlights) { try { // 尝试渲染 await this.bpmnViewer.importXML(xml); this.applyHighlights(highlights); this.setupNodeInteractions(); // 初始缩放 this.bpmnViewer.get('canvas').zoom('fit-viewport'); } catch (error) { // 处理渲染错误 if (error.warnings && error.warnings.length) { console.warn('流程图渲染警告:', error.warnings); } else { throw error; } } } } }

7. 扩展功能与进阶技巧

在基础功能之上,我们可以进一步扩展以下高级功能:

  1. 流程模拟:集成bpmn-js-token-simulation实现流程模拟
  2. 属性面板:展示和编辑元素属性
  3. 自定义元素:扩展支持公司特有的流程节点类型

流程模拟集成示例

import TokenSimulation from 'bpmn-js-token-simulation'; // 初始化Viewer时添加模拟模块 this.bpmnViewer = new BpmnViewer({ container: '#bpmn-canvas', additionalModules: [ TokenSimulation ] }); // 添加模拟控制按钮 methods: { startSimulation() { const simulation = this.bpmnViewer.get('tokenSimulation'); simulation.start(); }, stopSimulation() { const simulation = this.bpmnViewer.get('tokenSimulation'); simulation.stop(); } }

自定义元素样式

// 注册自定义渲染器 this.bpmnViewer.get('modeling').updateProperties(element, { 'custom:customColor': '#FF5722', 'custom:customIcon': 'data:image/svg+xml;utf8,...' }); // 自定义渲染逻辑 const customRenderer = { canRender: (element) => { return element.businessObject.get('custom:isCustom'); }, drawShape: (parentNode, element) => { // 实现自定义绘制逻辑 // ... } }; this.bpmnViewer.get('elementRegistry').addRenderer(customRenderer);

8. 最佳实践与常见问题

在实际项目中,我们总结了以下最佳实践:

  1. 性能优化

    • 对于大型流程图,考虑分阶段渲染
    • 使用Web Worker处理XML解析
    • 实现虚拟滚动只渲染可视区域内的元素
  2. 常见问题解决

问题1:排他网关标记不显示
解决方案:在XML中为对应的BPMNShape添加isMarkerVisible属性

function fixGatewayMarkers(xml) { return xml.replace( /<bpmndi:BPMNShape[^>]*bpmnElement="([^"]*Gateway[^"]*)"[^>]*>/g, '$& isMarkerVisible="true"' ); }

问题2:自定义样式不生效
解决方案:确保CSS选择器优先级足够高,必要时添加!important

/* 提高选择器特异性 */ div#bpmn-canvas .highlight .djs-visual > :nth-child(1) { fill: red !important; }

问题3:流程图渲染错位
解决方案:确保在容器可见后再初始化Viewer,或在容器尺寸变化时调用canvas.resized()

this.$nextTick(() => { this.initViewer(); this.loadProcessDiagram(); }); // 处理窗口大小变化 window.addEventListener('resize', () => { if (this.bpmnViewer) { this.bpmnViewer.get('canvas').resized(); } });
  1. 移动端适配
    • 添加手势支持(双指缩放、拖动)
    • 优化触摸反馈
    • 调整UI元素大小确保可操作性
// 触摸事件处理 document.getElementById('bpmn-canvas').addEventListener('touchstart', (e) => { if (e.touches.length === 2) { this.startPinchDistance = getDistance( e.touches[0], e.touches[1] ); } }); // 实现手势缩放逻辑 // ...
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/14 19:53:47

MoneyPrinter知识图谱集成终极指南:3步提升AI脚本生成准确性

MoneyPrinter知识图谱集成终极指南&#xff1a;3步提升AI脚本生成准确性 【免费下载链接】MoneyPrinter Automate Creation of YouTube Shorts using MoviePy. 项目地址: https://gitcode.com/gh_mirrors/mo/MoneyPrinter 想要让AI生成的YouTube Shorts脚本更加准确、专…

作者头像 李华
网站建设 2026/5/14 19:49:45

为什么你的RPA项目ROI逐年下滑?(AI Agent底层范式迁移白皮书)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;RPA项目ROI下滑的底层归因诊断 RPA项目在落地12–18个月后普遍出现投资回报率&#xff08;ROI&#xff09;断崖式下滑&#xff0c;其根源往往不在技术失效&#xff0c;而在于流程治理、组织适配与数据熵…

作者头像 李华
网站建设 2026/5/14 19:46:23

MATPOWER数据格式 vs. IEEE格式:电力潮流计算该选谁?附实战对比

MATPOWER与IEEE数据格式深度对比&#xff1a;电力系统分析的格式选择策略 电力系统潮流计算是电网规划、运行和研究的基石&#xff0c;而数据格式的选择直接影响着分析效率和结果可靠性。面对MATPOWER自定义格式与IEEE标准格式这两种主流选择&#xff0c;许多工程师常陷入决策困…

作者头像 李华
网站建设 2026/5/14 19:46:15

MySQL 反模式与排查宝典

概述衔接前文本系列从 MySQL 分层架构出发&#xff0c;逐层深入 InnoDB 存储引擎的 BTree 索引原理、事务与 MVCC 机制、行锁与间隙锁实现、SQL 优化器决策逻辑、主从复制与 GTID 架构、分库分表与 ShardingSphere 内核、慢查询与性能诊断体系&#xff0c;以及连接管理与连接池…

作者头像 李华
网站建设 2026/5/14 19:46:01

HeaderEditor终极指南:5大实用场景教会你如何掌控浏览器请求

HeaderEditor终极指南&#xff1a;5大实用场景教会你如何掌控浏览器请求 【免费下载链接】HeaderEditor Manage browsers requests, include modify the request headers, response headers, response body, redirect requests, cancel requests 项目地址: https://gitcode.c…

作者头像 李华
网站建设 2026/5/14 19:45:00

Golang怎么做API网关_Golang API网关教程【总结】

最简路由转发应先用 net/http 原生能力硬编码转发&#xff0c;避免过早引入中间件或高级路由库&#xff1b;必须显式透传 Host 头、自定义 Transport 和 ErrorHandler&#xff0c;并手动安全重写路径&#xff1b;健康检查与熔断需按上游服务单独实现。用 net/http 做最简路由转…

作者头像 李华