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

echarts使用graph、lines实现拓扑,可以拖动增加effect效果

options.js

// import React from 'react'
// import * as echarts from 'echarts'import './index.less'export const useEchartsOptionFun = ({ nodeDataList, getNodeLinksDataList, getLinesCoordsFun }) => {const option = {title: {text: '拓扑关系图',top: 'top',left: 'center',},itemStyle: {normal: {color: '#67C23A',},shadowBlur: 0,},textStyle: {color: '#444',fontSize: 16,fontWeight: 600,},legend: [{// formatter: function (name) {//     return echarts.format.truncateText(name, 200, '12px', '…')// },tooltip: {show: false,},selectedMode: 'false',bottom: 20,},],animationDuration: 500,animationEasingUpdate: 'quinticInOut',xAxis: {show: false,max: 500,type: 'value',},yAxis: {show: false,type: 'value',max: 500,},tooltip: {formatter(params) {const { itemInfo = [] } = params.data || {}if (itemInfo.length === 0) return nulllet itemInfoStr = ''itemInfo.map(item => {itemInfoStr += `<div key=${item.name} class='topoEchartsBox_tooltip_title'>${item.name}:<span class='topoEchartsBox_tooltip_title_span'>${item.value}</span></div>`})const str = `<div class='topoEchartsBox_tooltip'> ${itemInfoStr} </div>`return str},},series: [{// focusNodeAdjacency:true,id: 'nodes',type: 'graph',// layout: 'force',layout: 'none',// roam: true, //鼠标缩放及平移coordinateSystem: 'cartesian2d',// coordinateSystem: 'cartesian2d',legendHoverLink: false,hoverAnimation: true,nodeScaleRatio: false,//建头edgeSymbol: ['circle', 'none'],edgeSymbolSize: [2, 15],edgeLabel: {show: true,normal: {show: true,position: 'middle',textStyle: {fontSize: 12,},// formatter: '{c}',},// formatter: '{c}',},emphasis: {scale: true,},cursor: 'pointer',roam: true,draggable: true,// focusNodeAdjacency: true,//圆形上面的文字label: {normal: {position: 'bottom',show: true,textStyle: {fontSize: 12,},},},itemStyle: {normal: {color: '#409eff',},shadowBlur: 0,},data: nodeDataList,links: getNodeLinksDataList(nodeDataList),lineStyle: {normal: {curveness: 0,color: '#67c23a',width: 2,},emphasis: {color: 'red',width: 3,type: 'dashed', //虚线},},},// ].concat([]),].concat([...getLinesCoordsFun()]),}return {option,}
}

topoEchartsBox.js

