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 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: [{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()?.topsetboxHeight(`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 = 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
