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

vue3中使用Antv G6渲染树形结构并支持节点增删改

写在前面

在一些管理系统中,会对组织架构、级联数据等做一些管理,你会怎么实现呢?在经过调研很多插件之后决定使用 Antv G6 实现,文档也比较清晰,看看怎么实现吧,先来看看效果图。点击在线体验

效果图
实现的功能有:

  1. 增加节点
  2. 删除节点
  3. 编辑节点
  4. 展开收起

具体实现

  1. 先在项目中安装 antv g6
npm install --save @antv/g6
  1. vue 文件创建容器渲染
  • 渲染的容器
<div id="container" class="one-tree"></div>
  • 渲染方法和初始化树图
import G6 from '@antv/g6'const state = reactive({treeData: {id: 'root',sname: 'root',name: uniqueId(),children: [],},graph: null,
})function renderMap(data: any[], graph: Graph): void {G6.registerNode('icon-node',{options: {size: [60, 20],stroke: '#73D13D',fill: '#fff'},draw(cfg: any, group: any) {const styles = (this as any).getShapeStyle(cfg)const { labelCfg = {} } = cfgconst w = cfg.size[0]const h = cfg.size[1]const keyShape = group.addShape('rect', {attrs: {...styles,cursor: 'pointer',x: 0,y: 0,width: w, // 200,height: h, // 60fill: cfg.style.fill || '#fff'},name: 'node-rect',draggable: true})// 动态增加和删除元素group.addShape('text', {attrs: {x: 131,y: 20,r: 6,stroke: '#707070',cursor: 'pointer',opacity: 1,fontFamily: 'iconfont',textAlign: 'center',textBaseline: 'middle',text: '\ue658',fontSize: 16},name: 'add-item'})// 删除icon,根元素不能删除if (cfg.id !== 'root') {group.addShape('text', {attrs: {x: 110,y: 20,r: 6,fontFamily: 'iconfont',textAlign: 'center',textBaseline: 'middle',text: '\ue74b',fontSize: 14,stroke: '#909399',cursor: 'pointer',opacity: 0},name: 'remove-item'})}if (cfg.sname) {group.addShape('text', {attrs: {...labelCfg.style,text: fittingString(cfg.sname, 110, 12),textAlign: 'left',x: 10,y: 25}})}// 展开收起if (cfg.children && cfg.children.length > 0) {group.addShape('circle', {attrs: {width: 24,height: 24,x: 154,y: 20,r: 12,cursor: 'pointer',lineWidth: 1,fill: !cfg.collapsed ? '#9e9e9e' : '#2196f3',opacity: 1,text: 1},name: 'collapse-icon'})group.addShape('text', {attrs: {...labelCfg.style,text: cfg.children.length,textAlign: 'left',x: 150,y: 25,fill: '#ffffff',fontWeight: 500,cursor: 'pointer'},name: 'collapse-icon'})}return keyShape},setState(name, value, item) {const group = item?.getContainer()if (name === 'collapsed') {const marker = item?.get('group').find((ele: any) => ele.get('name') === 'collapse-icon')const icon = value ? G6.Marker.expand : G6.Marker.collapsemarker.attr('symbol', icon)}if (name === 'selected') {const nodeRect = group?.find(function (e) {return e.get('name') === 'node-rect'})if (value) {nodeRect?.attr({stroke: '#2196f3',lineWidth: 2})}}if (name === 'hover') {const addMarker = group?.find(function (e) {return e.get('name') === 'add-item'})const reduceMarker = group?.find(function (e) {return e.get('name') === 'remove-item'})if (value) {addMarker?.attr({opacity: 1})reduceMarker?.attr({opacity: 1})}}},update: undefined},'rect')graph.data(data)graph.render()mouseenterNode(graph)mouseLeaveNode(graph)collapseNode(graph)
}function initGraph(graphWrapId: string): Graph {const width = (document.getElementById(graphWrapId) as HTMLElement).clientWidth || 1000const height = (document.getElementById(graphWrapId) as HTMLElement).clientHeight || 1000const graph = new G6.TreeGraph({container: graphWrapId,width,height,linkCenter: true,animate: false,fitView: false, // 自动调整节点位置和缩放,使得节点适应画布大小modes: {default: ['scroll-canvas'],edit: ['click-select']},defaultNode: {type: 'icon-node',size: [120, 40],style: defaultNodeStyle,labelCfg: defaultLabelCfg},defaultEdge: {type: 'cubic-vertical'},comboStateStyles,layout: defaultLayout})return graph
}
  • 事件处理
/*** @description:树型图的事件绑定*/// 展开收起子节点
function collapseNode(graph: Graph): void {// 展开和收起子节点graph.on('node:click', (e: any) => {if (e.target.get('name') === 'collapse-icon') {e.item.getModel().collapsed = !e.item.getModel().collapsedgraph.setItemState(e.item, 'collapsed', e.item.getModel().collapsed)graph.layout()}})
}// 鼠标滑入
function mouseenterNode(graph: Graph): void {graph.on('node:mouseover', (evt: any) => {const { item, target } = evtif (item._cfg.id === 'root') returnconst canHoverName = ['node-rect', 'remove-item']if (!canHoverName.includes(target.get('name'))) return// 显示iconconst deleteItem = item.get('group').find(function (el: any) {return el.cfg.name === 'remove-item'})deleteItem.attr('opacity', 1)if (item._cfg && item._cfg.keyShape) {item._cfg.keyShape.attr('stroke', '#2196f3')}graph.setItemState(item, 'active', true)})
}// 鼠标离开
function mouseLeaveNode(graph: Graph): void {graph.on('node:mouseout', (evt: any) => {const { item, target } = evtconst canHoverName = ['node-rect', 'remove-item']if (item._cfg.id === 'root') returnif (!canHoverName.includes(target.get('name'))) return// 隐藏iconconst deleteItem = item.get('group').find(function (el: any) {return el.cfg.name === 'remove-item'})deleteItem.attr('opacity', 0)if (item._cfg && item._cfg.keyShape) {item._cfg.keyShape.attr('stroke', '#fff')}graph.setItemState(item, 'active', false)})
}/*** @description 文本超长显示*/
const fittingString = (str: string, maxWidth: number, fontSize: number): string => {const ellipsis = '...'const ellipsisLength = Util.getTextSize(ellipsis, fontSize)[0]let currentWidth = 0let res = strconst pattern = new RegExp('[\u4E00-\u9FA5]+')str.split('').forEach((letter, i) => {if (currentWidth > maxWidth - ellipsisLength) returnif (pattern.test(letter)) {currentWidth += fontSize} else {currentWidth += Util.getLetterWidth(letter, fontSize)}if (currentWidth > maxWidth - ellipsisLength) {res = `${str.substr(0, i)}${ellipsis}`}})return res
}
  • 节点的增加、删除、编辑时间
const addEvent = (graph: any) => {graph.on('node:click', (evt: any) => {const { item, target } = evtconst name = target.get('name')// 增加元素const model = item.getModel()if (name === 'add-item') {state.editType = 'add'// 如果收起需要展开if (model.collapsed) model.collapsed = false// 没有子级的时候设置空数组if (!model.children) model.children = []const id = uniqueId()model.children.push({id,name: 1,sname: '',parentId: model.id,})graph.updateChild(model, model.id)const curTarget = graph.findDataById(id)const canvasXY = graph.getCanvasByPoint(curTarget.x, curTarget.y)state.editOne = curTargetstate.input = curTarget.snamesetTimeout(() => {state.showInput = truenextTick(() => {inputRref.value.focus()})}, 200)// 更改输入框的位置state.inputStyle = {left: `${canvasXY.x}px`,top: `${canvasXY.y}px`,}}// 删除节点if (name === 'remove-item') {graph.removeChild(model.id)// 查找当前的父id,更新其子元素的长度graph.updateItem(model.parentId, {})}// 编辑if (name === 'node-rect') {const curTarget = graph.findDataById(item._cfg.id)const canvasXY = graph.getCanvasByPoint(curTarget.x, curTarget.y)state.editOne = evt.itemstate.input = curTarget.snamestate.showInput = truestate.editType = 'edit'nextTick(() => {inputRref.value.focus()})state.inputStyle = {left: `${canvasXY.x}px`,top: `${canvasXY.y}px`,}}})// 画布滚动、拖动时,不能编辑节点名称graph.on('dragstart', () => {state.showInput = false})graph.on('wheel', () => {state.showInput = false})
}
  • dom 节点渲染后渲染树图
onMounted(() => {nextTick(() => {state.graph = initGraph('container')state.graph.clear()addEvent(state.graph)renderMap(state.treeData, state.graph)})
})

相关链接

  1. 源码链接
  2. Antv G6 官网
  3. 参考文章

相关文章:

vue3中使用Antv G6渲染树形结构并支持节点增删改

写在前面 在一些管理系统中&#xff0c;会对组织架构、级联数据等做一些管理&#xff0c;你会怎么实现呢&#xff1f;在经过调研很多插件之后决定使用 Antv G6 实现&#xff0c;文档也比较清晰&#xff0c;看看怎么实现吧&#xff0c;先来看看效果图。点击在线体验 实现的功能…...

【PB案例学习笔记】-26制作一个带浮动图标的工具栏

写在前面 这是PB案例学习笔记系列文章的第26篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…...

反向沙箱技术:安全隔离上网

在信息化建设不断深化的今天&#xff0c;业务系统的安全性和稳定性成为各公司和相关部门关注的焦点。面对日益复杂的网络威胁&#xff0c;传统的安全防护手段已难以满足需求。深信达反向沙箱技术&#xff0c;以其独特的设计和强大的功能&#xff0c;成为保障政务系统信息安全的…...

前端在for循环中使用Element-plus el-select中的@click.native动态传参

<el-table ref"table" :data"editTableVariables" cell-dblclick"handleRowDblClick" style"width: 100%" > <!-- el-table-column: 表格列组件&#xff0c;定义每列的展示内容和属性 --><el-table-column prop&q…...

Oracle SQL - CONNECT BY语句Where条件中不能使用OR?[已解决]

数据 SQL> SELECT * FROM demo_a;CUSTOMER TOTAL ---------- ---------- A 100200SQL> SELECT * FROM demo_b;CUSTOMER RN QTY ---------- ---------- ---------- A 1 30 A 2 …...

python-逻辑语句

if else语句 不同于C&#xff1a;else if range语句&#xff1a; continue continue的作用是&#xff1a; 中断所在循环的当次执行&#xff0c;直接进入下一次 continue在嵌套循环中的应用 break 直接结束所在的循环 break在嵌套循环中的应用 continue和break&#xff0c;在…...

【stm32】大一上学期笔记复制

砌墙单片机 外设是什么&#xff1f; ipage 8 nx轴 128 X0-127 y0-63 PWM脉冲宽度调制 PWM脉冲宽度调制 2023年10月13日 基本特性&#xff1a;脉冲宽度调制PWM是一种对模拟信号进行数字编码的方法。广泛引用于电机控制&#xff0c;灯光的亮度调节&#xff0c;功率控制等领域…...

LeetCode题练习与总结:二叉树的前序遍历--144

一、题目描述 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;roo…...

如何优化Spring Boot应用的性能

如何优化Spring Boot应用的性能 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何通过优化技术和最佳实践来提升Spring Boot应用的性能&#x…...

人工智能--目标检测

欢迎来到 Papicatch的博客 文章目录 &#x1f349;引言 &#x1f349;概述 &#x1f348;目标检测的主要流程通常包括以下几个步骤 &#x1f34d;数据采集 &#x1f34d;数据预处理 &#x1f34d;特征提取 &#x1f34d;目标定位 &#x1f34d;目标分类 &#x1f348;…...

Java基础之List实现类

文章目录 一、基本介绍二、常见方法三、ArrayList注意事项四、ArrayList底层结构我的理解 五、ArrayList扩容机制无参构造器有参构造器 六、LinkedList介绍底层操作机制 七、ArrayList 与 LinkedListArrayListLinkedList tip&#xff1a;以下是正文部分 一、基本介绍 List集合…...

java List接口介绍

List 是 Java 集合框架中的一个接口,它继承自 Collection 接口,代表一个有序的元素集合。List 允许重复的元素,并且可以通过索引来访问元素。Java 提供了多种 List 的实现,如 ArrayList、LinkedList、Vector 和 CopyOnWriteArrayList。 List接口概述 List 接口提供了一些…...

调度器APScheduler定时执行任务

APScheduler&#xff08;Advanced Python Scheduler&#xff09;是一个Python库&#xff0c;用于调度任务&#xff0c;使其在预定的时间间隔或特定时间点执行。它支持多种调度方式&#xff0c;包括定时&#xff08;interval&#xff09;、日期&#xff08;date&#xff09;和Cr…...

git合并分支的疑问

今天遇到一个奇怪的问题&#xff1a; 1、后端从master拉了三个分支。分别为dev、test、和stage。 2、研发1从dev拉了分支feature1,然后commit、commit、commit……。最后request merge到dev、test和stage。成功了。 3、研发2从dev拉了分支feature2,注意&#xff0c;feature2…...

catia数控加工仿真Productlist无法添加部件或零件

这种情况是没有把NCSetup显示 在工具中勾选即可...

关于Pycharm右下角不显示解释器interpreter的问题解决

关于Pycharm右下角不显示解释器interpreter的问题 在安装新的Pycharm后&#xff0c;发现右下角的 interpreter 的选型消失了&#xff1a; 觉得还挺不习惯的&#xff0c;于是网上找解决办法&#xff0c;无果。 自己摸索了一番后&#xff0c;发现解决办法如下&#xff1a; 勾…...

为什么word生成的PDF内容显示不全?

在现代办公环境中&#xff0c;将文档从一个格式转换为另一个格式是一个常见的任务。然而&#xff0c;有时候我们可能会遇到意想不到的问题&#xff0c;比如使用Word转换成PDF时&#xff0c;生成的PDF文件只显示了整个界面的四分之一内容。这种问题不仅令人困扰&#xff0c;也可…...

JVM专题十三:总结与整理(持续更新)

图解JVM JVM与Java体系结构 JVM垃圾回收算法 JVM垃圾回收器 图解JVM主要是放了前面12个章节的我们给大家画的图&#xff0c;做了整体的汇总&#xff0c;大家可以根据图区回忆我们所说的内容&#xff0c;查缺补漏。 实战经验 1、项目中数据量多少&#xff0c;QPS与TPS最高多少…...

MobPush iOS端海外推送最佳实现

推送注册 在AppDelegate里进行SDK初始化&#xff08;也可以在Info.plist文件中进行AppKey&#xff0c;AppSecret的配置&#xff09;并对通知功能进行注册以及设置推送的环境和切换海外服务器等&#xff0c;参考如下步骤代码&#xff1a; <span style"background-colo…...

商家团购app微信小程序模板

手机微信商家团购小程序页面&#xff0c;商家订餐外卖小程序前端模板下载。包含&#xff1a;团购主页、购物车订餐页面、我的订单、个人主页等。 商家团购app微信小程序模板...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...