HF_catalyst / frontend /components /PingLabServer.tsx
SissiFeng's picture
Initial commit
cdb8847
import React, { useState, useEffect, useCallback } from 'react';
import apiClient from '../api/apiClient';
import { HealthStatus } from '../types/types';
interface PingLabServerProps {
autoCheckInterval?: number; // 自动检查间隔时间(毫秒),默认为30秒
showLatency?: boolean; // 是否显示延迟时间
}
const PingLabServer: React.FC<PingLabServerProps> = ({
autoCheckInterval = 30000,
showLatency = true,
}) => {
const [status, setStatus] = useState<HealthStatus | null>(null);
const [isChecking, setIsChecking] = useState<boolean>(false);
const [lastChecked, setLastChecked] = useState<Date | null>(null);
// 检查服务器状态
const checkServerStatus = useCallback(async () => {
setIsChecking(true);
try {
const result = await apiClient.checkHealth();
if (result.data) {
setStatus(result.data);
} else {
setStatus({
status: 'offline',
message: result.error || '未知错误',
});
}
} catch (error) {
setStatus({
status: 'offline',
message: error instanceof Error ? error.message : '检查失败',
});
} finally {
setIsChecking(false);
setLastChecked(new Date());
}
}, []);
// 手动检查
const handleManualCheck = () => {
checkServerStatus();
};
// 自动定时检查
useEffect(() => {
// 初始检查
checkServerStatus();
// 设置定时器
const intervalId = setInterval(checkServerStatus, autoCheckInterval);
// 清理定时器
return () => clearInterval(intervalId);
}, [checkServerStatus, autoCheckInterval]);
// 格式化上次检查时间
const getFormattedLastChecked = () => {
if (!lastChecked) return '尚未检查';
// 格式化为 HH:MM:SS
return lastChecked.toLocaleTimeString();
};
return (
<div className={`ping-server ${status?.status || 'unknown'}`}>
<h3>服务器状态</h3>
<div className="status-display">
<div className="status-indicator">
<span className={`status-dot ${status?.status || 'unknown'}`}></span>
<span className="status-text">
{status?.status === 'online' ? '在线' :
status?.status === 'offline' ? '离线' : '未知'}
</span>
</div>
{status?.message && (
<div className="status-message">{status.message}</div>
)}
{showLatency && status?.latency && (
<div className="status-latency">
延迟: {status.latency}ms
</div>
)}
<div className="last-checked">
上次检查: {getFormattedLastChecked()}
</div>
</div>
<button
onClick={handleManualCheck}
disabled={isChecking}
className="check-button"
>
{isChecking ? '检查中...' : '检查状态'}
</button>
<style jsx>{`
.ping-server {
padding: 15px;
border-radius: 6px;
background-color: #f8f9fa;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
}
.ping-server.online {
border-left: 4px solid #28a745;
}
.ping-server.offline {
border-left: 4px solid #dc3545;
}
.ping-server.unknown {
border-left: 4px solid #6c757d;
}
h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 18px;
}
.status-display {
margin-bottom: 15px;
}
.status-indicator {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
}
.status-dot.online {
background-color: #28a745;
box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
animation: pulse 2s infinite;
}
.status-dot.offline {
background-color: #dc3545;
}
.status-dot.unknown {
background-color: #6c757d;
}
.status-text {
font-weight: 500;
}
.status-message {
font-size: 14px;
margin-bottom: 8px;
}
.status-latency {
font-size: 14px;
color: #495057;
margin-bottom: 8px;
}
.last-checked {
font-size: 12px;
color: #6c757d;
}
.check-button {
padding: 6px 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.check-button:hover:not(:disabled) {
background-color: #0069d9;
}
.check-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.6);
}
70% {
box-shadow: 0 0 0 6px rgba(40, 167, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0);
}
}
`}</style>
</div>
);
};
export default PingLabServer;