\> 背景:根据目前设计稿与大致功能,需要调研出符合功能需求的flow依赖,大致需求: \> \> 1、自定义节点 \> \> 2、自定义连接线 \> \> 3、自定义连接桩(可连接态、不可连接态、悬浮态) \> \> 4、画布缩放api \> \> 5、撤销和恢复api \> \> 6、适应画布 \> \> 7、框选 \> \> 8、上手难度与代码复杂度 \> \> 9、导出导入功能 \> 根据具体需求选取出了三个比较符合的依赖 vue flow:\[https://vueflow.dev/\](https://vueflow.dev/) logicflow:\[http://logicflow.cn/\](http://logicflow.cn/) antv x6:\[https://x6.antv.antgroup.com/\](https://x6.antv.antgroup.com/) # 1、基础信息 \| \| vue flow \| logicflow \| antv x6 \| \| --- \| --- \| --- \| --- \| \| 最新更新时间 \| 2025 年 9 月 22 日 \| 2025 年 9 月 15 日 \| 一年前 \| \| 适用技术栈 \| vue \| JS \| JS \| \| stars \| 5.7k \| 10.6k \| 6.2k \| \| 自定义节点 \| ✅ \| ✅ \| ✅ \| \| 自定义连接线 \| ✅ \| ✅ \| ✅ \| \| 自定义连接桩 \| ✅ \| ✅ \| ✅ \| \| 画布缩放api \| ✅ \| ✅ \| ✅ \| \| 撤销和恢复api \| ❌ \| ✅ \| ✅ \| \| 适应画布 \| ✅ \| ✅ \| ✅ \| \| 框选 \| ✅ \| ✅ \| ✅ \| \| 自动布局 \| ❌ \| ✅ \| ❌ \| \| 群组/嵌套 \| ✅ \| ✅ \| ✅ \| # 2、使用方法 ## 2.1. Vue Flow ### 基本介绍 Vue Flow 是一个高度可定制的 Vue 3 流程图组件,专为构建复杂的节点式编辑器而设计,支持缩放、平移、元素拖拽等交互功能,以及自定义节点和边的能力。 ### 核心特性 + 完全基于 Vue 3 开发,充分利用了组合式 API 的优势 + 支持自定义节点和边的创建,通过插槽机制实现组件复用 + 提供丰富的内置组件如 MiniMap、Background、Controls 等 + 响应式数据流,节点和边的更新会自动反映在视图上 + 支持缩放、平移、对齐等交互功能 ### 安装方式 \`\`\`bash npm add @vue-flow/core \`\`\` ### 基本使用 1. 引入核心组件和样式 \`\`\`javascript import { VueFlow } from '@vue-flow/core' import '@vue-flow/core/dist/style.css' import '@vue-flow/core/dist/theme-default.css' \`\`\` 2. 定义节点和边 \`\`\`javascript const nodes = ref(\[ { id: '1', type: 'input', // 输入类型节点,通常用作流程的开始 data: { label: 'Node 1' }, position: { x: 250, y: 0 }, class: 'light', }, { id: '2', type: 'output', // 输出类型节点,通常用作流程的结束 data: { label: 'Node 2' }, position: { x: 100, y: 100 }, class: 'light', }, { id: '3', type: 'multi-handle', // 使用自定义的多连接点节点类型 data: { label: '多连接点节点', description: '这个节点有多个输入和输出连接点' }, position: { x: 400, y: 100 }, class: 'light', }, \]) const edges = ref(\[ { id: 'e1-2', source: '1', // 起始节点ID target: '2', // 目标节点ID animated: true, // 启用动画效果 }, { id: 'e1-3', source: '1', target: '3', targetHandle: 'top-1', // 指定连接到Node3的顶部第一个连接点 label: 'edge with arrowhead', // 边的标签文本 markerEnd: MarkerType.ArrowClosed, // 结束处添加箭头标记 }, { id: 'e4-5', type: 'step', // 阶梯型连接线 source: '4', target: '5', label: 'Node 2', style: { stroke: 'orange' }, // 自定义边的样式 labelBgStyle: { fill: 'orange' }, // 自定义标签背景样式 }, \]) \`\`\` 3. 使用组件 \`\`\`html \`\`\` ### 导入导出数据 \`\`\`javascript const { toObject, fromObject } = useVueFlow() // 保存图表状态的存储键名 const FLOW_STORAGE_KEY = 'vue-flow-saved-state' /\*\* \* 保存当前流程图状态到本地存储 \*/ function saveFlowState() { // 获取当前流程图的完整状态 const flowObject = toObject() // 将状态序列化后保存到本地存储 localStorage.setItem(FLOW_STORAGE_KEY, JSON.stringify(flowObject)) } /\*\* \* 从本地存储恢复流程图状态 \*/ function loadFlowState() { // 从本地存储获取保存的流程图状态 const savedFlow = localStorage.getItem(FLOW_STORAGE_KEY) // 解析保存的数据 const flowObject = JSON.parse(savedFlow) // 使用fromObject方法恢复流程图状态 fromObject(flowObject) } \`\`\` ### 优势 + 与 Vue 3 生态系统深度集成,使用体验一致 + 基于插槽的自定义节点和边系统,灵活度高 + 组件化设计,便于扩展和定制 + 文档完善,但只有英文 ### 局限性 + 仅支持 Vue 3,不兼容 Vue 2 + 不支持撤销回退api,如果通过change时间监听可能会有问题(issue中有人提到多选移动后的change有问题) + 扩展插件相对较少 !\[\](https://cdn.nlark.com/yuque/0/2025/png/32647592/1758781542318-1c97154a-4d51-4e94-99bd-88d229bf168c.png) ## 2.2. LogicFlow ### 基本介绍 LogicFlow 是由滴滴开源的一个专注于流程可视化的前端框架,提供了基础的绘图能力和完整的配置和扩展机制,能够满足各种流程图和可视化需求。 ### 核心特性 + 内置多种基础节点和边 + 提供完备的事件机制和插件系统 + 提供Vue React 节点渲染机制 ### 安装方式 \`\`\`bash npm install @logicflow/core --save npm install @logicflow/extension --save \`\`\` ### 基本使用 1. 创建容器和引入样式 \`\`\`html \`\`\` 2. 初始化图表 \`\`\`javascript import { ref, onMounted } from 'vue'; import LogicFlow from "@logicflow/core"; import "@logicflow/core/lib/style/index.css"; import { register, getTeleport } from '@logicflow/vue-node-registry' import ProgressNode from './logicflow-com/ProgressNode.vue' const containerRef = ref(null); const flowId = ref(''); const TeleportContainer = getTeleport(); let lfRef = ref(null); onMounted(() =\> { if (containerRef.value) { const lf = new LogicFlow({ container: containerRef.value, grid: true, }); lfRef.value = lf; // 注册自定义Vue节点 register({ type: 'custom-vue-node', component: ProgressNode }, lf); lf.on('graph:rendered', ({ graphModel }) =\> { flowId.value = graphModel.flowId; }); // 渲染基础图形 lf.render({ // 节点数据 nodes: \[ { id: '21', type: 'rect', x: 200, y: 200, text: '矩形节点', properties: { width: 160, height: 80, } }, { id: '50', type: 'circle', x: 400, y: 200, text: '圆形节点', properties: { r: 60, } }, \], // 边数据 edges: \[ { id: 'edge-1', type: 'polyline', sourceNodeId: '21', targetNodeId: '50', }, \], }); // 添加自定义Vue节点 const node1 = lf.addNode({ id: 'vue-node-1', type: 'custom-vue-node', x: 80, y: 80, properties: { progress: 60, width: 80, height: 80 } }); console.log('node1 ---\>\>\>', node1); // 添加第二个自定义Vue节点 const node2 = lf.addNode({ id: 'vue-node-2', type: 'custom-vue-node', x: 360, y: 80, properties: { progress: 30, width: 80, height: 80 } }); // 定时更新节点2的进度 setInterval(() =\> { const { properties } = node2.getData(); console.log('properties.progress ---\>\>\>', properties?.progress); const progress = properties?.progress \|\| 0; node2.setProperty('progress', (progress + 10) % 100); }, 2000); // 连接两个自定义节点 lf.addEdge({ id: 'edge-vue-nodes', type: 'polyline', sourceNodeId: 'vue-node-1', targetNodeId: 'vue-node-2' }); } }); \`\`\` 3. 使用插件 \`\`\`javascript import { Control } from "@logicflow/extension"; import "@logicflow/extension/lib/style/index.css"; // 全局使用 LogicFlow.use(Control); // 或实例使用 const lf = new LogicFlow({ plugins: \[Control\], }); \`\`\` ### 导入导出数据 \`\`\`javascript // 导出数据 const data = lf.getGraphData(); // 导入数据 const lf = lfRef.value; lf.render(data); \`\`\` ### 优势 + 强大的插件生态系统:\[http://logicflow.cn/tutorial/extension/intro\](http://logicflow.cn/tutorial/extension/intro) + 众多实用api:\[http://logicflow.cn/api\](http://logicflow.cn/api) + 支持流程图数据导入导出 + 与主流框架良好兼容(Vue、React等) ### 局限性 + 数据无双向绑定,修改数据需要通过函数通知 + 更适合自定义节点较少的项目 ## 2.3. AntV X6 ### 基本介绍 AntV X6 是蚂蚁集团开源的图编辑引擎,提供了一系列开箱即用的交互组件和简单易用的节点定制能力,特别适合各类流程图、DAG图、ER图等应用场景。 ### 核心特性 + 丰富的内置节点和边类型 + 支持 SVG 和 HTML 节点混合渲染 + 完备的事件系统和交互控制 + 支持插件扩展,如 MiniMap、Snapline、Transform等 + 提供Vue React 节点渲染机制 ### 安装方式 \`\`\`bash npm install @antv/x6 --save \`\`\` ### 基本使用 1. 创建容器 \`\`\`html \`\`\` 2. 注册自定义节点和添加元素 \`\`\`javascript import { ref, onMounted, onBeforeUnmount } from 'vue'; import { Graph, Shape } from '@antv/x6'; import { register, getTeleport } from '@antv/x6-vue-shape'; import ProgressNode from './antVX6-com/ProgressNode.vue'; // 注册自定义矩形节点 Graph.registerNode( 'custom-rect', { inherit: 'rect', width: 120, height: 60, attrs: { body: { fill: '#ffffff', stroke: '#5F95FF', strokeWidth: 2, rx: 6, ry: 6, }, text: { fontSize: 14, fill: '#333333', }, }, ports: { groups: { top: { position: 'top', attrs: { circle: { r: 4, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', }, }, }, right: { position: 'right', attrs: { circle: { r: 4, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', }, }, }, bottom: { position: 'bottom', attrs: { circle: { r: 4, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', }, }, }, left: { position: 'left', attrs: { circle: { r: 4, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', }, }, }, }, items: \[ { group: 'top', id: 't1' }, { group: 'right', id: 'r1' }, { group: 'bottom', id: 'b1' }, { group: 'left', id: 'l1' }, \], }, }, true, ); \`\`\` 3. 初始化图表 \`\`\`javascript // 创建DOM引用 const containerRef = ref(null); let graph = null; // 获取传送门组件 const TeleportContainer = getTeleport(); // 图表数据 - 节点 const graphNodes = \[ { id: 'rect1', shape: 'custom-rect', x: 100, y: 100, label: '开始节点', }, { id: 'barNode', shape: 'custom-vue-node', x: 400, y: 100, data: { progress: 60, }, showCircle: false, }, { id: 'circleNode', shape: 'custom-vue-node', x: 250, y: 250, data: { progress: 30, }, showCircle: true, }, \]; // 图表数据 - 连接边 const graphEdges = \[ { id: 'edge1', source: { cell: 'rect1', port: 'r1' }, target: { cell: 'barNode', port: 'l1' }, attrs: { line: { stroke: '#5F95FF', strokeWidth: 2, }, }, label: { text: '连接到条形进度', fill: '#333', fontSize: 12, refX: 0.5, refY: -10, }, }, { id: 'edge2', source: { cell: 'barNode', port: 'b1' }, target: { cell: 'circleNode', port: 't1' }, attrs: { line: { stroke: '#5F95FF', strokeWidth: 2, }, }, label: { text: '连接到圆形进度', fill: '#333', fontSize: 12, refX: 0.5, refY: -10, }, }, \]; onMounted(() =\> { // 初始化图表 graph = new Graph({ container: containerRef.value, // 图表容器元素引用 width: 800, // 图表宽度 height: 500, // 图表高度 grid: { size: 10, // 网格大小 visible: true, // 显示网格 type: 'dot', // 网格类型:点状 args: { color: '#cccccc', // 网格颜色 thickness: 1, // 网格线粗细 }, }, background: { color: '#f8f9fa', // 背景颜色 }, connecting: { router: 'manhattan', // 连接线路由:曼哈顿路由(直角连接) connector: { name: 'rounded', // 连接线类型:圆角 args: { radius: 8, // 圆角半径 }, }, anchor: 'center', // 锚点位置:中心 connectionPoint: 'boundary', // 连接点:边界 allowBlank: false, // 不允许连接到空白区域 snap: { radius: 20, // 吸附半径 }, createEdge () { // 创建边的回调函数 return new Shape.Edge({ attrs: { line: { stroke: '#5F95FF', // 线条颜色 strokeWidth: 2, // 线条宽度 targetMarker: { name: 'block', // 目标标记:块状 width: 12, // 标记宽度 height: 8, // 标记高度 }, }, }, zIndex: 0, // 层级 }); }, validateConnection ({ sourceView, targetView, sourceMagnet, targetMagnet }) { // 验证连接是否有效 if (sourceView === targetView) { return false; // 不允许连接到自身 } if (!sourceMagnet) { return false; // 源节点必须有磁体 } if (!targetMagnet) { return false; // 目标节点必须有磁体 } return true; }, }, highlighting: { // 高亮设置 magnetAvailable: { name: 'stroke', // 高亮类型:描边 args: { padding: 4, // 内边距 attrs: { strokeWidth: 2, // 描边宽度 stroke: 'rgba(223,234,255)', // 描边颜色 }, }, }, }, mousewheel: { // 鼠标滚轮设置 enabled: true, // 启用鼠标滚轮 zoomAtMousePosition: true, // 以鼠标位置为中心缩放 modifiers: 'ctrl', // 需按下Ctrl键才能缩放 minScale: 0.5, // 最小缩放比例 maxScale: 3, // 最大缩放比例 }, interacting: { // 交互设置 nodeMovable: true, // 允许移动节点 edgeMovable: true, // 允许移动边 edgeLabelMovable: true, // 允许移动边标签 arrowheadMovable: true, // 允许移动箭头 vertexMovable: true, // 允许移动顶点 vertexAddable: true, // 允许添加顶点 vertexDeletable: true, // 允许删除顶点 }, }); // 注册自定义矩形节点 Graph.registerNode( 'custom-rect', { inherit: 'rect', width: 120, height: 60, attrs: { body: { fill: '#ffffff', stroke: '#5F95FF', strokeWidth: 2, rx: 6, ry: 6, }, text: { fontSize: 14, fill: '#333333', }, }, }, true, ); // 使用fromJSON加载整个图结构 const graphData = { nodes: graphNodes, edges: graphEdges, }; graph.fromJSON(graphData); // 添加事件监听 graph.on('node:click', ({ node }) =\> { // 如果是Vue节点,更新进度 if (node.shape === 'custom-vue-node') { const { progress } = node.getData() \|\| { progress: 0 }; node.setData({ progress: (progress + 10) % 100, }); } else { alert(\`点击了节点: ${node.attr('text/text') \|\| '自定义Vue节点'}\`); } }); graph.on('edge:click', ({ edge }) =\> { alert(\`点击了连接线: ${edge.getLabelAt(0).attrs.text.text}\`); }); // 定时更新Vue节点进度 const intervalId = setInterval(() =\> { const allVueNodes = graph.getNodes().filter(node =\> node.shape === 'custom-vue-node'); allVueNodes.forEach(node =\> { const { progress } = node.getData() \|\| { progress: 0 }; node.setData({ progress: (progress + 5) % 100, }); }); }, 3000); // 保存定时器ID以便清除 graph.intervalId = intervalId; // 缩放到适合视图 graph.zoomToFit({ padding: 20 }); }); \`\`\` 4. 事件监听和交互 \`\`\`javascript // 添加事件监听 graph.on('node:click', ({ node }) =\> { // 如果是Vue节点,更新进度 if (node.shape === 'custom-vue-node') { const { progress } = node.getData() \|\| { progress: 0 }; node.setData({ progress: (progress + 10) % 100, }); } else { alert(\`点击了节点: ${node.attr('text/text') \|\| '自定义Vue节点'}\`); } }); graph.on('edge:click', ({ edge }) =\> { alert(\`点击了连接线: ${edge.getLabelAt(0).attrs.text.text}\`); }); // 定时更新Vue节点进度 const intervalId = setInterval(() =\> { const allVueNodes = graph.getNodes().filter(node =\> node.shape === 'custom-vue-node'); allVueNodes.forEach(node =\> { const { progress } = node.getData() \|\| { progress: 0 }; node.setData({ progress: (progress + 5) % 100, }); }); }, 3000); // 子組件 // 监听数据变化 node.on('change:data', ({ current }) =\> { const { progress } = current; if (progress !== undefined) { percentage.value = progress; } }); \`\`\` ### 导入导出数据 \`\`\`javascript // 导出 const jsonData = graph.toJSON(); // 清空现有图表 graph.clearCells(); // 导入新数据 graph.fromJSON(jsonData); \`\`\` ### 优势 + 功能丰富,拥有最完备的图编辑能力 + 性能优秀,能处理大规模图形渲染 + 高度可定制,几乎所有部分都可以自定义 ### 局限性 + API 较为复杂,学习成本高,维护频率低 + 配置项繁多,初始化需要较多代码 + 与框架集成需要额外处理,如需要特定的适配组件 !\[\](https://cdn.nlark.com/yuque/0/2025/png/32647592/1758764598287-3802bbfa-96de-4e88-8e6b-fdd8bfffb41b.png)