import React, { useCallback, useState, useEffect, useRef } from 'react'
import { Row, Col, Select, Button, Spin, Input, Modal, modal, message, Form, Radio, Tooltip, Descriptions, DatePicker } from 'antd'
import RsFlowSearch from '@/components/RsFlowSearch'
import { TooltipBox } from '@/components/utils/common'
import TableTooltip from '@/components/TableTooltip'
import * as IPServe from '@/serve/IPServe/IPServe'
import _ from 'lodash-es'
import Chart from '@/components/Chart-Topo'
import * as echarts from 'echarts'
import { useEchartsOptionFun } from './echarts/index.js'
import './index.less'
import bnc from '@/assets/ComImg/topoImg/bnc.png'
import jiaoHuanJi from '@/assets/ComImg/topoImg/jiaoHuanJi.png'
import olt from '@/assets/ComImg/topoImg/olt.png'
// -- 断开异常图标
const imgae_ = 'image://'
let errorEffectSymbol ='path://M671.830688 511.699001l319.059377-319.059377c43.945914-43.945914 43.945914-115.583774 0-159.529688-43.945914-43.945914-115.583774-43.945914-159.529688 0l-319.059377 319.059377-319.059377-319.059377c-43.945914-43.945914-115.583774-43.945914-159.529688 0-43.945914 43.945914-43.945914 115.583774 0 159.529688l319.059377 319.059377-319.059377 319.059377c-43.945914 43.945914-43.945914 115.583774 0 159.529688 43.945914 43.945914 115.583774 43.945914 159.529688 0l319.059377-319.059377 319.059377 319.059377c43.945914 43.945914 115.583774 43.945914 159.529688 0 43.945914-43.945914 43.945914-115.583774 0-159.529688L671.830688 511.699001z'
// 节点数据export default function (props) {const [nodeDataList, setnodeDataList] = useState([{// 当前设备名称name: 'liuqing',id: '1',// 要连接的设备名称linkTargetName: ['2', '3', '4'],linkValue: '好好学习',coordConfig: { level: 0 },symbolSize: 40,symbol: imgae_ + bnc,// draggable: true,value: [250, 450],itemInfo: [{ name: 'liuqing', value: '12台' },{ name: 'liuqing', value: '260个' },{ name: 'liuqing', value: '10%' },{ name: 'liuqing', value: '10%' },{ name: 'liuqing', value: '10%' },{ name: 'liuqing', value: '10%' },{ name: 'liuqing liuqing 连接数', value: '100' },{ name: 'liuqing liuqing', value: '10%' },{ name: 'IP liuqing', value: '10%' },],},//交换机,C,D ....n{name: '交换机',id: '2',linkTargetName: ['5', '6'],linkValue: '好好学习 ',coordConfig: {level: '1',},symbol: imgae_ + jiaoHuanJi,symbolSize: 40,// draggable: true,value: [160, 350],},{name: '交换机',id: '3',linkTargetName: ['5', '6', '8'],linkValue: '111',coordConfig: {level: '1',},symbol: imgae_ + jiaoHuanJi,symbolSize: 40,// draggable: true,value: [250, 350],},{name: '智能城域网',id: '4',linkTargetName: ['6', '7'],linkValue: '好好学习 ',coordConfig: {level: '1',},symbol: imgae_ + jiaoHuanJi,symbolSize: 40,// draggable: true,value: [340, 350],},{name: 'liuqing-1', // 节点名id: '5', // 节点名linkTargetName: [], // 连线目标节点linkValue: '好好学习 ', // 连线介绍coordConfig: {level: '2-error',effect: {// show: true,show: false,smooth: false,trailLength: 0,symbol: errorEffectSymbol,color: '#fb3f3f',symbolSize: 10,period: 3,delay: 1500,loop: true,},lineStyle: {normal: {curveness: 0,color: '#fb3f3f',width: 2,},},}, // 连线动态箭头配置,没有就不需要此配置value: [90, 100],// draggable: true,// fixed: true,symbol: imgae_ + olt,symbolSize: 40,itemStyle: {color: '#fb3f3f',},},{name: 'liuqing-2',id: '6',linkTargetName: [],linkValue: ' 好好学习',coordConfig: {level: '2',},value: [190, 100],// draggable: true,// fixed: true,symbol: imgae_ + olt,symbolSize: 40,},{name: 'liuqing-3',id: '7',linkTargetName: [],linkValue: '好好学习 ',coordConfig: {level: '2',},value: [250, 100],// draggable: true,fixed: true,symbol: imgae_ + olt,symbolSize: 40,},{name: 'liuqing-4',id: '8',linkTargetName: [],linkValue: ' 好好学习',coordConfig: {level: '2',},value: [350, 100],// draggable: true,symbol: imgae_ + olt,symbolSize: 40,},])const [boxHeight, setboxHeight] = useState('300px')const [myChart, setmyChart] = useState(null)const resizeFun = () => {const box = document.querySelector('.rsflowSearchContent .topoEcharts')const boxTop = box?.getBoundingClientRect()?.topsetboxHeight(`calc(${window.innerHeight}px - ${boxTop}px - 30px)`)}useEffect(() => {if (parseFloat(boxHeight) < 300) {setboxHeight('300px')}}, [boxHeight])useEffect(() => {window.addEventListener('resize', resizeFun)setTimeout(() => {resizeFun()})// nodeDataList 改变的时候 说明是拖动页面元素的时候// 重新 setOptionif (myChart) {let currentLinks = getNodeLinksDataList(nodeDataList) // 或者更高效地只更新受影响的 linkmyChart &&myChart.setOption({series: [{id: 'nodes',data: nodeDataList,links: currentLinks,},].concat([...getLinesCoordsFun()]),})}return () => {window.removeEventListener('resize', resizeFun)}}, [nodeDataList])const getNodeLinksDataList = function (nodeDataList) {let coordData = []nodeDataList.map(item => {item.linkTargetName.map(i => {const { id, name } = nodeDataList.find(i_find => i === i_find.id)coordData = [...coordData,{// 光点流动效果symbol: ['none', 'arrow'],symbolSize: [4, 8],label: {show: false,position: 'middle',formatter: item.name + '--' + name,},source: item.id,target: id,roam: true, // 允许缩放和平移focusNodeAdjacency: true, // 聚焦邻接点id: item.name + '---to---' + name,},]})})return coordData}// type === lines 的线条const getLinesCoordsFun = function () {let coorDataDict = {}let defaultConfig = {type: 'lines', //块1,2...n到节点A,B...NcoordinateSystem: 'cartesian2d',z: 1,effect: {show: true,smooth: true,trailLength: 0,symbol: 'arrow',color: '#67c23a',width: 20,symbolSize: 10,period: 3,delay: 1500,// loop: false,loop: true,},lineStyle: {width: 2,color: '#67c23a',},data: [],}nodeDataList.map(item => {if (item.coordConfig !== undefined) {if (!(item.coordConfig.level in coorDataDict)) {let coorConfig = JSON.parse(JSON.stringify(defaultConfig))// 自定义好的样式 lineStyleif (item.coordConfig.lineStyle !== undefined) {coorConfig.lineStyle = item.coordConfig.lineStyle}// 自定义好的样式 effectif (item.coordConfig.effect !== undefined) {coorConfig.effect = item.coordConfig.effect}// 根据 level 存起来各自的 coordConfigcoorDataDict[item.coordConfig.level] = coorConfig}// 设置连线 coordsitem.linkTargetName.map(i => {const { value, name } = nodeDataList.find(i_find => i === i_find.id)coorDataDict[item.coordConfig.level].data.push({coords: [item.value, value],})})}})return Object.values(coorDataDict)}const onChartdrag = ({ draggingNode, dataCoord }) => {// 更新 nodeDataList 中对应节点的位置const nodeDataListNew = nodeDataList.map(n => {if (n.id === draggingNode.data.id) {n.value = dataCoord}return n})setnodeDataList(nodeDataListNew)}// myChart 初始后调用的第一个方法// 展示接口返回的数据 nodeDataListconst returnMyChartFun = myChart => {const { option } = useEchartsOptionFun({ nodeDataList, getNodeLinksDataList, getLinesCoordsFun })setmyChart(myChart)myChart.setOption(option)}return (<div className="topoEchartsBox"><RsFlowSearch title="BNC/BRAS跨域综合分析拓扑关系图" isShowRightIcon={false}><Chart className={'topoEcharts'} onChartdrag={onChartdrag} returnMyChartFun={returnMyChartFun} style={{ height: boxHeight, width: '100%' }} /></RsFlowSearch></div>)
}

