// info-card.js - Unified info card module import { showStatusMessage } from './ui.js'; let currentType = null; let cardMounted = false; const CARD_CONFIG = { cable: { icon: '🛥️', title: '电缆详情', className: 'cable', fields: [ { key: 'name', label: '名称' }, { key: 'owner', label: '所有者' }, { key: 'status', label: '状态' }, { key: 'length', label: '长度' }, { key: 'coords', label: '经纬度' }, { key: 'rfs', label: '投入使用' } ] }, satellite: { icon: '🛰️', title: '卫星详情', className: 'satellite', fields: [ { key: 'name', label: '名称' }, { key: 'norad_id', label: 'NORAD ID' }, { key: 'inclination', label: '倾角', unit: '°' }, { key: 'period', label: '周期', unit: '分钟' }, { key: 'perigee', label: '近地点', unit: 'km' }, { key: 'apogee', label: '远地点', unit: 'km' } ] }, bgp: { icon: '📡', title: 'BGP事件详情', className: 'bgp', fields: [ { key: 'anomaly_type', label: '事件类型' }, { key: 'severity', label: '严重度' }, { key: 'status', label: '状态' }, { key: 'route_change', label: '事件特征' }, { key: 'prefix', label: '前缀' }, { key: 'as_path_display', label: '传播路径' }, { key: 'origin_asn', label: '涉及 ASN' }, { key: 'new_origin_asn', label: '关联 ASN' }, { key: 'confidence', label: '置信度' }, { key: 'collector', label: '主观测站' }, { key: 'observed_by', label: '观测范围' }, { key: 'impacted_scope', label: '影响区域' }, { key: 'related_cables', label: '附近基础设施' }, { key: 'related_satellites', label: '附近卫星' }, { key: 'location', label: '观测位置' }, { key: 'created_at', label: '事件时间' }, { key: 'summary', label: '摘要' } ] }, bgp_collector: { icon: '📍', title: 'BGP观测站详情', className: 'bgp', fields: [ { key: 'collector', label: '采集器' }, { key: 'location', label: '观测位置' }, { key: 'anomaly_count', label: '当前事件数' }, { key: 'observation_count', label: '观测事件数' }, { key: 'recent_24h_observation_count', label: '近24h事件数' }, { key: 'recent_7d_observation_count', label: '近7d事件数' }, { key: 'prefix_count', label: '观测前缀数' }, { key: 'origin_asn_count', label: '观测 ASN 数' }, { key: 'top_event_types', label: '主要事件类型' }, { key: 'coverage_halo', label: '日常活跃度' }, { key: 'related_satellites', label: '附近卫星' }, { key: 'latest_event_type', label: '最近事件类型' }, { key: 'latest_observed_at', label: '最近活跃时间' }, { key: 'baseline_scope', label: '日常覆盖范围' }, { key: 'status', label: '状态' } ] }, supercomputer: { icon: '🖥️', title: '超算详情', className: 'supercomputer', fields: [ { key: 'name', label: '名称' }, { key: 'rank', label: '排名' }, { key: 'r_max', label: 'Rmax', unit: 'GFlops' }, { key: 'r_peak', label: 'Rpeak', unit: 'GFlops' }, { key: 'country', label: '国家' }, { key: 'city', label: '城市' } ] }, gpu_cluster: { icon: '🎮', title: 'GPU集群详情', className: 'gpu_cluster', fields: [ { key: 'name', label: '名称' }, { key: 'country', label: '国家' }, { key: 'city', label: '城市' } ] } }; function getPanel() { return document.getElementById('info-panel'); } function setupInfoCardDrag(panel) { const app = document.getElementById('container'); if (!app) return; const handle = panel.querySelector('.hud-panel-drag-handle'); if (!handle) return; let isDragging = false; let startPointerX = 0; let startPointerY = 0; let startLeft = 0; let startTop = 0; const stopDragging = () => { isDragging = false; panel.classList.remove('is-dragging'); document.body.style.userSelect = ''; }; const onMove = (event) => { if (!isDragging) return; const appRect = app.getBoundingClientRect(); const panelRect = panel.getBoundingClientRect(); const nextLeft = Math.min( Math.max(startLeft + (event.clientX - startPointerX), 0), appRect.width - panelRect.width, ); const nextTop = Math.min( Math.max(startTop + (event.clientY - startPointerY), 0), appRect.height - panelRect.height, ); panel.style.left = `${nextLeft}px`; panel.style.top = `${nextTop}px`; }; handle.addEventListener('pointerdown', (event) => { if (event.target.closest('.hud-panel-close, .info-card-close')) return; isDragging = true; startPointerX = event.clientX; startPointerY = event.clientY; const appRect = app.getBoundingClientRect(); const panelRect = panel.getBoundingClientRect(); startLeft = panelRect.left - appRect.left; startTop = panelRect.top - appRect.top; panel.style.left = `${startLeft}px`; panel.style.top = `${startTop}px`; panel.style.right = 'auto'; panel.style.bottom = 'auto'; panel.classList.add('is-dragging'); document.body.style.userSelect = 'none'; handle.setPointerCapture?.(event.pointerId); }); handle.addEventListener('pointermove', onMove); handle.addEventListener('pointerup', stopDragging); handle.addEventListener('pointercancel', stopDragging); handle.addEventListener('lostpointercapture', stopDragging); } function mountCard() { if (cardMounted) return; const container = document.getElementById('container'); if (!container) return; const panel = document.createElement('div'); panel.id = 'info-panel'; panel.className = 'hud-panel hud-panel-info'; panel.setAttribute('aria-live', 'polite'); panel.innerHTML = `