import { useEffect, useState } from 'react' import { statsAPI } from '../api/stats' import type { Hardware, StatsSnapshot } from '../types' import { AggregateStatsSection, StatsHeader, StatsTiles, SystemMonitorSection, } from './StatsPageSections' // StatsPage renders the operator dashboard. Aggregate stats refresh less often, // while hardware metrics are polled every 2 s for a real-time monitoring feel. export function StatsPage() { const [snap, setSnap] = useState(null) const [hardware, setHardware] = useState(null) const [lastMonitorAt, setLastMonitorAt] = useState('') const [monitorError, setMonitorError] = useState('') const [loading, setLoading] = useState(true) useEffect(() => { let cancelled = false const tick = () => statsAPI.snapshot().then((s) => { if (!cancelled) { setSnap(s) setHardware((current) => current ?? s.hardware) setLastMonitorAt((current) => current || s.generated_at) } }) tick().finally(() => setLoading(false)) const id = window.setInterval(tick, 30_000) return () => { cancelled = true window.clearInterval(id) } }, []) useEffect(() => { let cancelled = false const tick = () => statsAPI .monitor() .then((m) => { if (!cancelled) { setHardware(m) setLastMonitorAt(new Date().toISOString()) setMonitorError('') } }) .catch((err: unknown) => { if (!cancelled) { setMonitorError((err as { message?: string })?.message ?? '实时监控暂不可用') } }) tick() const id = window.setInterval(tick, 2_000) return () => { cancelled = true window.clearInterval(id) } }, []) if (loading) return

加载中…

if (!snap) return

无法获取统计数据

const live = hardware ?? snap.hardware const memPct = live.memory_total > 0 ? (live.memory_used / live.memory_total) * 100 : 0 const diskPct = live.disk_total > 0 ? (live.disk_used / live.disk_total) * 100 : 0 return (
) }