Files
wushu/server_monitor.html
2026-03-30 02:35:31 +08:00

418 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>服务器监控面板</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
color: white;
margin-bottom: 30px;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.header p {
font-size: 1.1em;
opacity: 0.9;
}
.status-badge {
display: inline-block;
padding: 8px 20px;
background: rgba(255,255,255,0.2);
border-radius: 20px;
margin-top: 15px;
font-size: 0.9em;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.card {
background: white;
border-radius: 16px;
padding: 24px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.card-icon {
width: 50px;
height: 50px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-right: 15px;
}
.card-title {
font-size: 1.3em;
font-weight: 600;
color: #2c3e50;
}
.metric {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
}
.metric-label {
color: #6c757d;
font-size: 0.95em;
}
.metric-value {
font-weight: 600;
color: #2c3e50;
font-size: 1.1em;
}
.progress-container {
margin-top: 15px;
}
.progress-bar {
height: 8px;
background: #e9ecef;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: 4px;
transition: width 0.5s ease;
}
.progress-text {
text-align: right;
margin-top: 5px;
font-size: 0.85em;
color: #6c757d;
}
.latency-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
margin-bottom: 10px;
background: #f8f9fa;
border-radius: 8px;
}
.latency-host {
font-weight: 500;
color: #2c3e50;
}
.latency-value {
font-weight: 600;
padding: 4px 12px;
border-radius: 6px;
font-size: 0.9em;
}
.latency-good {
background: #d4edda;
color: #155724;
}
.latency-medium {
background: #fff3cd;
color: #856404;
}
.latency-bad {
background: #f8d7da;
color: #721c24;
}
.latency-offline {
background: #e2e3e5;
color: #6c757d;
}
.refresh-btn {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: white;
border: none;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
cursor: pointer;
font-size: 24px;
transition: transform 0.3s ease;
z-index: 1000;
}
.refresh-btn:hover {
transform: scale(1.1);
}
.refresh-btn:active {
transform: scale(0.95);
}
.refresh-btn.loading {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.last-update {
text-align: center;
color: white;
margin-top: 20px;
font-size: 0.9em;
opacity: 0.8;
}
.error-message {
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
text-align: center;
}
@media (max-width: 768px) {
.header h1 {
font-size: 1.8em;
}
.grid {
grid-template-columns: 1fr;
}
.refresh-btn {
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
font-size: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🖥️ 服务器监控面板</h1>
<p>实时监控服务器状态与性能</p>
<div class="status-badge" id="statusBadge">正在加载...</div>
</div>
<div id="errorMessage"></div>
<div class="grid">
<!-- 服务器负载 -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
📊
</div>
<div class="card-title">服务器负载</div>
</div>
<div id="loadMetrics"></div>
</div>
<!-- 网络延迟 -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background: linear-gradient(135deg, #30cfd0 0%, #330867 100%);">
🌐
</div>
<div class="card-title">网络延迟</div>
</div>
<div id="latencyMetrics"></div>
</div>
<!-- 服务器响应时间 -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
</div>
<div class="card-title">服务器响应时间</div>
</div>
<div id="responseTimeMetrics"></div>
</div>
</div>
<div class="last-update" id="lastUpdate">最后更新: --</div>
</div>
<button class="refresh-btn" id="refreshBtn" onclick="fetchServerData()">
🔄
</button>
<script>
async function fetchServerData() {
const refreshBtn = document.getElementById('refreshBtn');
const statusBadge = document.getElementById('statusBadge');
const errorMessage = document.getElementById('errorMessage');
// 记录前端发起请求的时间(用于计算用户网络延迟)
const frontendStartTime = performance.now();
refreshBtn.classList.add('loading');
statusBadge.textContent = '正在更新...';
errorMessage.innerHTML = '';
try {
const response = await fetch('server_monitor_api.php');
const data = await response.json();
// 计算用户网络延迟(前端发起请求到收到响应的总时间)
const frontendEndTime = performance.now();
const userNetworkLatency = (frontendEndTime - frontendStartTime).toFixed(2);
if (data.status === 'success') {
updateDisplay(data, userNetworkLatency);
statusBadge.textContent = '✅ 运行正常';
statusBadge.style.background = 'rgba(40, 167, 69, 0.2)';
} else {
throw new Error('服务器返回错误状态');
}
} catch (error) {
console.error('获取服务器数据失败:', error);
errorMessage.innerHTML = `<div class="error-message">❌ 获取服务器数据失败: ${error.message}</div>`;
statusBadge.textContent = '❌ 连接失败';
statusBadge.style.background = 'rgba(220, 53, 69, 0.2)';
} finally {
refreshBtn.classList.remove('loading');
}
}
function updateDisplay(data, userNetworkLatency) {
// 更新时间戳
const timestamp = data.timestamp;
document.getElementById('lastUpdate').textContent =
`最后更新: ${timestamp.datetime} (${timestamp.timezone})`;
// 更新服务器负载
const load = data.server.load;
document.getElementById('loadMetrics').innerHTML = `
<div class="metric">
<span class="metric-label">1分钟负载</span>
<span class="metric-value">${load['1min']}</span>
</div>
<div class="metric">
<span class="metric-label">5分钟负载</span>
<span class="metric-value">${load['5min']}</span>
</div>
<div class="metric">
<span class="metric-label">15分钟负载</span>
<span class="metric-value">${load['15min']}</span>
</div>
`;
// 更新网络延迟
const latency = data.network.latency;
let latencyHTML = '';
latency.forEach(item => {
const latencyClass = getLatencyClass(item.latency, item.status);
latencyHTML += `
<div class="latency-item">
<div>
<div class="latency-host">${item.host}</div>
<div style="font-size: 0.8em; color: #6c757d;">${item.ip}</div>
</div>
<div class="latency-value ${latencyClass}">
${item.status === 'online' ? item.latency + ' ms' : '离线'}
</div>
</div>
`;
});
document.getElementById('latencyMetrics').innerHTML = latencyHTML;
// 更新服务器响应时间和用户网络延迟
const serverResponseTime = data.network.server_response_time;
document.getElementById('responseTimeMetrics').innerHTML = `
<div class="metric">
<span class="metric-label">服务器响应时间</span>
<span class="metric-value">${serverResponseTime} ms</span>
</div>
<div class="metric">
<span class="metric-label">用户网络延迟</span>
<span class="metric-value">${userNetworkLatency} ms</span>
</div>
<div class="metric">
<span class="metric-label">总延迟</span>
<span class="metric-value">${(parseFloat(serverResponseTime) + parseFloat(userNetworkLatency)).toFixed(2)} ms</span>
</div>
`;
}
function getLatencyClass(latency, status) {
if (status === 'offline') return 'latency-offline';
if (latency < 50) return 'latency-good';
if (latency < 150) return 'latency-medium';
return 'latency-bad';
}
// 页面加载时自动获取数据
window.onload = function() {
fetchServerData();
// 每30秒自动刷新
setInterval(fetchServerData, 30000);
};
</script>
</body>
</html>