feat: support multiple disks in host info panel

This commit is contained in:
Ethanfly 2025-12-31 15:42:43 +08:00
parent d7da96c0c5
commit 2387e6e5f4

View File

@ -47,11 +47,11 @@ function HostInfoPanel({ hostId, connectionId, isConnected, onOpenSFTP, onClose
echo "===CPU_CORES===$(nproc 2>/dev/null || sysctl -n hw.ncpu 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===$(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 "===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 "===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}')" 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}')"
echo "===DISKS_START==="
df -h -T 2>/dev/null | grep -E '^/dev/' | grep -v 'tmpfs\|devtmpfs\|squashfs\|overlay\|loop' | awk '{print $1"|"$2"|"$3"|"$4"|"$6"|"$7}' || df -h 2>/dev/null | grep -E '^/dev/' | awk '{print $1"|unknown|"$2"|"$3"|"$5"|"$6}'
echo "===DISKS_END==="
` `
); );
@ -61,6 +61,26 @@ function HostInfoPanel({ hostId, connectionId, isConnected, onOpenSFTP, onClose
return match ? match[1].trim() : 'N/A'; return match ? match[1].trim() : 'N/A';
}; };
// 解析多硬盘信息
const disksMatch = result.stdout.match(/===DISKS_START===([\s\S]*?)===DISKS_END===/);
const disks = [];
if (disksMatch && disksMatch[1]) {
const diskLines = disksMatch[1].trim().split('\n').filter(line => line.trim());
for (const line of diskLines) {
const parts = line.split('|');
if (parts.length >= 6) {
disks.push({
device: parts[0],
type: parts[1],
total: parts[2],
used: parts[3],
percent: parts[4],
mount: parts[5],
});
}
}
}
setSystemInfo({ setSystemInfo({
hostname: parseValue('HOSTNAME'), hostname: parseValue('HOSTNAME'),
os: parseValue('OS'), os: parseValue('OS'),
@ -70,11 +90,9 @@ function HostInfoPanel({ hostId, connectionId, isConnected, onOpenSFTP, onClose
cpuCores: parseValue('CPU_CORES'), cpuCores: parseValue('CPU_CORES'),
memory: parseValue('MEMORY'), memory: parseValue('MEMORY'),
memoryUsed: parseValue('MEMORY_USED'), memoryUsed: parseValue('MEMORY_USED'),
disk: parseValue('DISK'),
diskUsed: parseValue('DISK_USED'),
diskPercent: parseValue('DISK_PERCENT'),
load: parseValue('LOAD'), load: parseValue('LOAD'),
ip: parseValue('IP'), ip: parseValue('IP'),
disks: disks.length > 0 ? disks : [{ device: '/dev/sda1', type: 'unknown', total: 'N/A', used: 'N/A', percent: '0%', mount: '/' }],
}); });
} }
} catch (err) { } catch (err) {
@ -340,18 +358,45 @@ function HostInfoPanel({ hostId, connectionId, isConnected, onOpenSFTP, onClose
/> />
</div> </div>
{/* 磁盘使用 */} {/* 磁盘使用 - 支持多硬盘 */}
<div className="bg-shell-surface/50 rounded-lg p-3 border border-shell-border/50"> <div className="bg-shell-surface/50 rounded-lg p-3 border border-shell-border/50">
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-3">
<FiHardDrive size={14} className="text-shell-orange" /> <FiHardDrive size={14} className="text-shell-orange" />
<span className="text-shell-text-dim text-xs">磁盘 (/)</span> <span className="text-shell-text-dim text-xs">磁盘</span>
<span className="text-shell-text-dim text-xs ml-auto">
{systemInfo.disks?.length || 0} 个分区
</span>
</div>
<div className="space-y-3 max-h-48 overflow-y-auto custom-scrollbar pr-1">
{systemInfo.disks?.map((disk, index) => (
<div key={index} className="bg-shell-bg/30 rounded-lg p-2">
<div className="flex items-center justify-between text-xs mb-1">
<span className="text-shell-accent font-mono truncate max-w-[120px]" title={disk.mount}>
{disk.mount}
</span>
<span className="text-shell-text-dim text-[10px]" title={disk.device}>
{disk.type !== 'unknown' ? disk.type : disk.device.split('/').pop()}
</span>
</div>
<div className="flex justify-between text-xs mb-1">
<span className="text-shell-text-dim">使用</span>
<span className="text-shell-text">{disk.used} / {disk.total}</span>
</div>
<div className="h-1.5 bg-shell-border/50 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-300"
style={{
width: `${parseInt(disk.percent) || 0}%`,
backgroundColor: parseInt(disk.percent) > 80 ? '#f85149' : parseInt(disk.percent) > 60 ? '#d29922' : '#58a6ff'
}}
/>
</div>
<div className="text-right text-[10px] text-shell-text-dim mt-0.5">
{disk.percent}
</div>
</div>
))}
</div> </div>
<UsageBar
label="使用率"
used={systemInfo.diskUsed}
total={systemInfo.disk}
percent={parseInt(systemInfo.diskPercent) || 0}
/>
</div> </div>
</> </>
) : ( ) : (