options.js
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 : [
{
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 null
let 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 : [
{
id : 'nodes' ,
type : 'graph' ,
layout : 'none' ,
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 ,
} ,
} ,
} ,
emphasis : {
scale : true ,
} ,
cursor : 'pointer' ,
roam : true ,
draggable : 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 ( [ ... 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,
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%' } ,
] ,
} ,
{
name : '交换机' ,
id : '2' ,
linkTargetName : [ '5' , '6' ] ,
linkValue : '好好学习 ' ,
coordConfig : {
level : '1' ,
} ,
symbol : imgae_ + jiaoHuanJi,
symbolSize : 40 ,
value : [ 160 , 350 ] ,
} ,
{
name : '交换机' ,
id : '3' ,
linkTargetName : [ '5' , '6' , '8' ] ,
linkValue : '111' ,
coordConfig : {
level : '1' ,
} ,
symbol : imgae_ + jiaoHuanJi,
symbolSize : 40 ,
value : [ 250 , 350 ] ,
} ,
{
name : '智能城域网' ,
id : '4' ,
linkTargetName : [ '6' , '7' ] ,
linkValue : '好好学习 ' ,
coordConfig : {
level : '1' ,
} ,
symbol : imgae_ + jiaoHuanJi,
symbolSize : 40 ,
value : [ 340 , 350 ] ,
} ,
{
name : 'liuqing-1' ,
id : '5' ,
linkTargetName : [ ] ,
linkValue : '好好学习 ' ,
coordConfig : {
level : '2-error' ,
effect : {
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 ] ,
symbol : imgae_ + olt,
symbolSize : 40 ,
itemStyle : {
color : '#fb3f3f' ,
} ,
} ,
{
name : 'liuqing-2' ,
id : '6' ,
linkTargetName : [ ] ,
linkValue : ' 好好学习' ,
coordConfig : {
level : '2' ,
} ,
value : [ 190 , 100 ] ,
symbol : imgae_ + olt,
symbolSize : 40 ,
} ,
{
name : 'liuqing-3' ,
id : '7' ,
linkTargetName : [ ] ,
linkValue : '好好学习 ' ,
coordConfig : {
level : '2' ,
} ,
value : [ 250 , 100 ] ,
fixed : true ,
symbol : imgae_ + olt,
symbolSize : 40 ,
} ,
{
name : 'liuqing-4' ,
id : '8' ,
linkTargetName : [ ] ,
linkValue : ' 好好学习' ,
coordConfig : {
level : '2' ,
} ,
value : [ 350 , 100 ] ,
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 ( ) ?. top
setboxHeight ( ` calc( ${ window. innerHeight} px - ${ boxTop} px - 30px) ` )
}
useEffect ( ( ) => {
if ( parseFloat ( boxHeight) < 300 ) {
setboxHeight ( '300px' )
}
} , [ boxHeight] )
useEffect ( ( ) => {
window. addEventListener ( 'resize' , resizeFun)
setTimeout ( ( ) => {
resizeFun ( )
} )
if ( myChart) {
let currentLinks = getNodeLinksDataList ( nodeDataList)
myChart &&
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
}
const getLinesCoordsFun = function ( ) {
let coorDataDict = { }
let defaultConfig = {
type : 'lines' ,
coordinateSystem : 'cartesian2d' ,
z : 1 ,
effect : {
show : true ,
smooth : true ,
trailLength : 0 ,
symbol : 'arrow' ,
color : '#67c23a' ,
width : 20 ,
symbolSize : 10 ,
period : 3 ,
delay : 1500 ,
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) )
if ( item. coordConfig. lineStyle !== undefined ) {
coorConfig. lineStyle = item. coordConfig. lineStyle
}
if ( item. coordConfig. effect !== undefined ) {
coorConfig. effect = item. coordConfig. effect
}
coorDataDict[ item. coordConfig. level] = coorConfig
}
item. 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 } ) => {
const nodeDataListNew = nodeDataList. map ( n => {
if ( n. id === draggingNode. data. id) {
n. value = dataCoord
}
return n
} )
setnodeDataList ( nodeDataListNew)
}
const 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 = null
const 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 = null
function chart ( props ) {
const { style, className, onChartClick, onChartdrag, returnMyChartFun } = props
const chartRef = useRef ( null )
const [ barChart, setBarChart] = useState ( )
useEchartsSize ( barChart)
useEffect ( ( ) => {
const chartDom = chartRef. current
const 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