1. 安装Echarts

npm install echarts@5.3.2 --save

2. 页面代码

<!-- 拓扑图组件的模板结构 -->
<template>
  <div class="topology-container">
    <div ref="chart" class="chart-box"></div>
  </div>
</template>

<script>
import * as echarts from 'echarts'

export default {
  name: 'TopologyChart',
  data() {
    return {
      chartInstance: null,
      resizeChart: null,
      topologyData: {
        nodes: [],
        links: [],
        categories: []
      }
    }
  },
  mounted() {
    // 该方法用来获取数据
    this.getData()
  },
  beforeDestroy() {
    if (this.chartInstance) this.chartInstance.dispose()
    window.removeEventListener('resize', this.resizeChart)
  },
  methods: {
    getData() {
      // 获取数据
      const resData = [
        { id: 1, name: '节点1', value: 100 },
        { id: 2, name: '节点2', value: 200 },
        { id: 3, name: '节点3', value: 300 },
        { id: 4, name: '节点4', value: 400 },
        { id: 5, name: '节点5', value: 500 },
        { id: 6, name: '节点6', value: 600 },
        { id: 7, name: '节点7', value: 700 },
        { id: 8, name: '节点8', value: 800 },
      ]
      this.topologyData.nodes = resData

      /**
       * 静态数据
       * key 为节点id,值为节点坐标和指向的节点id
       * x 为节点x坐标左右偏移
       * y 为节点y坐标上下偏移
       * lineToNode 为指向的节点id,可以有多个
       * */ 
      const staticData = {
        1: { x: 100, y: 100, lineToNode: [2, 3], symbol: 'image://' + require('@/assets/images/node1.png') },
        2: { x: 200, y: 200, lineToNode: [1, 3], symbol: 'image://' + 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png' },
        3: { x: 300, y: 300, lineToNode: [1, 2], symbol: 'image://' + require('@/assets/images/node3.png') },
        4: { x: 400, y: 400, lineToNode: [1, 2, 3], symbol: 'image://' + require('@/assets/images/node4.png') },
        5: { x: 500, y: 500, lineToNode: [1, 3, 4], symbol: 'image://' + require('@/assets/images/node5.png') },
        6: { x: 600, y: 600, lineToNode: [1, 5], symbol: 'image://' + require('@/assets/images/node6.png') },
        7: { x: 700, y: 700, lineToNode: [1, 4, 5], symbol: 'image://' + require('@/assets/images/node7.png') },
        8: { x: 800, y: 800, lineToNode: [1, 6, 7], symbol: 'image://' + require('@/assets/images/node8.png') },
      }

      // 根据静态数据生成连线数据
      this.topologyData.nodes = resData.map(item => {
        return {
          ...item,
          x: staticData[item.id]?.x || 0,
          y: staticData[item.id]?.y || 0,
          symbol: staticData[item.id]?.symbol || null,
          symbolSize: staticData[item.id]?.symbolSize || 60,
          lineToNode: staticData[item.id]?.lineToNode || []
        }
      })

      // 处理连线数据
      let links = []
      for (const key in staticData) {
        const item = staticData[key]
        if(item.lineToNode && item.lineToNode.length) {
          item.lineToNode.forEach(nodeId => {
            links.push({
              source: key + '',
              target: nodeId + ''
            })
          })
        }
      }
      this.topologyData.links = links

      // 渲染ECharts
      this.initChart()
    },
    initChart() {
      this.chartInstance = echarts.init(this.$refs.chart)
      this.resizeChart = () => this.chartInstance?.resize()
      window.addEventListener('resize', this.resizeChart)

      const option = {
        tooltip: { trigger: 'item' },
        legend: { show: false },
        series: [
          {
            type: 'graph',
            roam: true, // 是否允许拖拽
            zoom: 1, // 缩放比例
            // layout: 'force', // 力导布局
            // force: {
            //   repulsion: 800,      // 增大节点间斥力,让节点更分散
            //   edgeLength: 200,     // 缩短连线长度,避免布局松散
            //   gravity: 0.1,        // 轻微向中心聚拢,防止节点飘到边缘
            //   layoutAnimation: false // 关闭布局动画,直接渲染最终位置
            // },
            layout: 'none', // 不布局
            force: {
              repulsion: 0, // 排斥力
              layoutAnimation: false // 关闭布局动画,直接渲染最终位置
            },
            symbol: null,
            symbolSize: 60, // 缩小节点尺寸,给文字更多空间
            label: {
              show: true,
              color: '#fff',
              fontSize: 25, // 缩小字体,减少重叠概率
              position: 'bottom',
              distance: 8, // 缩短文字与图标的距离,避免文字扩散
              overflow: 'truncate', // 超长文字自动截断,防止遮挡
              ellipsis: '...' // 截断后显示省略号
            },
            edgeSymbol: ['none', 'arrow'], // 连线箭头类型
            edgeSymbolSize: [0, 8], // 连线箭头大小
            lineStyle: {
              color: '#cccccc', // 连线颜色
              width: 1 // 连线宽度
            },
            data: this.topologyData.nodes,
            links: this.topologyData.links,
            categories: this.topologyData.categories
          }
        ]
      }
      this.chartInstance.setOption(option)
    }
  }
}
</script>

<style scoped>
.topology-container {
  width: 100%;
  height: 900px; /* 增加容器高度,给纵向更多布局空间 */
  padding: 20px;
  box-sizing: border-box;
}
.chart-box {
  width: 100%;
  height: 100%;
}
</style>

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