import React, { useState, useEffect, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { FiServer, FiCpu, FiHardDrive, FiActivity, FiClock, FiUser, FiGlobe, FiTerminal, FiFolder, FiRefreshCw, FiChevronRight, FiX, FiZap } from 'react-icons/fi'; function HostInfoPanel({ hostId, connectionId, isConnected, onOpenSFTP, onClose }) { const [hostInfo, setHostInfo] = useState(null); const [systemInfo, setSystemInfo] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [activeTab, setActiveTab] = useState('info'); // 'info' | 'system' // 加载主机基本信息 const loadHostInfo = useCallback(async () => { if (!hostId) return; try { const host = await window.electronAPI.hosts.getById(hostId); setHostInfo(host); } catch (err) { console.error('加载主机信息失败:', err); } }, [hostId]); // 获取系统信息 const fetchSystemInfo = useCallback(async () => { if (!connectionId || !isConnected || !hostInfo) return; setRefreshing(true); try { const result = await window.electronAPI.ssh.exec( { host: hostInfo.host, port: hostInfo.port, username: hostInfo.username, password: hostInfo.password, privateKey: hostInfo.private_key, }, ` echo "===HOSTNAME===$(hostname)" echo "===OS===$(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d'"' -f2 || uname -s)" echo "===KERNEL===$(uname -r)" echo "===UPTIME===$(uptime -p 2>/dev/null || uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1}')" echo "===CPU===$(grep 'model name' /proc/cpuinfo 2>/dev/null | head -1 | cut -d':' -f2 | xargs || sysctl -n machdep.cpu.brand_string 2>/dev/null)" echo "===CPU_CORES===$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null)" echo "===MEMORY===$(free -h 2>/dev/null | awk '/^Mem:/ {print $2}' || echo 'N/A')" echo "===MEMORY_USED===$(free -h 2>/dev/null | awk '/^Mem:/ {print $3}' || echo 'N/A')" echo "===DISK===$(df -h / | awk 'NR==2 {print $2}')" echo "===DISK_USED===$(df -h / | awk 'NR==2 {print $3}')" echo "===DISK_PERCENT===$(df -h / | awk 'NR==2 {print $5}')" echo "===LOAD===$(cat /proc/loadavg 2>/dev/null | awk '{print $1, $2, $3}' || uptime | awk -F'load average:' '{print $2}' | xargs)" echo "===IP===$(hostname -I 2>/dev/null | awk '{print $1}' || ifconfig 2>/dev/null | grep 'inet ' | grep -v 127.0.0.1 | head -1 | awk '{print $2}')" ` ); if (result.stdout) { const parseValue = (key) => { const match = result.stdout.match(new RegExp(`===${key}===(.+)`)); return match ? match[1].trim() : 'N/A'; }; setSystemInfo({ hostname: parseValue('HOSTNAME'), os: parseValue('OS'), kernel: parseValue('KERNEL'), uptime: parseValue('UPTIME'), cpu: parseValue('CPU'), cpuCores: parseValue('CPU_CORES'), memory: parseValue('MEMORY'), memoryUsed: parseValue('MEMORY_USED'), disk: parseValue('DISK'), diskUsed: parseValue('DISK_USED'), diskPercent: parseValue('DISK_PERCENT'), load: parseValue('LOAD'), ip: parseValue('IP'), }); } } catch (err) { console.error('获取系统信息失败:', err); } finally { setLoading(false); setRefreshing(false); } }, [connectionId, isConnected, hostInfo]); useEffect(() => { loadHostInfo(); }, [loadHostInfo]); useEffect(() => { if (hostInfo && isConnected) { fetchSystemInfo(); } }, [hostInfo, isConnected, fetchSystemInfo]); // 计算使用率百分比 const getUsagePercent = (used, total) => { if (!used || !total || used === 'N/A' || total === 'N/A') return 0; const usedNum = parseFloat(used); const totalNum = parseFloat(total); if (isNaN(usedNum) || isNaN(totalNum) || totalNum === 0) return 0; return Math.min(100, Math.round((usedNum / totalNum) * 100)); }; const InfoCard = ({ icon: Icon, label, value, subValue }) => (