如何在Vue中使用拓扑图功能

前言

该组件基于 Vue.js 和 AntV G6 构建

项目特色功能

1. 丰富的节点图标支持

本拓扑图系统的最大特色是支持使用自定义图片作为节点图标

2. 智能的力导向布局

系统采用力导向布局算法,能够自动优化节点位置,避免重叠,形成美观的网络拓扑结构。用户无需手动调整节点位置,系统会自动计算出最佳的显示效果。

3. 丰富的交互功能

  • 拖拽操作:支持节点拖拽,用户可以手动调整布局
  • 缩放平移:支持画布缩放和平移,方便查看大型网络
  • 悬停效果:鼠标悬停显示详细信息
  • 点击复制:点击节点可将信息复制到剪贴板

4. 漏洞信息展示

边缘连接不仅表示网络连接关系,还能显示具体的漏洞信息,如:

  • SQL注入漏洞
  • XSS跨站脚本攻击
  • 网络协议漏洞
  • 配置错误等

第一步:安装依赖

首先确保项目中已安装必要的依赖包:

bash 复制代码
npm install @antv/g6 vue axios

第二步:准备图标资源

在项目的 `public/目录下放置设备图标文件:

复制代码
public/

    defalut.png          # 默认设备图标
    kylin_high.png       # 麒麟系统高危
    linux_mid.png        # Linux中危
    win_high.png         # Windows高危
    win_low.png          # Windows低危
    win_safety.png       # Windows安全
    linux_high.png       # Linux高危
    linux_low.png        # Linux低危

第三步:创建拓扑图组件

创建主要的拓扑图组件文件:

vue 复制代码
<template>
  <div class="topology-container">
    <div id="container" class="topology-canvas"></div>
    <screen-component 
      :objF="objF"
      :currentEve="currentEve"
      :questionList="questionList"
      :list="list"
      @realType="handleRealType"
    />
  </div>
</template>

<script>
import { Graph, NodeEvent } from '@antv/g6'
import axios from 'axios'
import ScreenComponent from './screen.vue'

export default {
  name: 'TopologyViewer',
  components: {
    ScreenComponent
  },
  data() {
    return {
      graphHH: null,
      objF: {},
      currentEve: [],
      questionList: [],
      list: []
    }
  }
}
</script>

第四步:实现数据获取逻辑

javascript 复制代码
methods: {
  async fetchTopologyData(type) {
    try {
      const urlParams = new URLSearchParams(window.location.search);
      const taskId = urlParams.get('taskId');
      
      const response = await axios.get(
        `/api/topology?taskId=${taskId}&type=${type}`,
        {
          headers: {
            'platform-token': 'your-token-here'
          }
        }
      );
      
      if (response.data.code === 200) {
        const topologyData = response.data.data;
        this.processTopologyData(topologyData);
      }
    } catch (error) {
      console.error('获取拓扑数据失败:', error);
      this.useDefaultData();
    }
  }
}

第五步:初始化拓扑图

javascript 复制代码
initTopology(allData) {
  // 销毁现有实例
  if (this.graphHH) {
    this.graphHH.destroy();
    this.graphHH = null;
  }

  const graph = new Graph({
    container: document.getElementById("container"),
    width: 1520,
    height: 900,
    data: {
      nodes: allData.nodes.map((node) => ({
        id: node.id,
        type: node.type || "circle",
        style: {
          size: node.size || 60,
          labelText: `${node.name}\n${node.title}`,
          labelFill: '#fff',
          src: node.style?.src,
          fill: "rgba(255,255,255,.9)"
        }
      })),
      edges: allData.edges.map((edge) => ({
        id: edge.id,
        source: edge.source,
        target: edge.target,
        style: {
          labelText: edge.vulName,
          stroke: "#aaa",
          lineWidth: 1,
          labelFill: "rgba(255,255,255,.8)"
        }
      }))
    },
    layout: {
      type: "force"
    },
    behaviors: [
      'hover-activate',
      'drag-canvas',
      'zoom-canvas', 
      'drag-node',
      'drag-element'
    ]
  });

  // 绑定事件
  this.bindEvents(graph);
  
  this.graphHH = graph;
  graph.render();
}

数据结构说明

节点数据结构

javascript 复制代码
{
  id: 'firewall-001',           // 唯一标识
  name: '外网防火墙',            // 显示名称
  type: 'image',                // 节点类型
  size: 80,                     // 节点大小
  title: '192.168.1.1',         // 副标题(如IP地址)
  style: {
    src: '/img2/firewall.png'   // 图标路径
  }
}

边缘数据结构

javascript 复制代码
{
  id: 'edge-001',               // 唯一标识
  source: 'firewall-001',       // 源节点ID
  target: 'router-001',         // 目标节点ID
  vulName: '防火墙策略配置漏洞'   // 漏洞描述
}

高级功能实现

1. 节点状态管理

实现不同状态下节点的视觉效果:

javascript 复制代码
// 设置节点状态
setNodeState(nodeId, state) {
  const graph = this.graphHH;
  
  switch(state) {
    case 'active':
      graph.updateNodeData([{
        id: nodeId,
        style: {
          stroke: '#ff4d4f',
          shadowBlur: 10,
          shadowColor: 'rgba(255, 77, 79, 0.5)'
        }
      }]);
      break;
    case 'normal':
      graph.updateNodeData([{
        id: nodeId,
        style: {
          stroke: '#d9d9d9',
          shadowBlur: 0
        }
      }]);
      break;
  }
  
  graph.draw();
}

2. 动态数据更新

支持实时更新拓扑数据:

javascript 复制代码
updateTopologyData(newData) {
  const graph = this.graphHH;
  
  // 更新节点
  if (newData.nodes) {
    graph.updateData({
      nodes: newData.nodes.map(node => ({
        id: node.id,
        style: node.style
      }))
    });
  }
  
  // 更新边缘
  if (newData.edges) {
    graph.updateData({
      edges: newData.edges.map(edge => ({
        id: edge.id,
        style: edge.style
      }))
    });
  }
}

3. 搜索和筛选功能

实现节点搜索和类型筛选:

javascript 复制代码
searchNodes(keyword) {
  const graph = this.graphHH;
  const allNodes = graph.getNodes();
  
  allNodes.forEach(node => {
    const nodeData = graph.getNodeData(node.id);
    const isMatch = nodeData.name.includes(keyword) || 
                   nodeData.title.includes(keyword);
    
    graph.updateNodeData([{
      id: node.id,
      style: {
        opacity: isMatch ? 1 : 0.3
      }
    }]);
  });
  
  graph.draw();
}

filterByType(deviceType) {
  const graph = this.graphHH;
  const allNodes = graph.getNodes();
  
  allNodes.forEach(node => {
    const nodeData = graph.getNodeData(node.id);
    const isVisible = deviceType === 'all' || 
                     nodeData.deviceType === deviceType;
    
    graph.updateNodeData([{
      id: node.id,
      style: {
        display: isVisible ? 'block' : 'none'
      }
    }]);
  });
  
  graph.draw();
}

性能优化建议

1. 大数据量处理

当节点数量超过1000个时,建议采用以下优化策略:

javascript 复制代码
// 启用性能模式
const graph = new Graph({
  // ... 其他配置
  modes: {
    default: ['drag-canvas', 'zoom-canvas']
  },
  // 禁用一些耗性能的功能
  animate: false,
  groupByTypes: false
});

2. 内存管理

正确的实例销毁:

javascript 复制代码
beforeDestroy() {
  if (this.graphHH) {
    this.graphHH.destroy();
    this.graphHH = null;
  }
  
  // 清理定时器
  if (this.intervalId) {
    clearInterval(this.intervalId);
  }
}

3. 事件优化

避免频繁的事件触发:

javascript 复制代码
// 使用防抖处理鼠标事件
const debouncedHover = debounce((e) => {
  // 悬停处理逻辑
}, 100);

graph.on(NodeEvent.POINTER_ENTER, debouncedHover);

样式定制

CSS样式配置

css 复制代码
.topology-container {
  width: 100%;
  height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  position: relative;
}

.topology-canvas {
  width: 100%;
  height: 100%;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

/* 节点标签样式 */
.g6-tooltip {
  background: rgba(0, 0, 0, 0.8);
  color: white;
  border-radius: 4px;
  padding: 8px 12px;
  font-size: 12px;
}

常见问题及解决方案

  1. 图标不显示:检查图片路径是否正确,确保图片文件存在
  2. 节点重叠:调整力导向布局参数或增加画布大小
  3. 性能问题:减少节点数量或禁用动画效果
  4. 事件不响应:检查事件绑定是否正确,确保DOM元素已渲染
相关推荐
二哈喇子!1 小时前
Vue3 组合式API
前端·javascript·vue.js
二哈喇子!2 小时前
Vue 组件化开发
前端·javascript·vue.js
chxii3 小时前
2.9 插槽
前端·javascript·vue.js
姑苏洛言4 小时前
扫码点餐小程序产品需求分析与功能梳理
前端·javascript·后端
Freedom风间4 小时前
前端必学-完美组件封装原则
前端·javascript·设计模式
江城开朗的豌豆4 小时前
React表单控制秘籍:受控组件这样玩就对了!
前端·javascript·react.js
一枚前端小能手4 小时前
📋 代码片段管理大师 - 5个让你的代码复用率翻倍的管理技巧
前端·javascript
国家不保护废物4 小时前
Web Worker 多线程魔法:告别卡顿,轻松实现图片压缩!😎
前端·javascript·面试
老华带你飞5 小时前
生产管理ERP系统|物联及生产管理ERP系统|基于SprinBoot+vue的制造装备物联及生产管理ERP系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·论文·制造·毕设·生产管理erp系统