Vue3 + AntV G6实战:动态切换拓扑图节点图标(在线/离线/异常状态)

发布时间:2026/6/8 4:52:42

Vue3 + AntV G6实战:动态切换拓扑图节点图标(在线/离线/异常状态) Vue3 AntV G6实战动态切换拓扑图节点图标在线/离线/异常状态在现代Web应用中数据可视化已经成为不可或缺的一部分。网络拓扑图作为展示复杂系统结构的利器在运维监控、物联网设备管理等领域发挥着重要作用。本文将带你深入探索如何利用Vue3的响应式特性与AntV G6的强大可视化能力构建一个能够根据后端状态实时更新节点图标的动态拓扑图系统。1. 环境准备与基础配置1.1 项目初始化与依赖安装首先创建一个新的Vue3项目并安装必要的依赖npm create vuelatest vue3-g6-demo cd vue3-g6-demo npm install antv/g61.2 基础拓扑图实现我们先创建一个基本的拓扑图组件NetworkTopology.vuetemplate div refcontainer classtopology-container/div /template script setup import { ref, onMounted } from vue import G6 from antv/g6 const container ref(null) const graph ref(null) const initGraph () { graph.value new G6.Graph({ container: container.value, width: 800, height: 600, modes: { default: [drag-canvas, zoom-canvas, drag-node] }, defaultNode: { type: image, size: [40, 40] } }) // 初始数据 const data { nodes: [ { id: node1, label: 服务器1, status: 1 }, { id: node2, label: 数据库, status: 1 }, { id: node3, label: 网关, status: 0 } ], edges: [ { source: node1, target: node2 }, { source: node1, target: node3 } ] } graph.value.data(data) graph.value.render() } onMounted(() { initGraph() }) /script style .topology-container { width: 100%; height: 100%; border: 1px solid #eee; } /style2. 状态与图标映射系统设计2.1 定义状态枚举与图标资源在项目中创建一个constants.js文件来管理状态和图标映射export const NODE_STATUS { ONLINE: 1, OFFLINE: 0, WARNING: 2 } export const ICON_MAPPING { [NODE_STATUS.ONLINE]: /assets/icons/server-online.png, [NODE_STATUS.OFFLINE]: /assets/icons/server-offline.png, [NODE_STATUS.WARNING]: /assets/icons/server-warning.png }2.2 实现响应式图标更新修改NetworkTopology.vue组件加入状态监听和图标更新逻辑script setup import { ref, onMounted, watch } from vue import G6 from antv/g6 import { NODE_STATUS, ICON_MAPPING } from ./constants const props defineProps({ nodeData: { type: Array, required: true } }) const container ref(null) const graph ref(null) const currentData ref({ nodes: [], edges: [] }) const updateNodeIcons (nodes) { return nodes.map(node ({ ...node, img: ICON_MAPPING[node.status] || ICON_MAPPING[NODE_STATUS.OFFLINE] })) } const initGraph () { graph.value new G6.Graph({ container: container.value, width: 800, height: 600, modes: { default: [drag-canvas, zoom-canvas, drag-node] }, defaultNode: { type: image, size: [40, 40], style: { cursor: pointer } } }) // 初始数据 currentData.value.nodes updateNodeIcons(props.nodeData) graph.value.data(currentData.value) graph.value.render() } watch(() props.nodeData, (newVal) { if (!graph.value) return currentData.value.nodes updateNodeIcons(newVal) graph.value.changeData(currentData.value) }, { deep: true }) onMounted(() { initGraph() }) /script3. 动态状态更新与平滑过渡3.1 模拟实时状态更新创建一个模拟数据服务mockService.jsexport const fetchNodeStatus () { return new Promise((resolve) { setTimeout(() { const nodes [ { id: node1, label: 服务器1, status: Math.floor(Math.random() * 3) }, { id: node2, label: 数据库, status: Math.floor(Math.random() * 3) }, { id: node3, label: 网关, status: Math.floor(Math.random() * 3) } ] resolve(nodes) }, 2000) }) }3.2 在组件中集成实时更新修改主组件以集成实时状态更新script setup import { ref, onMounted } from vue import NetworkTopology from ./components/NetworkTopology.vue import { fetchNodeStatus } from ./services/mockService const nodeData ref([]) const loadData async () { nodeData.value await fetchNodeStatus() } // 初始加载 loadData() // 定时刷新 onMounted(() { setInterval(loadData, 5000) }) /script template div classapp-container h1网络拓扑监控系统/h1 NetworkTopology :node-datanodeData / /div /template4. 高级功能与性能优化4.1 添加节点状态动画为了提升用户体验我们可以为状态变化添加动画效果const updateNodeIcons (nodes) { return nodes.map(node { const newNode { ...node, img: ICON_MAPPING[node.status] || ICON_MAPPING[NODE_STATUS.OFFLINE] } // 添加动画配置 if (node.status ! newNode.status) { newNode.animate { attrs: { opacity: 0 }, duration: 300, easing: easeCubic, callback: () { newNode.animate { attrs: { opacity: 1 }, duration: 300, easing: easeCubic } } } } return newNode }) }4.2 性能优化策略对于大型拓扑图需要考虑以下优化措施按需渲染只更新状态发生变化的节点节流处理对高频更新进行节流控制Web Worker将数据处理移入Web Worker优化后的更新逻辑watch(() props.nodeData, (newVal, oldVal) { if (!graph.value) return const changes newVal.map((newNode, index) { const oldNode oldVal[index] return { ...newNode, changed: oldNode.status ! newNode.status } }) currentData.value.nodes changes.map(node ({ ...node, img: node.changed ? ICON_MAPPING[node.status] : node.img })) graph.value.changeData(currentData.value) }, { deep: true })5. 实战技巧与常见问题5.1 自定义节点样式进阶除了简单的图标替换我们还可以根据状态自定义更多样式const getNodeStyle (status) { const baseStyle { width: 40, height: 40, cursor: pointer } switch(status) { case NODE_STATUS.ONLINE: return { ...baseStyle, shadowColor: #52c41a, shadowBlur: 10 } case NODE_STATUS.WARNING: return { ...baseStyle, shadowColor: #faad14, shadowBlur: 10 } case NODE_STATUS.OFFLINE: return { ...baseStyle, opacity: 0.6 } default: return baseStyle } } // 在updateNodeIcons中添加样式 newNode.style getNodeStyle(node.status)5.2 常见问题排查问题1图片加载失败确保图标路径正确添加错误处理defaultNode: { type: image, size: [40, 40], style: { cursor: pointer }, // 添加图片加载失败的回调 img: fallback-icon.png, onError: (e) { console.error(图片加载失败:, e) } }问题2性能瓶颈使用graph.refreshItem()替代全量更新对大型图考虑使用virtualRender插件// 针对单个节点的更新 const updateSingleNode (nodeId, newStatus) { const node graph.value.findById(nodeId) graph.value.updateItem(node, { img: ICON_MAPPING[newStatus], style: getNodeStyle(newStatus) }) }

相关新闻