// 全局变量 let cy = null; let currentTaskId = null; // 初始化 document.addEventListener('DOMContentLoaded', function() { initCytoscape(); initEventListeners(); loadTopology(); loadDeviceList(); // 加载设备列表 }); // 初始化Cytoscape function initCytoscape() { cy = cytoscape({ container: document.getElementById('cy'), elements: [], style: [ { selector: 'node', style: { 'label': 'data(label)', 'background-color': function(ele) { return getNodeColor(ele.data('type')); }, 'width': 70, 'height': 70, 'border-width': 3, 'border-color': '#667eea', 'text-valign': 'bottom', 'text-halign': 'center', 'font-size': '11px', 'font-weight': 'bold', 'text-wrap': 'wrap', 'text-max-width': '80px' } }, { selector: 'edge', style: { 'width': 2, 'line-color': '#999', 'target-arrow-color': '#999', 'target-arrow-shape': 'triangle', 'curve-style': 'bezier', 'label': 'data(protocol)' } }, { selector: 'node:selected', style: { 'border-width': 5, 'border-color': '#FF9800' } } ], layout: { name: 'cose', animate: true, animationDuration: 1000, padding: 30 } }); // 节点点击事件 cy.on('tap', 'node', function(evt) { const node = evt.target; showDeviceDetail(node.data('id')); }); } // 获取节点颜色 function getNodeColor(type) { const colors = { 'cisco': '#4CAF50', 'huawei': '#2196F3', 'h3c': '#9C27B0', 'asa': '#FF5722', 'linux': '#607D8B', 'windows': '#00BCD4' }; return colors[type] || '#999'; } // 初始化事件监听 function initEventListeners() { // 扫描按钮 document.getElementById('btn-scan').addEventListener('click', startScan); // 添加设备按钮 document.getElementById('btn-add-device').addEventListener('click', function() { document.getElementById('modal').classList.add('active'); }); // 关闭模态框 document.querySelector('.close').addEventListener('click', function() { document.getElementById('modal').classList.remove('active'); }); // 添加设备表单 document.getElementById('add-device-form').addEventListener('submit', addDevice); // 导出按钮 document.getElementById('btn-export').addEventListener('click', exportTopology); } // 开始扫描 async function startScan() { const scanRange = document.getElementById('scan-range').value; const sshPort = document.getElementById('ssh-port').value; const username = document.getElementById('username').value; const password = document.getElementById('password').value; if (!scanRange) { alert('请输入IP范围'); return; } try { const response = await fetch('/api/scan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ scan_range: scanRange, ssh_port: parseInt(sshPort), username: username, password: password }) }); const data = await response.json(); currentTaskId = data.task_id; // 轮询进度 pollProgress(); } catch (error) { console.error('扫描失败:', error); alert('扫描失败: ' + error.message); } } // 轮询进度 async function pollProgress() { if (!currentTaskId) return; const poll = async () => { try { const response = await fetch(`/api/scan/${currentTaskId}`); const task = await response.json(); // 更新进度 document.getElementById('scan-status').textContent = task.status; document.getElementById('scan-progress').textContent = task.progress + '%'; document.getElementById('progress-fill').style.width = task.progress + '%'; // 更新设备列表 updateDeviceList(task.devices); // 如果完成,更新拓扑 if (task.status === 'completed' || task.status === 'failed') { loadTopology(); loadDeviceList(); // 刷新设备列表 currentTaskId = null; return; } // 继续轮询 setTimeout(poll, 1000); } catch (error) { console.error('获取进度失败:', error); } }; poll(); } // 更新设备列表 function updateDeviceList(devices) { const listContainer = document.getElementById('device-list'); listContainer.innerHTML = ''; devices.forEach(device => { const item = document.createElement('div'); item.className = 'device-item'; item.innerHTML = `
暂无设备
'; return; } devices.forEach(device => { const item = document.createElement('div'); item.className = 'device-item'; const interfaceCount = device.interfaces ? device.interfaces.length : 0; const neighborCount = device.neighbors ? device.neighbors.length : 0; item.innerHTML = `IP: ${device.ip}
主机名: ${device.hostname || 'N/A'}
类型: ${device.type}
系统: ${device.os_version || 'N/A'}
运行时间: ${device.uptime || 'N/A'}
状态: ${iface.status}
IP: ${iface.ip || 'N/A'}
MAC: ${iface.mac || 'N/A'}
速度: ${iface.speed || 'N/A'}
本地接口: ${neighbor.local_interface}
远程接口: ${neighbor.remote_interface}
协议: ${neighbor.protocol}