当前位置: 首页 > news >正文

基于 @antv/x6 实现流程图

文章目录

  • 1. 源码及其效果图
    • 1.1 下载所需npm包
    • 2.1 效果图
    • 1.3 完整代码
      • 1.3.1 x6Config.js
      • 1.3.2 x6.vue

1. 源码及其效果图

1.1 下载所需npm包

npm install @antv/x6

2.1 效果图

在这里插入图片描述

1.3 完整代码

1.3.1 x6Config.js

import { Graph } from "@antv/x6";const groupMock = {groups: {top: {position: "top",attrs: {circle: {magnet: true,stroke: "#8f8f8f",r: 1,style: {visibility: "hidden",},},},},left: {position: "left",attrs: {circle: {magnet: true,stroke: "#8f8f8f",r: 1,style: {visibility: "hidden",},},},},right: {position: "right",attrs: {circle: {magnet: true,stroke: "#8f8f8f",r: 1,style: {visibility: "hidden",},},},},bottom: {position: "bottom",attrs: {circle: {magnet: true,stroke: "#8f8f8f",r: 1,style: {visibility: "hidden",},},},},bottomR: {position: {name: "absolute",args: { x: 70, y: 50 },},attrs: {circle: {magnet: true,stroke: "#8f8f8f",r: 1,style: {visibility: "hidden",},},},},},
};export const antvX6TypeConfig = (width, headerWidth) => {Graph.registerNode("lane-contanier",{inherit: "rect",markup: [{tagName: "rect",selector: "body",},{tagName: "rect",selector: "name-rect",},{tagName: "text",selector: "name-text",},],attrs: {body: {fill: "#FFF",stroke: "#5F95FF",strokeWidth: 1,},"name-rect": {width: headerWidth,height: 40,fill: "#5F95FF",stroke: "#fff",strokeWidth: 1,x: -1,},"name-text": {ref: "name-rect",refY: 0.5,refX: 0.5,textAnchor: "middle",fontWeight: "bold",fill: "#fff",fontSize: 18,},},},true);Graph.registerNode("lane",{inherit: "rect",markup: [{tagName: "rect",selector: "body",},{tagName: "rect",selector: "name-rect",},{tagName: "text",selector: "name-text",},],attrs: {body: {fill: "#FFF",stroke: "#5F95FF",strokeWidth: 1,},"name-rect": {width: width,height: 40,fill: "#5F95FF",stroke: "#fff",strokeWidth: 1,x: -1,},"name-text": {ref: "name-rect",refY: 0.5,refX: 0.5,textAnchor: "middle",fontWeight: "bold",fill: "#fff",fontSize: 18,},},},true);Graph.registerNode("lane-rect",{inherit: "rect",width: 100,height: 60,attrs: {body: {strokeWidth: 1,stroke: "#5F95FF",fill: "#EFF4FF",},text: {fontSize: 18,fill: "#262626",},},ports: {...groupMock,items: [{ id: "top-1", group: "top" },{ id: "left-1", group: "left" },{ id: "right-1", group: "right" },{ id: "bottom-1", group: "bottom" },{ id: "bottom-1R", group: "bottomR" },],},},true);Graph.registerNode("lane-polygon",{inherit: "polygon",width: 80,height: 80,attrs: {body: {strokeWidth: 1,stroke: "#5F95FF",fill: "#EFF4FF",refPoints: "0,10 10,0 20,10 10,20",},text: {fontSize: 18,fill: "#262626",},},ports: {...groupMock,items: [{ id: "top-2", group: "top" },{ id: "left-2", group: "left" },{ id: "right-2", group: "right" },{ id: "bottom-2", group: "bottom" },],},},true);Graph.registerEdge("lane-edge",{inherit: "edge",attrs: {line: {stroke: "#8A9EB8",strokeWidth: 2,},},label: {attrs: {label: {fill: "#8A9EB8",fontSize: 18,},},},},true);Graph.registerEdge("lane-edge-revoke",{inherit: "edge",attrs: {line: {stroke: "#E96F0C",strokeWidth: 2,},},label: {attrs: {label: {fill: "#E96F0C",fontSize: 18,},},},},true);
}// 盒子 - 流程已经过
const success_box = {attrs: {body: {fill: "#5cbf77",stroke: "#5cbf77",strokeWidth: 1},text: {fill: "#262626",lineHeight: 24,fontSize: 18,}}
}
// 盒子 - 未经过
const never_passed_box = {attrs: {body: {fill: "#f56c6c",stroke: "#f56c6c",strokeWidth: 1},text: {fill: "#262626",lineHeight: 24,fontSize: 18,}}
}
// 盒子 - 流程到达
const success_box_active = {attrs: {body: {fill: "#ffc000",stroke: "#ffc000",strokeWidth: 1},text: {fill: "#262626",lineHeight: 24,fontSize: 18,}}
}
// 盒子 - 跳过
const success_box_skip = {attrs: {body: {fill: "#bfbfbf",stroke: "#bfbfbf",strokeWidth: 1},text: {fill: "#262626",lineHeight: 24,fontSize: 18,}}
}
// 线条 - 流程已经过 - 是
const success_line = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "是",fill: "#5cbf77",stroke: "#5cbf77",fontSize: 18,}}}],attrs: {line: {stroke: "#5cbf77",strokeWidth: 2}},label: {attrs: {label: {text: "是",fill: "#5cbf77",fontSize: 18,},},},
}
// 线条 - 流程未经过 - 是
const never_passed_line = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "是",fill: "#f56c6c",stroke: "#f56c6c",fontSize: 18,}}}],attrs: {line: {stroke: "#f56c6c",strokeWidth: 2}},label: {attrs: {label: {text: "是",fill: "#f56c6c",fontSize: 18,},},},
}
// 线条 - 流程跳过 - 是
const success_line_skip = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "是",fill: "#bfbfbf",stroke: "#bfbfbf",fontSize: 18,}}}],attrs: {line: {stroke: "#bfbfbf",strokeWidth: 2}},label: {attrs: {label: {text: "是",fill: "#bfbfbf",fontSize: 18,},},},
}
// 线条 - 流程跳过 - 否
const success_line_skip_no = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "否",fill: "#bfbfbf",stroke: "#bfbfbf",fontSize: 18,}}}],attrs: {line: {stroke: "#bfbfbf",strokeWidth: 2}},label: {attrs: {label: {text: "否",fill: "#bfbfbf",fontSize: 18,},},},
}
// 线条 - 流程已经过-否字
const success_line_no = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "否",fill: "#5cbf77",stroke: "#5cbf77",fontSize: 18,}}}],attrs: {line: {stroke: "#5cbf77",strokeWidth: 2}},label: {attrs: {label: {text: "否",fill: "#5cbf77",fontSize: 18,},},},
}
// 线条 - 未经过-否字
const never_passed_line_no = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "否",fill: "#f56c6c",stroke: "#f56c6c",fontSize: 18,}}}],attrs: {line: {stroke: "#f56c6c",strokeWidth: 2}},label: {attrs: {label: {text: "否",fill: "#f56c6c",fontSize: 18,},},},
}
// 线条 - 流程已经过 - 无文字
const success_line_no_text = {attrs: {line: {stroke: "#5cbf77",strokeWidth: 2}},
}
// 线条 - 流程未经过 - 无文字
const never_passed_line_no_text = {attrs: {line: {stroke: "#f56c6c",strokeWidth: 2}},
}
// 线条 - 流程跳过 - 无文字
const success_line_skip_no_text = {labels: [{position: 0.5, // 沿路径50%位置attrs: {text: {text: "",fill: "#bfbfbf",stroke: "#bfbfbf",fontSize: 18,}}}],attrs: {line: {stroke: "#bfbfbf",strokeWidth: 2}},label: {attrs: {label: {text: "",fill: "#bfbfbf",fontSize: 18,},},},
}export const dataMock = (node, flowLogRepVOS, width = 300, height = 800) => {return [{id: "1",shape: "lane",width: width,height: height,position: {x: 60,y: 40,},label: "节点1",}, {id: "2",shape: "lane",width: width,height: height,position: {x: 60 + width,y: 40,},label: "节点2",}, {id: "3",shape: "lane",width: width,height: height,position: {x: 60 + width * 2,y: 40,},label: "节点3",}, {id: "4",shape: "lane",width: width,height: height,position: {x: 60 + width * 3,y: 40,},label: "节点4",}, {id: "5",shape: "lane",width: width,height: height,position: {x: 60 + width * 4,y: 40,},label: "节点5",}, {id: "6",shape: "lane",width: width,height: height,position: {x: 60 + width * 5,y: 40,},label: "节点6",}, {id: "7",shape: "lane",width: width,height: height,position: {x: 60 + width * 6,y: 40,},label: "节点7\n(审批)",}, {id: "8",shape: "lane",width: width,height: height,position: {x: 60 + width * 7,y: 40,},label: "节点8",}, {id: "9",shape: "lane-rect",width: 100,height: 50,position: {x: 190,y: 120,},label: "开始",attrs: {body: {rx: 30,ry: 20,fill: "#5cbf77",},},parent: "1",}, {id: "10", // 10shape: "lane-rect",width: 310,height: 120,position: {x: 85,y: 260},label: "提交",...(node >= 10 && node < 20 ? success_box_active : node >= 20 ? success_box : never_passed_box),}, {id: "11", // 20shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width,y: 260},label: "审批",...(node >= 20 && node < 30 ? success_box_active : node >= 30 ? success_box : never_passed_box),}, {id: "12", // 20+shape: "lane-polygon",width: 240,height: 80,position: {x: 125 + width,y: 480},label: '审批是否通过',...(node >= 30 ? success_box : never_passed_box),}, {id: "13", // 30shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width * 2,y: 260},label: "审批",...(node >= 30 && node < 40 ? success_box_active : node >= 40 ? success_box : never_passed_box),}, {id: "14", // 30+shape: "lane-polygon",width: 240,height: 80,position: {x: 125 + width * 2,y: 480},label: '审批是否通过',...(node >= 40 ? success_box : never_passed_box),}, {id: "15", // 40shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width * 3,y: 260},label: "审批",...(node >= 40 && node < 50 ? success_box_active : node >= 50 ? success_box : never_passed_box),}, {id: "16", // 40+shape: "lane-polygon",width: 240,height: 80,position: {x: 125 + width * 3,y: 480},label: '审批是否通过',...(node >= 50 ? success_box : never_passed_box),}, {id: "17", // 50shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width * 4,y: 260},label: "审批",...(node >= 50 && node < 60 ? success_box_active : node >= 60 ? success_box : never_passed_box),}, {id: "18", // 50+shape: "lane-polygon",width: 240,height: 80,position: {x: 125 + width * 4,y: 480},label: '审批是否通过',...(node >= 60 ? success_box : never_passed_box),}, {id: "20", // 60shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width * 5,y: 260},label: "审批",...(node >= 60 && node < 70 ? success_box_active : node >= 70 && flowLogRepVOS.some(x => x.node > 59 && x.node < 70) ? success_box : node >= 70 && !flowLogRepVOS.some(x => x.node > 59 && x.node < 70) ? success_box_skip : never_passed_box),}, {id: "21", // 60+shape: "lane-polygon",width: 240,height: 80,position: {x: 125 + width * 5,y: 480},label: '审批是否通过',...(node >= 70 && flowLogRepVOS.some(x => x.node > 59 && x.node < 70) ? success_box : node >= 70 && !flowLogRepVOS.some(x => x.node > 59 && x.node < 70) ? success_box_skip : never_passed_box),}, {id: "22", // 70shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width * 6,y: 760},label: "抄送",...(node >= 70 && node < 80 ? success_box_active : node >= 80 ? success_box : never_passed_box),}, {id: "23", // 80shape: "lane-rect",width: 240,height: 120,position: {x: 125 + width * 7,y: 760},label: "抄送",...(node >= 80 && node < 90 ? success_box_active : node >= 90 ? success_box : never_passed_box),}, {id: "24",shape: "lane-rect",width: 100,height: 50,position: {x: 195 + width * 7,y: 960},label: "结束",attrs: {body: {rx: 30,ry: 20,},},attrs: {body: {rx: 30,ry: 20,fill: node > 100 ? "#5cbf77" : '#f56c6c',}},}, {id: "25",shape: "lane-edge",source: { cell: "9", port: "bottom-2" },target: { cell: "10", port: "top-1" },...success_line_no_text,}, {id: "26",shape: "lane-edge",source: { cell: "10", port: "right-2" },target: { cell: "11", port: "left-1" },...(node >= 20 ? success_line_no_text : never_passed_line_no_text),}, {id: "27",shape: "lane-edge",source: { cell: "11", port: "bottom-2" },target: { cell: "12", port: "top-1" },...(node >= 30 ? success_line_no_text : never_passed_line_no_text),}, {id: "28",shape: "lane-edge",source: { cell: "12", port: "right-2" },target: { cell: "13", port: "left-1" },...(node >= 30 ? success_line : never_passed_line),vertices: [{ x: 180 + width * 1.6, y: 500 }],}, {id: "29",shape: "lane-edge",source: { cell: "12", port: "bottom-2" },target: { cell: "10", port: "bottom-1" },label: "否",vertices: [{ x: 200 + width * 1.1, y: 620 }],}, {id: "30",shape: "lane-edge",source: { cell: "13", port: "bottom-2" },target: { cell: "14", port: "top-1" },...(node >= 40 ? success_line_no_text : never_passed_line_no_text),}, {id: "31",shape: "lane-edge",source: { cell: "14", port: "right-2" },target: { cell: "15", port: "left-1" },...(node >= 40 ? success_line : never_passed_line),vertices: [{ x: 180 + width * 2.6, y: 500 }],}, {id: "32",shape: "lane-edge",source: { cell: "14", port: "bottom-2" },target: { cell: "10", port: "bottom-1" },label: "否",vertices: [{ x: 200 + width * 2.1, y: 620 }],}, {id: "33",shape: "lane-edge",source: { cell: "15", port: "bottom-2" },target: { cell: "16", port: "top-1" },...(node >= 50 ? success_line_no_text : never_passed_line_no_text),}, {id: "34",shape: "lane-edge",source: { cell: "16", port: "right-2" },target: { cell: "17", port: "left-1" },...(node >= 50 ? success_line : never_passed_line),vertices: [{ x: 180 + width * 3.6, y: 500 }],}, {id: "35",shape: "lane-edge",source: { cell: "16", port: "bottom-2" },target: { cell: "10", port: "bottom-1" },label: "否",vertices: [{ x: 200 + width * 2.1, y: 620 }],}, {id: "36",shape: "lane-edge",source: { cell: "17", port: "bottom-2" },target: { cell: "18", port: "top-1" },...(node >= 60 ? success_line_no_text : never_passed_line_no_text),}, {id: "37",shape: "lane-edge",source: { cell: "18", port: "bottom-2" },target: { cell: "10", port: "bottom-1" },label: "否",vertices: [{ x: 200 + width * 4.1, y: 620 }],}, {id: "38",shape: "lane-edge",source: { cell: "18", port: "right-2" },target: { cell: "20", port: "top-2" },...(node >= 60 ? success_line : never_passed_line),vertices: [{ x: 200 + width * 4.55, y: 500 }],}, {id: "40",shape: "lane-edge",source: { cell: "20", port: "bottom-2" },target: { cell: "21", port: "top-1" },...(node >= 70 ? success_line_no_text : never_passed_line_no_text),}, {id: "41",shape: "lane-edge",source: { cell: "21", port: "bottom-2" },target: { cell: "10", port: "bottom-1" },label: "否",vertices: [{ x: 200 + width * 5.1, y: 620 }],}, {id: "43",shape: "lane-edge",source: { cell: "21", port: "right-2" },target: { cell: "22", port: "left-1" },...(node >= 70 ? success_line : never_passed_line),vertices: [{ x: 200 + width * 5.5, y: 820 }],}, {id: "44",shape: "lane-edge",source: { cell: "22", port: "right-2" },target: { cell: "23", port: "left-1" },...(node >= 80 ? success_line_no_text : never_passed_line_no_text),}, {id: "45",shape: "lane-edge",source: { cell: "23", port: "bottom-2" },target: { cell: "24", port: "top-1" },...(node >= 100 ? success_line_no_text : never_passed_line_no_text),},]
}

1.3.2 x6.vue

<template><div id="flowContainer"></div>
</template><script setup name="setup">
import { onMounted, ref, nextTick } from 'vue'
import { Graph } from "@antv/x6";
import { antvX6TypeConfig, dataMock } from "./x6Config.js";const initGraph = (node,flowLogRepVOS
) => {antvX6TypeConfig(366, 2700)const graph = new Graph({container: document.getElementById("flowContainer"),height: 950,connecting: {router: "orth",},interacting: { nodeMovable: false, edgeMovable: false },translating: {restrict (cellView) {const cell = cellView.cell;const parentId = cell.prop("parent");if (parentId) {const parentNode = graph.getCellById(parentId);if (parentNode) {return parentNode.getBBox().moveAndExpand({x: 0,y: 30,width: 0,height: -30,});}}return cell.getBBox();},},});// 添加节点事件监听graph.on('node:click', ({ node }) => {console.log('节点被点击:', node.id);// 可以在这里添加自定义点击逻辑});graph.on('node:dblclick', ({ node }) => {console.log('节点被双击:', node.id);});graph.on('node:mouseenter', ({ node }) => {node.attr('body/stroke', '#ff0000'); // 鼠标移入高亮边框});graph.on('node:mouseleave', ({ node }) => {node.attr('body/stroke', '#5F95FF'); // 鼠标移出恢复边框});graph.on('node:contextmenu', ({ e, node }) => {e.preventDefault(); // 阻止默认右键菜单console.log('节点右键点击:', node.id);// 可以在这里显示自定义右键菜单});// 其他可用事件graph.on('node:selected', ({ node }) => {console.log('节点被选中:', node.id);});graph.on('node:unselected', ({ node }) => {console.log('节点取消选中:', node.id);});Promise.resolve(dataMock(node, flowLogRepVOS, 366, 1000)).then((data) => {const cells = [];data.forEach((item) => {if (item.shape.includes("lane-edge")) {cells.push(graph.createEdge(item));} else {cells.push(graph.createNode(item));}});graph.resetCells(cells);graph.zoomToFit({ padding: 10, maxScale: 1 });nextTick(() => {let designWidth = 1920 - document.body.offsetWidth < 10 && document.body.offsetWidth < 1920 ? document.body.offsetWidth : 1920; //设计稿的宽度,根据实际项目调整let dom = document.querySelector("#flowContainer");if (dom) {graph.translate(-28 * (dom.clientWidth / designWidth), 0);}})});
};onMounted(() => {initGraph(40, [{ node: 10 }])
})// ------- 流程配置 --------</script><style scoped lang="less"></style>
http://www.dtcms.com/a/393797.html

相关文章:

  • markdown 绘制流程图
  • Spark专题-第二部分:Spark SQL 入门(5)-算子介绍-Join
  • 平替Jira,推荐一款国产开源免费的项目管理工具-Kanass
  • ssh不用版本管理器为多个服务器添加密钥
  • Windows Docker Desktop 实战:大模型存入 docker-desktop 实例 home 目录并与 Dify 联动运行指南
  • linux驱动开发笔记
  • 阿里云与腾讯云产品操作与体验:云平台运维实战技术解析
  • 深入了解linux网络—— 网络基础
  • leetcode3 哈希
  • Spring AI 整合OpenAI 聊天、做图
  • 阿里Motionshop人物角色替换成3D虚拟形象
  • C语言自学--字符函数和字符串函数
  • spring-boot--邮箱验证码发送--spring-boot-starter-mail
  • 3ds Max 2026安装教程(附安装包)3ds Max 2026下载详细安装图文教程
  • Genie 2:Google DeepMind 推出的基础世界模型,单张图生成 1分钟可玩 3D 世界
  • LeetCode 104. 二叉树的最大深度
  • 欧拉角描述相机的运动
  • Unity2D-Spriteshape
  • 打工人日报#20250921
  • Coolmuster Android Assistant:Windows系统下的Android设备管理专家
  • Android 的多进程机制 (Android Multi-Process Model)
  • 2025研究生数学建模通用神经网络处理器下的核内调度问题草案
  • Spring Boot 4 新特性详解:5大核心更新助力企业级开发
  • 计算机网络经典问题透视:网络利用率和网络时延之间,究竟存在着怎样一种“爱恨交织”的关系?我们梦寐以求的100%网络利用率,在现实世界中真的能够实现吗
  • requests 和 lxml 库的xpath实现
  • 前端梳理体系从常问问题去完善-工程篇(webpack,vite)
  • Go语言在K8s中的核心优势
  • 旅游门票预订系统支持微信小程序+H5
  • Requests 网络请求:Python API 交互与数据获取
  • 基于Dify实现简历自动筛选过滤