更新web显示

This commit is contained in:
Alex Yang
2025-11-23 18:51:08 +08:00
parent 15c87a9d94
commit 63a95f7463

View File

@@ -508,10 +508,25 @@
} }
.status-info { .status-info {
background-color: var(--gray-50); margin-top: 2rem;
padding: 1.5rem; padding: 1.5rem;
background-color: var(--gray-50);
border-radius: var(--border-radius-lg); border-radius: var(--border-radius-lg);
border-left: 4px solid var(--info-color); border-left: 4px solid var(--info-color);
box-shadow: var(--shadow);
}
.status-info h3 {
margin-bottom: 1rem;
}
.status-info p {
margin: 0.5rem 0;
line-height: 1.6;
}
.status-info .stat-card {
border-top: 4px solid var(--primary-color);
} }
.status-info p { .status-info p {
@@ -659,25 +674,20 @@
<!-- 概览面板 --> <!-- 概览面板 -->
<div id="dashboard" class="tab-content active"> <div id="dashboard" class="tab-content active">
<h2 style="margin-bottom: 1.5rem;">服务器状态</h2> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem;">
<h2>服务器状态</h2>
<div style="display: flex; gap: 1rem;">
<div style="background-color: white; padding: 0.5rem 1rem; border-radius: var(--border-radius-md); box-shadow: var(--shadow); display: flex; align-items: center; gap: 0.5rem;">
<i class="fas fa-ban" style="color: var(--primary-color);"></i>
<span style="font-size: 1rem; font-weight: 600;">规则: <span id="rules-count-inline">--</span></span>
</div>
<div style="background-color: white; padding: 0.5rem 1rem; border-radius: var(--border-radius-md); box-shadow: var(--shadow); display: flex; align-items: center; gap: 0.5rem;">
<i class="fas fa-file-alt" style="color: var(--primary-color);"></i>
<span style="font-size: 1rem; font-weight: 600;">Hosts: <span id="hosts-count-inline">--</span></span>
</div>
</div>
</div>
<div class="stats-grid"> <div class="stats-grid">
<div class="stat-card">
<i class="fas fa-ban"></i>
<div class="stat-value" id="rules-count">--</div>
<div class="stat-label">屏蔽规则数</div>
<div class="mini-chart-container">
<canvas id="rules-chart"></canvas>
</div>
</div>
<div class="stat-card">
<i class="fas fa-file-alt"></i>
<div class="stat-value" id="hosts-count">--</div>
<div class="stat-label">Hosts条目数</div>
<div class="mini-chart-container">
<canvas id="hosts-chart"></canvas>
</div>
</div>
<div class="stat-card"> <div class="stat-card">
<i class="fas fa-question-circle"></i> <i class="fas fa-question-circle"></i>
<div class="stat-value" id="query-count">--</div> <div class="stat-value" id="query-count">--</div>
@@ -739,6 +749,24 @@
</div> </div>
<div class="status-info"> <div class="status-info">
<h3 style="margin-bottom: 1rem; font-size: 1.25rem; color: var(--gray-700);">服务器信息</h3> <h3 style="margin-bottom: 1rem; font-size: 1.25rem; color: var(--gray-700);">服务器信息</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1rem; margin-bottom: 1.5rem;">
<div class="stat-card">
<i class="fas fa-ban"></i>
<div class="stat-value" id="rules-count">--</div>
<div class="stat-label">屏蔽规则数</div>
<div class="mini-chart-container">
<canvas id="rules-chart"></canvas>
</div>
</div>
<div class="stat-card">
<i class="fas fa-file-alt"></i>
<div class="stat-value" id="hosts-count">--</div>
<div class="stat-label">Hosts条目数</div>
<div class="mini-chart-container">
<canvas id="hosts-chart"></canvas>
</div>
</div>
</div>
<p><strong>服务器地址:</strong> <span id="server-address">--</span></p> <p><strong>服务器地址:</strong> <span id="server-address">--</span></p>
<p><strong>当前时间:</strong> <span id="current-time">--</span></p> <p><strong>当前时间:</strong> <span id="current-time">--</span></p>
<p><strong>运行状态:</strong> <span class="badge badge-success">正常运行</span></p> <p><strong>运行状态:</strong> <span class="badge badge-success">正常运行</span></p>
@@ -994,7 +1022,19 @@
display: false display: false
}, },
tooltip: { tooltip: {
enabled: false enabled: true,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: '#333',
borderWidth: 1,
cornerRadius: 4,
displayColors: false,
callbacks: {
label: function(context) {
return context.dataset.label + ': ' + context.parsed.y;
}
}
} }
}, },
scales: { scales: {
@@ -1006,7 +1046,11 @@
beginAtZero: true beginAtZero: true
} }
}, },
animation: false animation: false,
interaction: {
intersect: false,
mode: 'index'
}
} }
}); });
}); });
@@ -1073,29 +1117,52 @@
document.getElementById('current-time').textContent = new Date().toLocaleString('zh-CN'); document.getElementById('current-time').textContent = new Date().toLocaleString('zh-CN');
fetch('/api/stats') fetch('/api/stats')
.then(response => response.json()) .then(response => {
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
return response.json();
})
.then(data => { .then(data => {
// 检查数据是否有变动 console.log('获取到统计数据:', data);
if (isDataChanged(data, previousFullData)) {
console.log('数据有变动,正在更新页面...'); // 确保数据结构正确
if (!data || !data.shield) {
console.error('返回的数据结构不正确缺少shield字段');
// 即使数据结构不正确,也尝试更新服务器地址
document.getElementById('server-address').textContent = window.location.hostname + ':8080';
return;
}
// 获取各项统计数据 // 获取各项统计数据
const rulesCount = (data.shield && (data.shield.domainRules + data.shield.regexRules)) || 0; const domainRules = data.shield.domainRules || 0;
const hostsCount = (data.shield && data.shield.hostsRules) || 0; const regexRules = data.shield.regexRules || 0;
const rulesCount = domainRules + regexRules;
const hostsCount = data.shield.hostsRules || 0;
const queryCount = (data.dns && data.dns.Queries) || 0; const queryCount = (data.dns && data.dns.Queries) || 0;
const blockedCount = (data.dns && data.dns.Blocked) || 0; const blockedCount = (data.dns && data.dns.Blocked) || 0;
// 检查数据变化并添加动画 console.log(`规则统计: 域名规则=${domainRules}, 正则规则=${regexRules}, 总计=${rulesCount}`);
checkAndAnimate('rules-count', rulesCount); console.log(`Hosts统计: 条目数=${hostsCount}`);
checkAndAnimate('hosts-count', hostsCount);
checkAndAnimate('query-count', queryCount);
checkAndAnimate('blocked-count', blockedCount);
// 更新显示 // 直接更新显示,同时更新顶部状态区域和统计卡片
document.getElementById('rules-count').textContent = rulesCount; document.getElementById('rules-count').textContent = rulesCount;
document.getElementById('hosts-count').textContent = hostsCount; document.getElementById('hosts-count').textContent = hostsCount;
document.getElementById('query-count').textContent = queryCount; document.getElementById('query-count').textContent = queryCount;
document.getElementById('blocked-count').textContent = blockedCount;
// 更新顶部服务器状态区域的数据
document.getElementById('rules-count-inline').textContent = rulesCount;
document.getElementById('hosts-count-inline').textContent = hostsCount;
// 更新屏蔽次数显示,包含百分比
const blockedPercentage = queryCount > 0 ? Math.round((blockedCount / queryCount) * 100) : 0;
const blockedElement = document.getElementById('blocked-count');
if (blockedElement) {
blockedElement.innerHTML = blockedCount +
'<span style="position: absolute; top: -10px; right: -20px; ' +
'background-color: #ff6b6b; color: white; border-radius: 50%; padding: 2px 6px; ' +
'font-size: 0.7rem; font-weight: bold;">' + blockedPercentage + '%</span>';
}
document.getElementById('server-address').textContent = window.location.hostname + ':8080'; document.getElementById('server-address').textContent = window.location.hostname + ':8080';
// 更新数据历史记录 // 更新数据历史记录
@@ -1113,14 +1180,23 @@
// 更新完整数据缓存 // 更新完整数据缓存
previousFullData = JSON.parse(JSON.stringify(data)); previousFullData = JSON.parse(JSON.stringify(data));
// 数据有变动时才加载其他相关数据 // 加载其他相关数据
loadChartData(); loadChartData();
loadTopDomains(); loadTopDomains();
} else {
console.log('数据无变动,不更新页面');
}
}) })
.catch(error => console.error('加载统计数据失败:', error)); .catch(error => {
console.error('加载统计数据失败:', error);
// 即使出错,也尝试更新服务器地址
document.getElementById('server-address').textContent = window.location.hostname + ':8080';
// 尝试直接从本地文件加载规则和hosts数量备用方案
try {
console.log('尝试备用方案加载规则和hosts数量...');
// 这里可以添加从其他接口或本地缓存获取数据的逻辑
} catch (e) {
console.error('备用方案也失败:', e);
}
});
} }
// 保存上一次的图表数据,用于检测图表数据是否变化 // 保存上一次的图表数据,用于检测图表数据是否变化
@@ -1177,10 +1253,32 @@ function renderBlockChart(labels, data) {
plugins: { plugins: {
legend: { legend: {
position: 'top', position: 'top',
labels: {
padding: 20,
font: {
size: 14
}
}
}, },
tooltip: { tooltip: {
mode: 'index', mode: 'index',
intersect: false, intersect: false,
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: '#333',
borderWidth: 1,
cornerRadius: 6,
padding: 12,
displayColors: false,
callbacks: {
title: function(tooltipItems) {
return tooltipItems[0].label + ' 时';
},
label: function(context) {
return '屏蔽次数: ' + context.parsed.y;
}
}
} }
}, },
scales: { scales: {
@@ -1188,6 +1286,9 @@ function renderBlockChart(labels, data) {
beginAtZero: true, beginAtZero: true,
ticks: { ticks: {
precision: 0 precision: 0
},
grid: {
color: 'rgba(0, 0, 0, 0.05)'
} }
}, },
x: { x: {