Chat-Topo.js

import React, { useEffect, useRef, useState } from 'react'
import useEchartsSize from '@/components/useEchartsSize'
var echarts = require('echarts')
const debounce = () => {let timer = nullconst newDebounce = function (fn, wait, ...args) {return new Promise((resolve, reject) => {if (timer !== null) {clearTimeout(timer)}timer = setTimeout(_ => {try {resolve(fn(...args))} catch (e) {reject(e)}}, wait)})}return newDebounce
}
const newDebounce = debounce()
let draggingNode = nullfunction chart(props) {const { style, className, onChartClick, onChartdrag, returnMyChartFun } = propsconst chartRef = useRef(null)const [barChart, setBarChart] = useState()useEchartsSize(barChart)useEffect(() => {const chartDom = chartRef.currentconst myChart = echarts.init(chartDom)myChart.clear()myChart.resize()returnMyChartFun(myChart)setBarChart(myChart)onChartClick &&myChart.on('click', params => {onChartClick(params.name)})myChart.on('mousedown', params => {if (params.componentType === 'series' && params.seriesType === 'graph' && params.dataType === 'node') {draggingNode = params}})myChart.getDom().addEventListener('mouseup', params => {newDebounce(() => {if (draggingNode) {const pixel = [params.layerX, params.layerY]const dataCoord = myChart.convertFromPixel({ seriesIndex: 0 }, pixel)onChartdrag({ draggingNode, dataCoord, myChart })draggingNode = null}}, 16)})// }}, [])return <div className={className} ref={chartRef} style={style || { width: '100%', height: '300px' }} />
}export default chart

请添加图片描述

相关文章:

echarts使用graph、lines实现拓扑,可以拖动增加effect效果

options.js // import React from react // import * as echarts from echartsimport ./index.lessexport const useEchartsOptionFun ({ nodeDataList, getNodeLinksDataList, getLinesCoordsFun }) > {const option {title: {text: 拓扑关系图,top: top,left: center,}…...

产品经理课程(九)

从需求到功能设计 &#xff08;一&#xff09;复习 产品规划&#xff1a;产品定位、阶段性计划 产品定位&#xff1a;产品画布&#xff08;9个步骤&#xff1b;最重要的是先解决什么问题&#xff09; &#xff08;Roadmap&#xff09;目标要素&#xff1a;时间、事项、里程碑…...

二.单例模式‌

一.单例模式的定义 单例模式是一种‌创建型设计模式‌&#xff0c;确保一个类‌只有一个实例‌&#xff0c;并提供该实例的‌全局访问点‌。 1.1.核心目标 唯一实例‌&#xff1a;限制类的实例化次数仅一次。‌全局访问‌&#xff1a;提供统一的访问入口&#xff08;通常是静…...

从零开始开发纯血鸿蒙应用之网络检测

从零开始开发纯血鸿蒙应用 〇、前言一、认识 connection 模块1、获取默认网络2、获取网络能力信息3、解析网络能力信息3.1、NetCap3.2、NetBearType 二、实现网络检测功能1、申请权限2、获取默认网路的 NetCap 数组 三、总结 〇、前言 在之前的博文里&#xff0c;介绍了如何实…...

向 AI Search 迈进,腾讯云 ES 自研 v-pack 向量增强插件揭秘

作者&#xff1a;来自腾讯云刘忠奇 2025 年 1 月&#xff0c;腾讯云 ES 团队上线了 Elasticsearch 8.16.1 AI 搜索增强版&#xff0c;此发布版本重点提升了向量搜索、混合搜索的能力&#xff0c;为 RAG 类的 AI Search 场景保驾护航。除了紧跟 ES 官方在向量搜索上的大幅优化动…...

接IT方案编写(PPT/WORD)、业务架构设计、投标任务

1、IT 方案编写&#xff08;PPT/WORD&#xff09;​ 定制化方案&#xff1a;根据客户需求&#xff0c;提供涵盖云计算、大数据、人工智能等前沿技术领域的 PPT/WORD 方案编写服务&#xff0c;精准提炼核心价值&#xff0c;呈现专业技术内容。​ 逻辑清晰架构&#xff1a;采用…...

PostgreSQL 的扩展pageinspect

PostgreSQL 的扩展pageinspect pageinspect 是 PostgreSQL 提供的一个强大的底层扩展&#xff0c;允许数据库管理员和开发者直接检查数据库页面的内部结构。这个扩展对于数据库调试、性能优化和深入学习 PostgreSQL 存储机制非常有价值。 一、扩展概述 功能&#xff1a;提供…...

Unity——QFramework框架 内置工具

QFramework 除了提供了一套架构之外&#xff0c;QFramework 还提供了可以脱离架构使用的工具 TypeEventSystem、EasyEvent、BindableProperty、IOCContainer。 这些工具并不是有意提供&#xff0c;而是 QFramework 的架构在设计之初是通过这几个工具组合使用而成的。 内置工具…...

【win | docker开启远程配置】使用 SSH 隧道访问 Docker的前操作

在主机A pycharm如何连接远程主机B win docker? 需要win docker配置什么&#xff1f; 快捷配置-主机B win OpenSSH SSH Server https://blog.csdn.net/z164470/article/details/121683333 winR,打开命令行&#xff0c;输入net start sshd,启动SSH。 或者右击我的电脑&#…...

股指期货波动一个点多少钱?

很多朋友在交易股指期货时&#xff0c;都会好奇一个问题&#xff1a;股指期货波动一个点&#xff0c;我的账户里到底是赚了还是亏了多少钱&#xff1f;要搞清楚这个问题&#xff0c;其实很简单&#xff0c;只需要了解两个关键信息&#xff1a;股指期货的“交易单位”&#xff0…...

Kafka 快速上手:安装部署与 HelloWorld 实践(一)

一、Kafka 是什么&#xff1f;为什么要学&#xff1f; ** 在大数据和分布式系统的领域中&#xff0c;Kafka 是一个如雷贯耳的名字。Kafka 是一种分布式的、基于发布 / 订阅的消息系统&#xff0c;由 LinkedIn 公司开发&#xff0c;后成为 Apache 基金会的顶级开源项目 。它以…...

NGINX `ngx_stream_core_module` 模块概览

一、模块定位与功能 通用 TCP/UDP 代理 支持同时处理 TCP 和 UDP 流量&#xff0c;透明转发请求到后端服务器组&#xff08;upstream&#xff09;。可作为四层负载均衡&#xff0c;根据客户端 IP、权重、最少连接等策略将连接分发给后端。 预读&#xff08;preread&#xff09…...

iOS、Android、鸿蒙、Web、桌面 多端开发框架Kotlin Multiplatform

Kotlin Multiplatform&#xff08;简称 KMP&#xff09;是 JetBrains 推出的开源跨平台开发框架 Kuikly 是腾讯开源的跨端开发框架&#xff0c;基于 Kotlin Multiplatform 技术构建&#xff0c;为开发者提供了技术栈更统一的跨端开发体验 KMP 不仅局限于移动端&#xff0c;它…...

探索C++标准模板库(STL):String接口的底层实现(下篇)

前引&#xff1a;在C的面向对象编程中&#xff0c;对象模型是理解语言行为的核心。无论是类的成员函数如何访问数据&#xff0c;还是资源管理如何自动化&#xff0c;其底层机制均围绕两个关键概念展开&#xff1a;this指针与六大默认成员函数。它们如同对象的“隐形守护者”&am…...

Flutter知识点汇总

Flutter架构解析 1. Flutter 是什么?它与其他移动开发框架有什么不同? Flutter 是 Google 开发的开源移动应用开发框架,可用于快速构建高性能、高保真的移动应用(iOS 和 Android),也支持 Web、桌面和嵌入式设备。。它与其他移动开发框架(如 React Native、Xamarin、原…...

vue组件的data为什么是函数?

vue组件的data为什么是函数&#xff1f; 在JS中&#xff0c;实例是通过构造函数创建的&#xff0c;每个构造函数可以new出多个实例&#xff0c;每个实例都会继承原型上的方法和属性。 在vue中&#xff0c;一个vue组件就是一个实例&#xff0c;当一个组件被复用多次&#xff0…...

AI图片售卖:是暴利新风口还是虚幻泡沫?哪些平台适合售卖AI图片

还记得去年大火的Midjourney吗&#xff1f;今年4月&#xff0c;Midjourney又发布了备受期待的V7版本&#xff0c;带来了更高的图像质量和创新功能。使用Midjourney、Stable Diffusion、DALLE等AI图片生成工具&#xff0c;创作者只需输入关键词即可获得高质量的原创图片。这一变…...

​线性注意力 vs. 传统注意力:效率与表达的博弈新解

​核心结论​&#xff1a;线性注意力用计算复杂度降维换取全局建模能力&#xff0c;通过核函数和结构优化补足表达缺陷 一、本质差异&#xff1a;两种注意力如何工作&#xff1f; ​特性​传统注意力&#xff08;Softmax Attention&#xff09;线性注意力&#xff08;Linear At…...

YOLO在QT中的完整训练、验证与部署方案

以下是YOLO在QT中的完整训练、验证与部署方案&#xff1a; 训练方案 准备数据集&#xff1a; 收集数据&#xff1a;收集与目标检测任务相关的图像数据集&#xff0c;可以是公开数据集如COCO、Pascal VOC&#xff0c;也可以是自定义数据集。标注数据&#xff1a;使用标注工具如…...

Vue在线预览excel、word、ppt等格式数据。

目录 前言 1.安装库 2.预览文件子组件代码 3、新建store/system.ts 4、父页面进行使用 总结 前言 纯前端处理文件预览&#xff0c;包含excel、word、ppt、txt等格式&#xff0c;不需要后端服务器进行部署&#xff0c;并且内网也可以使用。 1.安装库 npm install vue-offi…...

增量式网络爬虫通用模板

之前做过一个项目&#xff0c;他要求是只爬取新产生的或者已经更新的页面&#xff0c;避免重复爬取未变化的页面&#xff0c;从而节省资源和时间。这里我需要设计一个增量式网络爬虫的通用模板。可以继承该类并重写部分方法以实现特定的解析和数据处理逻辑。这样可以更好的节约…...

【JVM】三色标记法原理

在JVM中&#xff0c;三色标记法是GC过程中对象状态的判断依据&#xff0c;回收前给对象设置上不同的三种颜色&#xff0c;三色分为白色、灰色、黑色。根据颜色的不同&#xff0c;决定对象是否要被回收。 白色表示&#xff1a; 初始状态&#xff1a;所有对象未被 GC 访问。含义…...

【uniapp开发】picker组件的使用

项目uniapp&#xff0c;结合fastadmin后端开发 picker组件的官方文档说明 https://en.uniapp.dcloud.io/component/picker.html#普通选择器 先看效果&#xff1a; 1、实现设备类型的筛选&#xff1b;2、实现设备状态的筛选&#xff1b; 前端代码&#xff08;节选&#xff0…...

【HarmonyOS Next之旅】DevEco Studio使用指南(三十一) -> 同步云端代码至DevEco Studio工程

目录 1 -> 同步云函数/云对象 1.1 -> 同步单个云函数/云对象 1.2 -> 批量同步云函数/云对象 2 -> 同步云数据库 2.1 -> 同步单个对象类型 2.2 -> 批量同步对象类型 3 -> 一键同步云侧代码 1 -> 同步云函数/云对象 说明 对于使用DevEco Studio…...

如何评估大语言模型效果

评估大模型微调后的效果是一个系统化的过程&#xff0c;需要结合客观指标和主观评估&#xff0c;并根据任务类型&#xff08;分类、生成、回归等&#xff09;选择合适的评估方法。 一、评估前的准备工作 数据集划分&#xff1a; 将数据分为 训练集、验证集 和 测试集&#xff…...

go-zero微服务入门案例

一、go-zero微服务环境安装 1、go-zero脚手架的安装 go install github.com/zeromicro/go-zero/tools/goctllatest2、etcd的安装下载地址根据自己电脑操作系统下载对应的版本&#xff0c;具体的使用自己查阅文章 二、创建一个user-rpc服务 1、定义user.proto文件 syntax &qu…...

Python控制台输出彩色字体指南

在Python开发中&#xff0c;有时我们需要在控制台输出彩色文本以提高可读性或创建更友好的用户界面。本文将介绍如何使用colorama库来实现这一功能。 为什么需要彩色输出&#xff1f; 提高可读性&#xff1a;重要信息可以用不同颜色突出显示更好的用户体验&#xff1a;错误信息…...

零基础在实践中学习网络安全-皮卡丘靶场(第十六期-SSRF模块)

最后一期了&#xff0c;感谢大家一直以来的关注&#xff0c;如果您对本系列文章内容有问题或者有更好的方法&#xff0c;请在评论区发送。 介绍 其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制导致攻击者可以传入任意…...

开源之夏·西安电子科技大学站精彩回顾:OpenTiny开源技术下沉校园,点燃高校开发者技术热情

开源之夏2025编程活动正在如火如荼的进行中&#xff0c;当前也迎来了报名的倒计时阶段&#xff0c;开源之夏组织方也通过高校行系列活动进入各大高校&#xff0c;帮助高校开发者科普开源文化、开源活动、开源技术。 6月4日 开源之夏携手多位开源技术大咖、经验型选手走进西安电…...

html、css(javaweb第一天)

HTML: 文字、图片、视频组成 由标签组成的语言 行内标签span//无语意 <img src"url">//图片 <a herf"url" target"是否开新页面">点击谁</a>//超链接 <video src"url" controls></video>//controls播放…...