增加websocket,数据实时显示
This commit is contained in:
@@ -260,6 +260,30 @@
|
|||||||
<div id="server-queries-bar" class="h-full bg-primary rounded-full" style="width: 0%"></div>
|
<div id="server-queries-bar" class="h-full bg-primary rounded-full" style="width: 0%"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 额外指标区域 - 初始隐藏,只在非首页显示 -->
|
||||||
|
<div id="server-additional-stats" class="hidden flex items-center">
|
||||||
|
<div class="w-1 h-8 bg-gray-200 rounded-full mx-1"></div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="text-xs font-medium text-gray-500">总量</span>
|
||||||
|
<span id="server-total-queries" class="ml-2 text-sm font-semibold">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-1 h-8 bg-gray-200 rounded-full mx-1"></div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="text-xs font-medium text-gray-500">屏蔽</span>
|
||||||
|
<span id="server-blocked-queries" class="ml-2 text-sm font-semibold">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-1 h-8 bg-gray-200 rounded-full mx-1"></div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="text-xs font-medium text-gray-500">正常</span>
|
||||||
|
<span id="server-allowed-queries" class="ml-2 text-sm font-semibold">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="absolute top-1 right-1">
|
<div class="absolute top-1 right-1">
|
||||||
<span id="server-status-indicator" class="inline-block w-2 h-2 bg-success rounded-full"></span>
|
<span id="server-status-indicator" class="inline-block w-2 h-2 bg-success rounded-full"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// server-status.js - 服务器状态组件功能实现
|
// 服务器状态组件 - 显示CPU使用率和查询统计
|
||||||
|
|
||||||
// 全局变量
|
// 全局变量
|
||||||
let serverStatusUpdateTimer = null;
|
let serverStatusUpdateTimer = null;
|
||||||
@@ -9,208 +9,284 @@ let previousServerData = {
|
|||||||
|
|
||||||
// 初始化服务器状态组件
|
// 初始化服务器状态组件
|
||||||
function initServerStatusWidget() {
|
function initServerStatusWidget() {
|
||||||
console.log('初始化服务器状态组件');
|
// 确保DOM元素存在
|
||||||
|
const widget = document.getElementById('server-status-widget');
|
||||||
|
if (!widget) return;
|
||||||
|
|
||||||
// 确保组件存在
|
// 初始化页面类型检测
|
||||||
if (!document.getElementById('server-status-widget')) {
|
updateWidgetDisplayByPageType();
|
||||||
console.warn('服务器状态组件不存在');
|
|
||||||
return;
|
// 设置页面切换事件监听
|
||||||
|
handlePageSwitchEvents();
|
||||||
|
|
||||||
|
// 设置WebSocket监听(如果可用)
|
||||||
|
setupWebSocketListeners();
|
||||||
|
|
||||||
|
// 立即加载一次数据
|
||||||
|
loadServerStatusData();
|
||||||
|
|
||||||
|
// 设置定时更新(每5秒更新一次)
|
||||||
|
serverStatusUpdateTimer = setInterval(loadServerStatusData, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前页面是否为仪表盘
|
||||||
|
function isCurrentPageDashboard() {
|
||||||
|
// 方法1:检查侧边栏激活状态
|
||||||
|
const dashboardLink = document.querySelector('.sidebar a[href="#dashboard"]');
|
||||||
|
if (dashboardLink && dashboardLink.classList.contains('active')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法2:检查仪表盘特有元素
|
||||||
|
const dashboardElements = [
|
||||||
|
'#dashboard-container',
|
||||||
|
'.dashboard-summary',
|
||||||
|
'#dashboard-stats'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const selector of dashboardElements) {
|
||||||
|
if (document.querySelector(selector)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 监听页面切换事件
|
// 方法3:检查URL哈希值
|
||||||
handlePageSwitchEvents();
|
if (window.location.hash === '#dashboard' || window.location.hash === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始加载数据
|
return false;
|
||||||
loadServerStatusData();
|
}
|
||||||
|
|
||||||
// 监听WebSocket连接事件
|
// 根据页面类型更新组件显示
|
||||||
monitorWebSocketConnection();
|
function updateWidgetDisplayByPageType() {
|
||||||
|
const additionalStats = document.getElementById('server-additional-stats');
|
||||||
|
if (!additionalStats) return;
|
||||||
|
|
||||||
|
// 如果当前页面是仪表盘,隐藏额外统计指标
|
||||||
|
if (isCurrentPageDashboard()) {
|
||||||
|
additionalStats.classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
// 非仪表盘页面,显示额外统计指标
|
||||||
|
additionalStats.classList.remove('hidden');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理页面切换事件
|
// 处理页面切换事件
|
||||||
function handlePageSwitchEvents() {
|
function handlePageSwitchEvents() {
|
||||||
// 监听导航点击事件
|
// 监听哈希变化(导航切换)
|
||||||
document.querySelectorAll('.sidebar-link').forEach(link => {
|
window.addEventListener('hashchange', updateWidgetDisplayByPageType);
|
||||||
link.addEventListener('click', () => {
|
|
||||||
// 页面切换后重新初始化组件
|
// 监听侧边栏点击事件
|
||||||
setTimeout(() => {
|
const sidebarLinks = document.querySelectorAll('.sidebar a');
|
||||||
if (document.getElementById('server-status-widget')) {
|
sidebarLinks.forEach(link => {
|
||||||
loadServerStatusData();
|
link.addEventListener('click', function() {
|
||||||
}
|
// 延迟检查,确保页面已切换
|
||||||
}, 300);
|
setTimeout(updateWidgetDisplayByPageType, 100);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听导航菜单点击事件
|
||||||
|
const navLinks = document.querySelectorAll('nav a');
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', function() {
|
||||||
|
setTimeout(updateWidgetDisplayByPageType, 100);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听WebSocket连接
|
// 监控WebSocket连接状态
|
||||||
function monitorWebSocketConnection() {
|
function monitorWebSocketConnection() {
|
||||||
// 检查全局WebSocket连接
|
// 如果存在WebSocket连接,监听消息
|
||||||
if (window.wsConnection && window.wsConnection.readyState === WebSocket.OPEN) {
|
if (window.socket) {
|
||||||
setupWebSocketListeners();
|
window.socket.addEventListener('message', function(event) {
|
||||||
} else {
|
try {
|
||||||
// 如果WebSocket未连接,尝试监听其连接状态
|
const data = JSON.parse(event.data);
|
||||||
const originalOnOpen = window.wsConnection?.onopen;
|
if (data.type === 'status_update') {
|
||||||
if (window.wsConnection) {
|
updateServerStatusWidget(data.payload);
|
||||||
window.wsConnection.onopen = function(event) {
|
|
||||||
if (originalOnOpen) originalOnOpen(event);
|
|
||||||
setupWebSocketListeners();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {
|
||||||
|
console.error('解析WebSocket消息失败:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置WebSocket监听器
|
// 设置WebSocket监听器
|
||||||
function setupWebSocketListeners() {
|
function setupWebSocketListeners() {
|
||||||
// 保存原始的onmessage函数
|
// 如果WebSocket已经存在
|
||||||
const originalOnMessage = window.wsConnection.onmessage;
|
if (window.socket) {
|
||||||
|
monitorWebSocketConnection();
|
||||||
// 重写onmessage函数
|
} else {
|
||||||
window.wsConnection.onmessage = function(event) {
|
// 监听socket初始化事件
|
||||||
// 调用原始函数
|
window.addEventListener('socketInitialized', function() {
|
||||||
if (originalOnMessage) originalOnMessage(event);
|
monitorWebSocketConnection();
|
||||||
|
});
|
||||||
// 处理服务器状态数据
|
}
|
||||||
try {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.type === 'initial_data' || data.type === 'stats_update') {
|
|
||||||
updateServerStatusWidget(data.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('处理服务器状态WebSocket消息失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载服务器状态数据
|
// 加载服务器状态数据
|
||||||
async function loadServerStatusData() {
|
async function loadServerStatusData() {
|
||||||
try {
|
try {
|
||||||
// 使用现有的API获取系统状态
|
// 使用现有的API获取系统状态
|
||||||
const statusData = await window.api?.getStatus();
|
const api = window.api || {};
|
||||||
if (statusData && !statusData.error) {
|
const getStatusFn = api.getStatus || function() { return Promise.resolve({}); };
|
||||||
updateServerStatusWidget(statusData);
|
const statusData = await getStatusFn();
|
||||||
}
|
if (statusData && !statusData.error) {
|
||||||
} catch (error) {
|
updateServerStatusWidget(statusData);
|
||||||
console.error('加载服务器状态数据失败:', error);
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载服务器状态数据失败:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新服务器状态组件
|
// 更新服务器状态组件
|
||||||
function updateServerStatusWidget(stats) {
|
function updateServerStatusWidget(stats) {
|
||||||
// 确保组件存在
|
// 确保组件存在
|
||||||
const widget = document.getElementById('server-status-widget');
|
const widget = document.getElementById('server-status-widget');
|
||||||
if (!widget) return;
|
if (!widget) return;
|
||||||
|
|
||||||
// 提取CPU使用率
|
// 确保stats存在
|
||||||
let cpuUsage = 0;
|
stats = stats || {};
|
||||||
if (stats.system && stats.system.cpu) {
|
|
||||||
cpuUsage = stats.system.cpu;
|
// 提取CPU使用率
|
||||||
} else if (stats.cpuUsage) {
|
let cpuUsage = 0;
|
||||||
cpuUsage = stats.cpuUsage;
|
if (stats.system && typeof stats.system.cpu === 'number') {
|
||||||
|
cpuUsage = stats.system.cpu;
|
||||||
|
} else if (typeof stats.cpuUsage === 'number') {
|
||||||
|
cpuUsage = stats.cpuUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取查询统计数据
|
||||||
|
let totalQueries = 0;
|
||||||
|
let blockedQueries = 0;
|
||||||
|
let allowedQueries = 0;
|
||||||
|
|
||||||
|
if (stats.dns) {
|
||||||
|
const allowed = typeof stats.dns.Allowed === 'number' ? stats.dns.Allowed : 0;
|
||||||
|
const blocked = typeof stats.dns.Blocked === 'number' ? stats.dns.Blocked : 0;
|
||||||
|
const errors = typeof stats.dns.Errors === 'number' ? stats.dns.Errors : 0;
|
||||||
|
totalQueries = allowed + blocked + errors;
|
||||||
|
blockedQueries = blocked;
|
||||||
|
allowedQueries = allowed;
|
||||||
|
} else {
|
||||||
|
totalQueries = typeof stats.totalQueries === 'number' ? stats.totalQueries : 0;
|
||||||
|
blockedQueries = typeof stats.blockedQueries === 'number' ? stats.blockedQueries : 0;
|
||||||
|
allowedQueries = typeof stats.allowedQueries === 'number' ? stats.allowedQueries : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新CPU使用率
|
||||||
|
const cpuValueElement = document.getElementById('server-cpu-value');
|
||||||
|
if (cpuValueElement) {
|
||||||
|
cpuValueElement.textContent = cpuUsage.toFixed(1) + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpuBarElement = document.getElementById('server-cpu-bar');
|
||||||
|
if (cpuBarElement) {
|
||||||
|
cpuBarElement.style.width = Math.min(cpuUsage, 100) + '%';
|
||||||
|
|
||||||
|
// 根据CPU使用率改变颜色
|
||||||
|
if (cpuUsage > 80) {
|
||||||
|
cpuBarElement.className = 'h-full bg-danger rounded-full';
|
||||||
|
} else if (cpuUsage > 50) {
|
||||||
|
cpuBarElement.className = 'h-full bg-warning rounded-full';
|
||||||
|
} else {
|
||||||
|
cpuBarElement.className = 'h-full bg-success rounded-full';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 提取查询量
|
// 更新查询量
|
||||||
let queryCount = 0;
|
const queriesValueElement = document.getElementById('server-queries-value');
|
||||||
if (stats.dns) {
|
if (queriesValueElement) {
|
||||||
queryCount = (stats.dns.Allowed || 0) + (stats.dns.Blocked || 0) + (stats.dns.Errors || 0);
|
queriesValueElement.textContent = formatNumber(totalQueries);
|
||||||
} else if (stats.totalQueries) {
|
}
|
||||||
queryCount = stats.totalQueries;
|
|
||||||
|
// 计算查询量百分比(假设最大查询量为10000)
|
||||||
|
const queryPercentage = Math.min((totalQueries / 10000) * 100, 100);
|
||||||
|
const queriesBarElement = document.getElementById('server-queries-bar');
|
||||||
|
if (queriesBarElement) {
|
||||||
|
queriesBarElement.style.width = queryPercentage + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新额外统计指标
|
||||||
|
const totalQueriesElement = document.getElementById('server-total-queries');
|
||||||
|
if (totalQueriesElement) {
|
||||||
|
totalQueriesElement.textContent = formatNumber(totalQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockedQueriesElement = document.getElementById('server-blocked-queries');
|
||||||
|
if (blockedQueriesElement) {
|
||||||
|
blockedQueriesElement.textContent = formatNumber(blockedQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowedQueriesElement = document.getElementById('server-allowed-queries');
|
||||||
|
if (allowedQueriesElement) {
|
||||||
|
allowedQueriesElement.textContent = formatNumber(allowedQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加光晕提示效果
|
||||||
|
if (previousServerData.cpu !== cpuUsage || previousServerData.queries !== totalQueries) {
|
||||||
|
addGlowEffect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新服务器状态指示器
|
||||||
|
const statusIndicator = document.getElementById('server-status-indicator');
|
||||||
|
if (statusIndicator) {
|
||||||
|
// 检查系统状态
|
||||||
|
if (stats.system && stats.system.status === 'error') {
|
||||||
|
statusIndicator.className = 'inline-block w-2 h-2 bg-danger rounded-full';
|
||||||
|
} else {
|
||||||
|
statusIndicator.className = 'inline-block w-2 h-2 bg-success rounded-full';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新CPU使用率
|
// 保存当前数据用于下次比较
|
||||||
if (document.getElementById('server-cpu-value')) {
|
previousServerData = {
|
||||||
document.getElementById('server-cpu-value').textContent = cpuUsage.toFixed(1) + '%';
|
cpu: cpuUsage,
|
||||||
}
|
queries: totalQueries
|
||||||
if (document.getElementById('server-cpu-bar')) {
|
};
|
||||||
document.getElementById('server-cpu-bar').style.width = Math.min(cpuUsage, 100) + '%';
|
|
||||||
|
|
||||||
// 根据CPU使用率改变颜色
|
|
||||||
const cpuBar = document.getElementById('server-cpu-bar');
|
|
||||||
if (cpuUsage > 80) {
|
|
||||||
cpuBar.className = 'h-full bg-danger rounded-full';
|
|
||||||
} else if (cpuUsage > 50) {
|
|
||||||
cpuBar.className = 'h-full bg-warning rounded-full';
|
|
||||||
} else {
|
|
||||||
cpuBar.className = 'h-full bg-success rounded-full';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新查询量
|
|
||||||
if (document.getElementById('server-queries-value')) {
|
|
||||||
document.getElementById('server-queries-value').textContent = formatNumber(queryCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算查询量百分比(假设最大查询量为10000)
|
|
||||||
const queryPercentage = Math.min((queryCount / 10000) * 100, 100);
|
|
||||||
if (document.getElementById('server-queries-bar')) {
|
|
||||||
document.getElementById('server-queries-bar').style.width = queryPercentage + '%';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加光晕提示效果
|
|
||||||
if (previousServerData.cpu !== cpuUsage || previousServerData.queries !== queryCount) {
|
|
||||||
addGlowEffect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新服务器状态指示器
|
|
||||||
if (document.getElementById('server-status-indicator')) {
|
|
||||||
const indicator = document.getElementById('server-status-indicator');
|
|
||||||
|
|
||||||
// 检查系统状态
|
|
||||||
if (stats.system && stats.system.status === 'error') {
|
|
||||||
indicator.className = 'inline-block w-2 h-2 bg-danger rounded-full';
|
|
||||||
} else {
|
|
||||||
indicator.className = 'inline-block w-2 h-2 bg-success rounded-full';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存当前数据用于下次比较
|
|
||||||
previousServerData = {
|
|
||||||
cpu: cpuUsage,
|
|
||||||
queries: queryCount
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加光晕提示效果
|
// 添加光晕提示效果
|
||||||
function addGlowEffect() {
|
function addGlowEffect() {
|
||||||
const widget = document.getElementById('server-status-widget');
|
const widget = document.getElementById('server-status-widget');
|
||||||
if (!widget) return;
|
if (!widget) return;
|
||||||
|
|
||||||
// 添加光晕类
|
// 添加光晕类
|
||||||
widget.classList.add('glow-effect');
|
widget.classList.add('glow-effect');
|
||||||
|
|
||||||
// 2秒后移除光晕
|
// 2秒后移除光晕
|
||||||
setTimeout(() => {
|
setTimeout(function() {
|
||||||
widget.classList.remove('glow-effect');
|
widget.classList.remove('glow-effect');
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化数字(添加千位分隔符)
|
// 格式化数字
|
||||||
function formatNumber(num) {
|
function formatNumber(num) {
|
||||||
if (num >= 1000000) {
|
if (num >= 1000000) {
|
||||||
return (num / 1000000).toFixed(1) + 'M';
|
return (num / 1000000).toFixed(1) + 'M';
|
||||||
} else if (num >= 1000) {
|
} else if (num >= 1000) {
|
||||||
return (num / 1000).toFixed(1) + 'K';
|
return (num / 1000).toFixed(1) + 'K';
|
||||||
}
|
}
|
||||||
return num.toString();
|
return num.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在DOM加载完成后初始化
|
// 在DOM加载完成后初始化
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', function() {
|
||||||
// 延迟初始化,确保页面完全加载
|
// 延迟初始化,确保页面完全加载
|
||||||
setTimeout(initServerStatusWidget, 500);
|
setTimeout(initServerStatusWidget, 500);
|
||||||
|
|
||||||
// 监听页面切换事件(已在handlePageSwitchEvents中处理)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 在页面卸载时清理资源
|
// 在页面卸载时清理资源
|
||||||
window.addEventListener('beforeunload', () => {
|
window.addEventListener('beforeunload', function() {
|
||||||
if (serverStatusUpdateTimer) {
|
if (serverStatusUpdateTimer) {
|
||||||
clearInterval(serverStatusUpdateTimer);
|
clearInterval(serverStatusUpdateTimer);
|
||||||
serverStatusUpdateTimer = null;
|
serverStatusUpdateTimer = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 导出函数供其他模块使用
|
// 导出函数供其他模块使用
|
||||||
window.serverStatusWidget = {
|
window.serverStatusWidget = {
|
||||||
init: initServerStatusWidget,
|
init: initServerStatusWidget,
|
||||||
update: updateServerStatusWidget
|
update: updateServerStatusWidget
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user