vue3+vite+Ts 基于Antv/x6 绘制流程图
需求效果:



需求:
实现一个流程图,双击可对相应的组件进行一些功能操作;
工具栈:
这里使用@antv/x6, 基于vue3+vite+ts进行开发
官网地址:
https://x6.antv.antgroup.com/examples/showcase/practices/#flowchart
代码:
<template><div id="container"><router-view /></div>
</template><script lang="ts" setup>
import { Graph, Shape } from '@antv/x6'
import { Stencil } from '@antv/x6-plugin-stencil'
import { Transform } from '@antv/x6-plugin-transform'
import { Selection } from '@antv/x6-plugin-selection'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Clipboard } from '@antv/x6-plugin-clipboard'
import { History } from '@antv/x6-plugin-history'
import insertCss from 'insert-css'import { useRouter } from "vue-router"// 引入本地图片
const getImageUrl = (url: any) => {return new URL(url, import.meta.url).href;
}const router = useRouter()onMounted(() => {// 为了协助代码演示preWork()// #region 初始化画布const graph = new Graph({container: document.getElementById('graph-container')!,grid: true,mousewheel: {enabled: true,zoomAtMousePosition: true,modifiers: 'ctrl',minScale: 0.5,maxScale: 3,},connecting: {router: 'manhattan',connector: {name: 'rounded',args: {radius: 8,},},anchor: 'left',connectionPoint: 'anchor',allowBlank: false,snap: {radius: 20,},createEdge() {return new Shape.Edge({attrs: {line: {stroke: '#A2B1C3',strokeWidth: 2,targetMarker: {name: 'block',width: 12,height: 8,},},},zIndex: 0,//....})},validateConnection({ targetMagnet }) {return !!targetMagnet},},highlighting: {magnetAdsorbed: {name: 'stroke',args: {attrs: {fill: '#5F95FF',stroke: '#5F95FF',},},},},})// #endregion// #region 使用插件graph.use(new Transform({resizing: true,rotating: true,}),).use(new Selection({rubberband: true,showNodeSelectionBox: true,}),).use(new Snapline()).use(new Keyboard()).use(new Clipboard()).use(new History())// #endregion// #region 初始化 stencilconst stencil = new Stencil({title: '流程图',target: graph,stencilGraphWidth: 300,stencilGraphHeight: 180,collapsable: true,groups: [{title: '输入',name: 'group2',graphHeight: 300,layoutOptions: {rowHeight: 70,},},{title: '输出',name: 'group3',graphHeight: 0,layoutOptions: {rowHeight: 70,},},{title: '转换组件',name: 'group1',graphHeight: 300,layoutOptions: {rowHeight: 70,},},],layoutOptions: {columns: 2,columnWidth: 80,rowHeight: 55,},})document.getElementById('stencil')!.appendChild(stencil.container)// #endregion// #region 快捷键与事件graph.bindKey(['meta+c', 'ctrl+c'], () => {const cells = graph.getSelectedCells()if (cells.length) {graph.copy(cells)}return false})graph.bindKey(['meta+x', 'ctrl+x'], () => {const cells = graph.getSelectedCells()if (cells.length) {graph.cut(cells)}return false})graph.bindKey(['meta+v', 'ctrl+v'], () => {if (!graph.isClipboardEmpty()) {const cells = graph.paste({ offset: 32 })graph.cleanSelection()graph.select(cells)}return false})// undo redograph.bindKey(['meta+z', 'ctrl+z'], () => {if (graph.canUndo()) {graph.undo()}return false})graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {if (graph.canRedo()) {graph.redo()}return false})// select allgraph.bindKey(['meta+a', 'ctrl+a'], () => {const nodes = graph.getNodes()if (nodes) {graph.select(nodes)}})// deletegraph.bindKey('backspace', () => {const cells = graph.getSelectedCells()if (cells.length) {graph.removeCells(cells)}})// zoomgraph.bindKey(['ctrl+1', 'meta+1'], () => {const zoom = graph.zoom()if (zoom < 1.5) {graph.zoom(0.1)}})graph.bindKey(['ctrl+2', 'meta+2'], () => {const zoom = graph.zoom()if (zoom > 0.5) {graph.zoom(-0.1)}})// 控制连接桩显示/隐藏const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {for (let i = 0, len = ports.length; i < len; i += 1) {ports[i].style.visibility = show ? 'visible' : 'hidden'}}graph.on('node:mouseenter', () => {const container = document.getElementById('graph-container')!const ports = container.querySelectorAll('.x6-port-body',) as NodeListOf<SVGElement>showPorts(ports, true)})graph.on('node:mouseleave', () => {const container = document.getElementById('graph-container')!const ports = container.querySelectorAll('.x6-port-body',) as NodeListOf<SVGElement>showPorts(ports, false)})// #endregion// #region 初始化图形const ports = {groups: {top: {position: 'top',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},right: {position: 'right',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},bottom: {position: 'bottom',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},left: {position: 'left',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},},items: [{group: 'top',},{group: 'right',},{group: 'bottom',},{group: 'left',},],}Graph.registerNode('custom-rect',{inherit: 'rect',width: 66,height: 36,attrs: {body: {strokeWidth: 1,stroke: '#5F95FF',fill: '#EFF4FF',},text: {fontSize: 12,fill: '#262626',},},ports: { ...ports },},true,)Graph.registerNode('custom-polygon',{inherit: 'polygon',width: 66,height: 36,attrs: {body: {strokeWidth: 1,stroke: '#5F95FF',fill: '#EFF4FF',},text: {fontSize: 12,fill: '#262626',},},ports: {...ports,items: [{group: 'top',},{group: 'bottom',},],},},true,)Graph.registerNode('custom-circle',{inherit: 'circle',width: 45,height: 45,attrs: {body: {strokeWidth: 1,stroke: '#5F95FF',fill: '#EFF4FF',},text: {fontSize: 12,fill: '#262626',},},ports: { ...ports },},true,)Graph.registerNode('custom-image',{inherit: 'rect',width: 65,height: 60,markup: [{tagName: 'rect',selector: 'body',},{tagName: 'image',},{tagName: 'text',selector: 'label',},],attrs: {body: {stroke: '#ccc',fill: '#ccc', //..},image: {width: 28,height: 28,refX: 20,refY: 12,},label: {refX: 1,refY: 48,textAnchor: 'bottom',textVerticalAnchor: 'top',fontSize: 12,fill: '#fff',},},ports: {groups: {top: {position: 'top',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},right: {position: 'right',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},bottom: {position: 'bottom',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},left: {position: 'left',attrs: {circle: {r: 4,magnet: true,stroke: '#5F95FF',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},},},},true,)//转换组件const convertDataList = [{label: '添加默认值',path: "addDefault",image: getImageUrl("../../assets/tool_image/information_add.svg"),},{label: '基本运算',path: "basicoPerations",image:getImageUrl("../../assets/tool_image/calculator.svg"),},{label: '脚本转换',path: "conversionScript",image:getImageUrl("../../assets/tool_image/script.svg"),},{label: '合并/拆分',path: "mergeSplit",image:getImageUrl("../../assets/tool_image/merge.svg"),},{label: '数字类型',path: "numberType",image:getImageUrl("../../assets/tool_image/script_1.svg"),},{label: '简单过滤',path: "simpleFiltering",image:getImageUrl("../../assets/tool_image/filter-records-fill.svg"),},{label: '时间格式',path: "timeConversion",image:getImageUrl("../../assets/tool_image/Date_time.svg"),},]const convertNodes = convertDataList.map((item) =>graph.createNode({shape: 'custom-image',label: item.label,data: item.path,attrs: {image: {'xlink:href': item.image,}},ports: {items: [{group: 'right',},{group: 'left',},],}}),graph.on('node:dblclick', ({ cell }) => { // cell 基类对象 view 视图对象// 目标数据logicrouter.push({name: cell.data,query: {}})}))stencil.load(convertNodes, 'group1')//endconst imageShapes = [{label: 'Excel',path: "Excel",image:getImageUrl("../../assets/tool_image/file-excel.svg"),},{label: 'CSV',path: "CSV",image:getImageUrl("../../assets/tool_image/CSV.svg"),},{label: 'JSON',path: "JSON",image:getImageUrl("../../assets/tool_image/json_1.svg"),},{label: 'Kafka',path: "Kafka",image:getImageUrl("../../assets/tool_image/Kafka_1.svg"),},{label: 'OWL文件',path: "OWL",image:getImageUrl("../../assets/tool_image/calculator_1.svg"),},{label: '日志文件',path: "Log",image:getImageUrl("../../assets/tool_image/console2.svg"),},{label: 'mongodb',path: "mongodb",image:getImageUrl("../../assets/tool_image/yunshujukuMongoDB.svg"),},{label: '表输入',path: "outside",image:getImageUrl("../../assets/tool_image/database.svg"),},]const imageNodes = imageShapes.map((item) =>graph.createNode({shape: 'custom-image',label: item.label,data: item.path,attrs: {image: {'xlink:href': item.image,},},ports: {items: [{group: 'right',},],}}),graph.on('node:dblclick', ({ cell }) => { // cell 基类对象 view 视图对象// 目标数据logicconsole.log(cell.data, 'aaa')router.push({name: cell.data,query: {}})}))stencil.load(imageNodes, 'group2')//outputconst outputDataList = [{label: '控制台',path: "consoleoutput",image:getImageUrl("../../assets/tool_image/console.svg"),},{label: 'Kafka',path: "kafkaoutput",image:getImageUrl("../../assets/tool_image/Kafka.svg"),},{label: 'mongodb',path: "mongodboutput",image:getImageUrl("../../assets/tool_image/mongo.svg"),},{label: '表',path: "tableOut",image:getImageUrl("../../assets/tool_image/database.svg"),}]const outputNodes = outputDataList.map((item) =>graph.createNode({shape: 'custom-image',label: item.label,data: item.path,attrs: {image: {'xlink:href': item.image,},},ports: {items: [{group: 'left',},],}}),graph.on('node:dblclick', ({ cell }) => { // cell 基类对象 view 视图对象// 目标数据logicconsole.log(cell.data, 'aaa')router.push({name: cell.data,query: {}})}))stencil.load(outputNodes, 'group3')// #endregionfunction preWork() {// 这里协助演示的代码,在实际项目中根据实际情况进行调整const container = document.getElementById('container')!const stencilContainer = document.createElement('div')stencilContainer.id = 'stencil'const graphContainer = document.createElement('div')graphContainer.id = 'graph-container'container.appendChild(stencilContainer)container.appendChild(graphContainer)insertCss(`#container {display: flex;border: 1px solid #dfe3e8;}#stencil {width: 200px;height: 100%;position: relative;border-right: 1px solid #dfe3e8;}#graph-container {width: calc(100% - 180px);height: 100%;}.x6-widget-stencil {background-color: #fff;}.x6-widget-stencil-title {background-color: #fff;}.x6-widget-stencil-group-title {background-color: #fff !important;}.x6-widget-transform {margin: -1px 0 0 -1px;padding: 0px;border: 1px solid #239edd;}.x6-widget-transform > div {border: 1px solid #239edd;}.x6-widget-transform > div:hover {background-color: #3dafe4;}.x6-widget-transform-active-handle {background-color: #3dafe4;}.x6-widget-transform-resize {border-radius: 0;}.x6-widget-selection-inner {border: 1px solid #239edd;}.x6-widget-selection-box {opacity: 0;}`)}
})</script><style scoped lang="less">
#container {width: 100%;height: 100vh;overflow: hidden !important;display: flex;justify-content: center;
}
</style>
相关文章:
vue3+vite+Ts 基于Antv/x6 绘制流程图
需求效果: 需求: 实现一个流程图,双击可对相应的组件进行一些功能操作; 工具栈: 这里使用antv/x6, 基于vue3vitets进行开发 官网地址: https://x6.antv.antgroup.com/examples/showcase/pra…...
sys V 进程间通信之消息队列
note msgget获取内核消息队列实例 msgctl可以删除内核消息队列实例 msgsnd\msgrcv函数注意struct msgbuf字段的填充 code #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include &l…...
Android 报错,闪退(错误)日志保存到手机内存中,以文本文件的形式保存
1.直接贴代码 import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Environment; import android.util.Log;import com.nuotu.atmBookClient.App;import java.io.File; i…...
flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel
flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel 在之前实现了flutter聊天界面的自定义表情的展示,这里记录一下更多操作展开的相机、相册等操作功能实现。 一、查看效果 更多操作展开的相机、相册等操作功能实现。 二、代码实现 展开的操作按钮可能比…...
浅析泵站自动化技术的发展趋势
摘要:基于泵站对我国水利及水务事业的重要性,文章以城市供水行业大型泵站为对象,分析了泵站自动化技术 发展现状,结合泵站自动化技术的发展需求,从管控一体化、系统自诊断、运行信息实时化管理等方面展望了泵 站自动化…...
5.4.1 虚拟专用网VPN
5.4.1 虚拟专用网VPN 我们已经学习了因特网的路由协议(5.3.1 因特网的路由协议(一)、5.3.2 因特网的路由协议(二)基于距离向量算法的RIP协议、5.3.3 因特网的路由协议(三)OSPF协议、5.3.4 因特…...
第42节:cesium 火焰效果(含源码+视频)
结果示例: 完整源码: <template><div class="viewer"><!-- :shouldAnimate="true" 添加动画 --><vc-viewer @ready...
MySQL基础篇第5章(排序与分页)
文章目录 1、排序1.1 排序规则1.2 单列排序1.3 多列排序 2、分页2.1 背景2.2 实现规则2.3 拓展 1、排序 1.1 排序规则 使用 ORDER BY 子句排序 ASC(ascend): 升序DESC(descend):降序 ORDER BY 子句在SELECT语句的结尾。 1.2 …...
LeetCode解法汇总2679. 矩阵中的和
目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣 描述: 给你一个下标从 0 开始的二维整数数组 nums 。一开始你的分数为 0 。你需要执行…...
flask-apscheduler实现定时任务
秋风阁-北溪入江流 flask-apscheduler是一个支持apscheduler的flask插件,通过flask-apscheduler可以很方便的定义定时任务。 安装flask-apscheduler pip install flask-apschedulerflask-apscheduler组件 apscheduler库包含有组件: triggers…...
FPGA纯verilog实现UDP协议栈,sgmii接口SFP光口收发,提供工程源码和技术支持
目录 1、前言2、我这里已有的UDP方案3、该UDP协议栈性能4、详细设计方案SFPGMII AXIS接口模块AXIS FIFOUDP协议栈1G/2.5G Ethernet PCS/PMA or SGMII 5、vivado工程详解6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利:工程代码的获取 1、前言 目前网…...
【Python入门系列】第十五篇:Python数据可视化和图表绘制
文章目录 前言一、可视化与绘图常用库二、Matplotlib1、折线图2、散点图3、柱状图: 三、Seaborn1、散点图2、箱线图3、小提琴图4、热力图 四、Plotly1、折线图2、散点图3、条形图 总结 前言 数据可视化是数据分析和数据科学中非常重要的一部分。通过可视化…...
isBlank函数和isEmpty函数的区别
StrUtil.isBlank(CharSequence):该方法用于判断字符串是否为空或仅包含空白字符。如果传入的字符串为null、空字符串(“”)或仅包含空白字符(如空格、制表符、换行符等),则返回true;否则返回false。 StrUtil.isEmpty(…...
「SQL面试题库」 No_121 The Most Recent Three Orders
🍅 1、专栏介绍 「SQL面试题库」是由 不是西红柿 发起,全员免费参与的SQL学习活动。我每天发布1道SQL面试真题,从简单到困难,涵盖所有SQL知识点,我敢保证只要做完这100道题,不仅能轻松搞定面试࿰…...
【计算机视觉 | 目标检测 | 图像分割】arxiv 计算机视觉关于目标检测和图像分割的学术速递(7 月 7 日论文合集)
文章目录 一、检测相关(5篇)1.1 Contextual Affinity Distillation for Image Anomaly Detection1.2 Noise-to-Norm Reconstruction for Industrial Anomaly Detection and Localization1.3 MMNet: Multi-Collaboration and Multi-Supervision Network for Sequential Deepfake…...
直流运算放大器-----仪表放大器(三)
详见 模拟电子技术基础 6.4.1 电路图 计算公式 仿真 电流流向...
【Zookeeper】终端操作常用命令
文章目录 服务端常用命令客户端常用命令 zookeeper版本3.7.1 Zookeeper是一个开源的分布式协调服务。 Zookeeper是Apache软件基金会的一个项目,它提供了分布式应用程序协调的通用服务,如分布式同步、命名服务、集群维护等,以简化分布式应用协…...
leetcode 1110. 删点成林
给出二叉树的根节点 root,树上每个节点都有一个不同的值。 如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。 返回森林中的每棵树。你可以按任意顺序组织答案…...
华为Harmony应用开发初探
HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。在传统的单设备系统能力基础上,HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备,提供全场景(移动办公、运动健康、社交通信、媒体…...
电脑应用程序发生异常怎么办?
有时候我们打开电脑上面的某个软件时,会打不开,并且会弹出如下的错误提示“应用程序发生异常 未知的软件异常(xxx),位置为xx”。相信大多数的人在使用电脑的时…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
