Files
mengyaping/mengyaping-frontend/src/components/StatsCard.jsx
2026-05-16 19:03:32 +08:00

140 lines
6.0 KiB
JavaScript

import { formatUptime, getUptimeColor } from '../hooks/useMonitor';
// 统计概览卡片
export default function StatsCard({ websites }) {
// 计算统计数据
const stats = {
total: websites?.length || 0,
online: 0,
offline: 0,
avgUptime24h: 0,
avgUptime7d: 0,
avgLatency: 0,
};
if (websites && websites.length > 0) {
let totalUptime24h = 0;
let totalUptime7d = 0;
let totalLatency = 0;
let latencyCount = 0;
websites.forEach(site => {
// 检查所有URL的状态
const hasOnlineUrl = site.url_statuses?.some(us => us.current_state?.is_up);
if (hasOnlineUrl) {
stats.online++;
} else {
stats.offline++;
}
totalUptime24h += site.uptime_24h || 0;
totalUptime7d += site.uptime_7d || 0;
site.url_statuses?.forEach(us => {
if (us.current_state?.latency) {
totalLatency += us.current_state.latency;
latencyCount++;
}
});
});
stats.avgUptime24h = totalUptime24h / stats.total;
stats.avgUptime7d = totalUptime7d / stats.total;
stats.avgLatency = latencyCount > 0 ? Math.round(totalLatency / latencyCount) : 0;
}
return (
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{/* 监控网站数 */}
<div className="bg-white rounded-xl p-4 shadow-sm border border-gray-100">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500">监控网站</p>
<p className="text-2xl font-bold text-gray-800 mt-1">{stats.total}</p>
</div>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center">
<svg className="w-6 h-6 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
</svg>
</div>
</div>
<div className="flex items-center mt-3 text-xs">
<span className="text-green-500 flex items-center">
<span className="w-2 h-2 rounded-full bg-green-500 mr-1"></span>
{stats.online} 在线
</span>
<span className="text-gray-300 mx-2">|</span>
<span className="text-red-500 flex items-center">
<span className="w-2 h-2 rounded-full bg-red-500 mr-1"></span>
{stats.offline} 离线
</span>
</div>
</div>
{/* 24小时可用率 */}
<div className="bg-white rounded-xl p-4 shadow-sm border border-gray-100">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500">24h 可用率</p>
<p className={`text-2xl font-bold mt-1 ${getUptimeColor(stats.avgUptime24h)}`}>
{formatUptime(stats.avgUptime24h)}
</p>
</div>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-emerald-50 to-emerald-100 flex items-center justify-center">
<svg className="w-6 h-6 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
<div className="mt-3 h-1.5 bg-gray-100 rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-emerald-400 to-green-500 rounded-full"
style={{ width: `${Math.min(stats.avgUptime24h, 100)}%` }}
/>
</div>
</div>
{/* 7天可用率 */}
<div className="bg-white rounded-xl p-4 shadow-sm border border-gray-100">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500">7d 可用率</p>
<p className={`text-2xl font-bold mt-1 ${getUptimeColor(stats.avgUptime7d)}`}>
{formatUptime(stats.avgUptime7d)}
</p>
</div>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-green-50 to-green-100 flex items-center justify-center">
<svg className="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
</div>
<div className="mt-3 h-1.5 bg-gray-100 rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-green-400 to-teal-500 rounded-full"
style={{ width: `${Math.min(stats.avgUptime7d, 100)}%` }}
/>
</div>
</div>
{/* 平均延迟 */}
<div className="bg-white rounded-xl p-4 shadow-sm border border-gray-100">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-500">平均延迟</p>
<p className="text-2xl font-bold text-gray-800 mt-1">
{stats.avgLatency}
<span className="text-sm font-normal text-gray-500 ml-1">ms</span>
</p>
</div>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-50 to-purple-100 flex items-center justify-center">
<svg className="w-6 h-6 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
</div>
</div>
</div>
);
}