React使用LogicFlow实现流程图(一)

📅 2026/7/10 21:45:29
React使用LogicFlow实现流程图(一)
项目背景公司项目中要用到流程图试过几个后发现LogicFlow是最符合我们想要的效果的所以研究了下这个插件并集成到公司的框架项目中。效果如图LogicFlow介绍LogicFlow 是由滴滴研发的一款流程图编辑框架内部集成了BPMN流程插件导出的数据格式是json文件。更多功能实现参考官方文档 LogicFlow官网地址注意logicflow输入输出的数据结构不同看自己需要什么结构的数据可参考项目 turboAdapter插件源码 实现了 logicflow的数据格式与turbo数据格式的相互转换实现步骤1. 使用npm安装依赖npm install logicflow/core --save # 插件包不使用插件时不需要引入 npm install logicflow/extension --save2. 定义组件 LogicFlowWidget.tsximport React, { useEffect, useRef, useState } from react import LogicFlow from logicflow/core; import { Control, BpmnElement, SelectionSelect, DndPanel, Snapshot } from logicflow/extension; import logicflow/core/dist/style/index.css; import logicflow/extension/lib/style/index.css; import TurboAdapter from ./turboAdapter import ./styles.less export const LogicFlowWidget (() { const containerRef useRef(null); const lfRef useRef(null); useEffect(() { if (!containerRef.current) return; const lf new LogicFlow({ container: containerRef.current, // 指定容器ID必传 plugins: [ Control, BpmnElement, SelectionSelect, DndPanel, Snapshot, TurboAdapter ], grid: { visible: true, size: 10, }, keyboard: { enabled: true }, }); lfRef.current lf; // 设置左侧节点类型 setPatternItems() // 渲染流程图 lfRef.current.render(); return () { lfRef.current?.off(); }; }, []); // 定义节点 const setPatternItems () { lfRef.current.extension.dndPanel.setPatternItems([ { label: 选区, icon: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP2026ETdpv10iy7WFbqFyyW6GBywLCv5gIDw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3QJAKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAWGu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLdaDUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxmv7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII, callback: () { lfRef.current.extension.selectionSelect.openSelectionSelect(); lfRef.current.once(selection:selected, () { lfRef.current.extension.selectionSelect.closeSelectionSelect(); }); } }, { type: bpmn:startEvent, text: 开始, label: 开始节点, icon: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBgb1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQmsxrB/nNSUWVveI48ayrFGyC2E2CaWrZHXvOuzCiV6iycWe1Rd1Q6QUG07nb5SbPrL4426d9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47XoepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3usDFMVrIju54RYYbFKpALZAo7sB6wcKyyrdaBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0zPnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCdlQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGhlPE/wG0rgfIUbZK5AAAAABJRU5ErkJggg, }, { type: bpmn:userTask, label: 用户任务, icon: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0Dg1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQBVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeAYeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQyJjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTbyr31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0HAd2GelJsZz1OJegCxqzlCLVHa/IibuHeJ1HAKzhuDRymNaRFM4jU6UWKXorRmbyqkq/D76FffevwdCpjN3UAN/C9JRVTDuOxC/ohEdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg, }, { type: bpmn:exclusiveGateway, label: 条件判断, icon: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunuL7JvmUTU3AgmTfvffPNN9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8lFsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuONi6bBxVUCvS5d9aa04so4pHW5jLTywuXAL7jJD06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9TkRiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/QtlOUYJj9HG5pVFEU7zqIYDVsw2sAJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsYwMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCCEsFdS8DCyuCn2munFpL7ctHKpXc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJNMMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII, }, { type: bpmn:endEvent, text: 结束, label: 结束节点, icon: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOftq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0MjpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPbmKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwSt1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9EKCAYhe1NGstUa6356kS9NVvt3DU2fdWtbm/lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXMXaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQpqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOebX3FClY4PwydVhthOZ9NnSntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIAlwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC, } ]); } // 保存图表 const handleSave async () { try { const graphData lfRef.current.getGraphData() console.log(graphData , graphData); // ... 可调接口保存到服务器 } catch (err) { console.error(Failed to save, err); } }; // 清空画布 const handleClearCanvas () { if (!lfRef.current) return; lfRef.current.clearData(); } // 导出JSON const handleExportJSON () { if (!lfRef.current) return; const data lfRef.current.getGraphData(); const blob new Blob([JSON.stringify(data, null, 2)], { type: application/json }); const url URL.createObjectURL(blob); const a document.createElement(a); a.href url; a.download flowchart.json; a.click(); URL.revokeObjectURL(url); }; // 导出PNG const handleExportPNG () { if (!lfRef.current) return; lfRef.current?.getSnapshot(); } // 导入流程图数据 const handleImport (event) { const file event.target.files[0]; if (!file) return; const reader new FileReader(); reader.onload (e) { try { const data JSON.parse(e.target.result); lfRef.current.render(data); } catch (error) { console.error(导入失败:, error); alert(文件格式错误); } }; reader.readAsText(file); }; return ( div classNamelogic-flow div ref{containerRef} classNameflow-container / div classNametoolbar button onClick{handleClearCanvas}清空画布/button button onClick{() handleSave()}保存/button button onClick{handleExportJSON}导出json/button button onClick{handleExportPNG}导出图片/button input typefile accept.json onChange{handleImport} / /div /div ) })3. 添加 turboAdapter.tsx文件地址turbo-client/src/components/turboAdapter.js at master · Logic-Flow/turbo-client · GitHub把该文件内容放到自己的文件夹下并引入。turboAdapter默认将turbo数据转换为bpmn节点不然导出的文件再导入会读取文件格式失败4. 设置样式 styles.less.logic-flow { position: relative; .flow-container { height: 100%; } .toolbar { position: absolute; bottom: 10px; left: 10px; z-index: 2; button { cursor: pointer; margin: 0 5px; } input { width: 76px; } } }这样就完成了一个基本的流程图组件了。从左侧拖拽节点到画布上双击节点或连接线都可以编辑文本内容。当然自己也可以扩展其他功能比如注册其他类型的节点、自定义节点属性等更多内容下篇文章补充