web增加恢复解析统计图表
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
// dashboard.js - 仪表盘功能实现
|
||||
|
||||
// 全局变量
|
||||
let queryTrendChart = null;
|
||||
let ratioChart = null;
|
||||
let dnsRequestsChart = null;
|
||||
let intervalId = null;
|
||||
|
||||
// 初始化仪表盘
|
||||
@@ -14,6 +14,9 @@ async function initDashboard() {
|
||||
// 初始化图表
|
||||
initCharts();
|
||||
|
||||
// 初始化时间范围切换
|
||||
initTimeRangeToggle();
|
||||
|
||||
// 设置定时更新
|
||||
intervalId = setInterval(loadDashboardData, 5000); // 每5秒更新一次
|
||||
} catch (error) {
|
||||
@@ -24,39 +27,95 @@ async function initDashboard() {
|
||||
|
||||
// 加载仪表盘数据
|
||||
async function loadDashboardData() {
|
||||
console.log('开始加载仪表盘数据');
|
||||
try {
|
||||
console.log('开始加载仪表盘数据...');
|
||||
|
||||
// 先分别获取数据以调试
|
||||
// 获取基本统计数据
|
||||
const stats = await api.getStats();
|
||||
console.log('统计数据:', stats);
|
||||
|
||||
// 获取TOP被屏蔽域名
|
||||
const topBlockedDomains = await api.getTopBlockedDomains();
|
||||
console.log('Top屏蔽域名:', topBlockedDomains);
|
||||
console.log('TOP被屏蔽域名:', topBlockedDomains);
|
||||
|
||||
// 获取最近屏蔽域名
|
||||
const recentBlockedDomains = await api.getRecentBlockedDomains();
|
||||
console.log('最近屏蔽域名:', recentBlockedDomains);
|
||||
|
||||
const hourlyStats = await api.getHourlyStats();
|
||||
console.log('小时统计数据:', hourlyStats);
|
||||
|
||||
// 原并行请求方式(保留以备后续恢复)
|
||||
// const [stats, topBlockedDomains, recentBlockedDomains, hourlyStats] = await Promise.all([
|
||||
// const [stats, topBlockedDomains, recentBlockedDomains] = await Promise.all([
|
||||
// api.getStats(),
|
||||
// api.getTopBlockedDomains(),
|
||||
// api.getRecentBlockedDomains(),
|
||||
// api.getHourlyStats()
|
||||
// api.getRecentBlockedDomains()
|
||||
// ]);
|
||||
|
||||
// 更新统计卡片
|
||||
updateStatsCards(stats);
|
||||
|
||||
// 更新数据表格
|
||||
// 尝试从stats中获取总查询数等信息
|
||||
if (stats.dns) {
|
||||
totalQueries = stats.dns.Allowed + stats.dns.Blocked + (stats.dns.Errors || 0);
|
||||
blockedQueries = stats.dns.Blocked;
|
||||
errorQueries = stats.dns.Errors || 0;
|
||||
allowedQueries = stats.dns.Allowed;
|
||||
} else {
|
||||
totalQueries = stats.totalQueries || 0;
|
||||
blockedQueries = stats.blockedQueries || 0;
|
||||
errorQueries = stats.errorQueries || 0;
|
||||
allowedQueries = stats.allowedQueries || 0;
|
||||
}
|
||||
|
||||
// 更新新卡片数据 - 添加模拟数据支持
|
||||
if (document.getElementById('avg-response-time')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const responseTime = stats.avgResponseTime !== undefined ? stats.avgResponseTime : 42;
|
||||
const responsePercent = stats.responseTimePercent !== undefined ? stats.responseTimePercent : 15;
|
||||
document.getElementById('avg-response-time').textContent = formatNumber(responseTime) + 'ms';
|
||||
document.getElementById('response-time-percent').textContent = responsePercent + '%';
|
||||
}
|
||||
|
||||
if (document.getElementById('top-query-type')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const queryType = stats.topQueryType || 'A';
|
||||
const queryPercent = stats.queryTypePercentage !== undefined ? stats.queryTypePercentage : 68;
|
||||
document.getElementById('top-query-type').textContent = queryType;
|
||||
document.getElementById('query-type-percentage').textContent = queryPercent + '%';
|
||||
}
|
||||
|
||||
if (document.getElementById('active-ips')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const activeIPs = stats.activeIPs !== undefined ? stats.activeIPs : 12;
|
||||
const ipsPercent = stats.activeIPsPercent !== undefined ? stats.activeIPsPercent : 23;
|
||||
document.getElementById('active-ips').textContent = formatNumber(activeIPs);
|
||||
document.getElementById('active-ips-percent').textContent = ipsPercent + '%';
|
||||
}
|
||||
|
||||
if (document.getElementById('cpu-usage')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const cpuUsage = stats.cpuUsage !== undefined ? stats.cpuUsage : 45;
|
||||
document.getElementById('cpu-usage').textContent = cpuUsage + '%';
|
||||
|
||||
// 设置CPU状态颜色
|
||||
const cpuStatusElem = document.getElementById('cpu-status');
|
||||
if (cpuStatusElem) {
|
||||
if (cpuUsage > 80) {
|
||||
cpuStatusElem.textContent = '警告';
|
||||
cpuStatusElem.className = 'text-danger text-sm flex items-center';
|
||||
} else if (cpuUsage > 60) {
|
||||
cpuStatusElem.textContent = '较高';
|
||||
cpuStatusElem.className = 'text-warning text-sm flex items-center';
|
||||
} else {
|
||||
cpuStatusElem.textContent = '正常';
|
||||
cpuStatusElem.className = 'text-success text-sm flex items-center';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新表格
|
||||
updateTopBlockedTable(topBlockedDomains);
|
||||
updateRecentBlockedTable(recentBlockedDomains);
|
||||
|
||||
// 更新图表
|
||||
updateCharts(stats, hourlyStats);
|
||||
updateCharts({totalQueries, blockedQueries, allowedQueries, errorQueries});
|
||||
|
||||
// 更新运行状态
|
||||
updateUptime();
|
||||
@@ -92,13 +151,6 @@ function updateStatsCards(stats) {
|
||||
blockedQueries = stats[0].blocked || 0;
|
||||
allowedQueries = stats[0].allowed || 0;
|
||||
errorQueries = stats[0].error || 0;
|
||||
} else {
|
||||
// 如果都不匹配,使用一些示例数据以便在界面上显示
|
||||
totalQueries = 12500;
|
||||
blockedQueries = 1500;
|
||||
allowedQueries = 10500;
|
||||
errorQueries = 500;
|
||||
console.log('使用示例数据填充统计卡片');
|
||||
}
|
||||
|
||||
// 更新数量显示
|
||||
@@ -107,11 +159,18 @@ function updateStatsCards(stats) {
|
||||
document.getElementById('allowed-queries').textContent = formatNumber(allowedQueries);
|
||||
document.getElementById('error-queries').textContent = formatNumber(errorQueries);
|
||||
|
||||
// 更新百分比(模拟数据,实际应该从API获取)
|
||||
document.getElementById('queries-percent').textContent = '12%';
|
||||
document.getElementById('blocked-percent').textContent = '8%';
|
||||
document.getElementById('allowed-percent').textContent = '15%';
|
||||
document.getElementById('error-percent').textContent = '2%';
|
||||
// 计算并更新百分比
|
||||
if (totalQueries > 0) {
|
||||
document.getElementById('blocked-percent').textContent = `${Math.round((blockedQueries / totalQueries) * 100)}%`;
|
||||
document.getElementById('allowed-percent').textContent = `${Math.round((allowedQueries / totalQueries) * 100)}%`;
|
||||
document.getElementById('error-percent').textContent = `${Math.round((errorQueries / totalQueries) * 100)}%`;
|
||||
document.getElementById('queries-percent').textContent = `100%`;
|
||||
} else {
|
||||
document.getElementById('queries-percent').textContent = '---';
|
||||
document.getElementById('blocked-percent').textContent = '---';
|
||||
document.getElementById('allowed-percent').textContent = '---';
|
||||
document.getElementById('error-percent').textContent = '---';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新Top屏蔽域名表格
|
||||
@@ -138,11 +197,11 @@ function updateTopBlockedTable(domains) {
|
||||
// 如果没有有效数据,提供示例数据
|
||||
if (tableData.length === 0) {
|
||||
tableData = [
|
||||
{ name: 'ads.example.com', count: 1250 },
|
||||
{ name: 'tracking.example.org', count: 980 },
|
||||
{ name: 'malware.test.net', count: 765 },
|
||||
{ name: 'spam.service.com', count: 450 },
|
||||
{ name: 'analytics.unknown.org', count: 320 }
|
||||
{ name: '---', count: '---' },
|
||||
{ name: '---', count: '---' },
|
||||
{ name: '---', count: '---' },
|
||||
{ name: '---', count: '---' },
|
||||
{ name: '---', count: '---' }
|
||||
];
|
||||
console.log('使用示例数据填充Top屏蔽域名表格');
|
||||
}
|
||||
@@ -179,11 +238,11 @@ function updateRecentBlockedTable(domains) {
|
||||
if (tableData.length === 0) {
|
||||
const now = Date.now();
|
||||
tableData = [
|
||||
{ name: 'ads.example.com', timestamp: now - 5 * 60 * 1000 },
|
||||
{ name: 'tracking.example.org', timestamp: now - 15 * 60 * 1000 },
|
||||
{ name: 'malware.test.net', timestamp: now - 30 * 60 * 1000 },
|
||||
{ name: 'spam.service.com', timestamp: now - 45 * 60 * 1000 },
|
||||
{ name: 'analytics.unknown.org', timestamp: now - 60 * 60 * 1000 }
|
||||
{ name: '---', timestamp: now - 5 * 60 * 1000 },
|
||||
{ name: '---', timestamp: now - 15 * 60 * 1000 },
|
||||
{ name: '---', timestamp: now - 30 * 60 * 1000 },
|
||||
{ name: '---', timestamp: now - 45 * 60 * 1000 },
|
||||
{ name: '---', timestamp: now - 60 * 60 * 1000 }
|
||||
];
|
||||
console.log('使用示例数据填充最近屏蔽域名表格');
|
||||
}
|
||||
@@ -202,69 +261,43 @@ function updateRecentBlockedTable(domains) {
|
||||
tableBody.innerHTML = html;
|
||||
}
|
||||
|
||||
// 当前选中的时间范围
|
||||
let currentTimeRange = '24h'; // 默认为24小时
|
||||
|
||||
// 初始化时间范围切换
|
||||
function initTimeRangeToggle() {
|
||||
const timeRangeButtons = document.querySelectorAll('.time-range-btn');
|
||||
timeRangeButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
// 移除所有按钮的激活状态
|
||||
timeRangeButtons.forEach(btn => btn.classList.remove('active'));
|
||||
// 添加当前按钮的激活状态
|
||||
button.classList.add('active');
|
||||
// 更新当前时间范围
|
||||
currentTimeRange = button.dataset.range;
|
||||
// 重新加载数据
|
||||
loadDashboardData();
|
||||
// 更新DNS请求图表
|
||||
drawDNSRequestsChart();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
function initCharts() {
|
||||
// 初始化查询趋势图表
|
||||
const queryTrendCtx = document.getElementById('query-trend-chart').getContext('2d');
|
||||
queryTrendChart = new Chart(queryTrendCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array.from({length: 24}, (_, i) => `${(i + 1) % 24}:00`),
|
||||
datasets: [
|
||||
{
|
||||
label: '总查询',
|
||||
data: Array(24).fill(0),
|
||||
borderColor: '#165DFF',
|
||||
backgroundColor: 'rgba(22, 93, 255, 0.1)',
|
||||
tension: 0.4,
|
||||
fill: true
|
||||
},
|
||||
{
|
||||
label: '屏蔽数量',
|
||||
data: Array(24).fill(0),
|
||||
borderColor: '#F53F3F',
|
||||
backgroundColor: 'rgba(245, 63, 63, 0.1)',
|
||||
tension: 0.4,
|
||||
fill: true
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
drawBorder: false
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化比例图表
|
||||
const ratioCtx = document.getElementById('ratio-chart').getContext('2d');
|
||||
const ratioChartElement = document.getElementById('ratio-chart');
|
||||
if (!ratioChartElement) {
|
||||
console.error('未找到比例图表元素');
|
||||
return;
|
||||
}
|
||||
const ratioCtx = ratioChartElement.getContext('2d');
|
||||
ratioChart = new Chart(ratioCtx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['正常解析', '被屏蔽', '错误'],
|
||||
datasets: [{
|
||||
data: [70, 25, 5],
|
||||
data: ['---', '---', '---'],
|
||||
backgroundColor: ['#00B42A', '#F53F3F', '#FF7D00'],
|
||||
borderWidth: 0
|
||||
}]
|
||||
@@ -280,15 +313,101 @@ function initCharts() {
|
||||
cutout: '70%'
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化DNS请求统计图表
|
||||
drawDNSRequestsChart();
|
||||
}
|
||||
|
||||
// 绘制DNS请求统计图表
|
||||
function drawDNSRequestsChart() {
|
||||
const ctx = document.getElementById('dns-requests-chart');
|
||||
if (!ctx) {
|
||||
console.error('未找到DNS请求图表元素');
|
||||
return;
|
||||
}
|
||||
|
||||
const chartContext = ctx.getContext('2d');
|
||||
let apiFunction;
|
||||
|
||||
// 根据当前时间范围选择API函数
|
||||
switch (currentTimeRange) {
|
||||
case '7d':
|
||||
apiFunction = api.getDailyStats;
|
||||
break;
|
||||
case '30d':
|
||||
apiFunction = api.getMonthlyStats;
|
||||
break;
|
||||
default: // 24h
|
||||
apiFunction = api.getHourlyStats;
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
apiFunction().then(data => {
|
||||
// 创建或更新图表
|
||||
if (dnsRequestsChart) {
|
||||
dnsRequestsChart.data.labels = data.labels;
|
||||
dnsRequestsChart.data.datasets[0].data = data.data;
|
||||
dnsRequestsChart.update();
|
||||
} else {
|
||||
dnsRequestsChart = new Chart(chartContext, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.labels,
|
||||
datasets: [{
|
||||
label: 'DNS请求数量',
|
||||
data: data.data,
|
||||
borderColor: '#3b82f6',
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||
tension: 0.4,
|
||||
fill: true
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('绘制DNS请求图表失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
function updateCharts(stats, hourlyStats) {
|
||||
console.log('更新图表,收到统计数据:', stats, '小时统计:', hourlyStats);
|
||||
function updateCharts(stats) {
|
||||
console.log('更新图表,收到统计数据:', stats);
|
||||
|
||||
// 空值检查
|
||||
if (!stats) {
|
||||
console.error('更新图表失败: 未提供统计数据');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新比例图表
|
||||
if (ratioChart) {
|
||||
let allowed = 70, blocked = 25, error = 5;
|
||||
let allowed = '---', blocked = '---', error = '---';
|
||||
|
||||
// 尝试从stats数据中提取
|
||||
if (stats.dns) {
|
||||
@@ -304,38 +423,6 @@ function updateCharts(stats, hourlyStats) {
|
||||
ratioChart.data.datasets[0].data = [allowed, blocked, error];
|
||||
ratioChart.update();
|
||||
}
|
||||
|
||||
// 更新趋势图表
|
||||
if (queryTrendChart) {
|
||||
let labels = Array.from({length: 24}, (_, i) => `${(i + 1) % 24}:00`);
|
||||
let totalData = [], blockedData = [];
|
||||
|
||||
// 尝试从hourlyStats中提取数据
|
||||
if (Array.isArray(hourlyStats) && hourlyStats.length > 0) {
|
||||
labels = hourlyStats.map(h => `${h.hour || h.time || h[0]}:00`);
|
||||
totalData = hourlyStats.map(h => h.total || h.queries || h[1] || 0);
|
||||
blockedData = hourlyStats.map(h => h.blocked || h[2] || 0);
|
||||
} else {
|
||||
// 如果没有小时统计数据,生成示例数据
|
||||
for (let i = 0; i < 24; i++) {
|
||||
// 生成模拟的查询数据,形成一个正常的流量曲线
|
||||
const baseValue = 500;
|
||||
const timeFactor = Math.sin((i - 8) * Math.PI / 12); // 早上8点开始上升,晚上8点开始下降
|
||||
const randomFactor = 0.8 + Math.random() * 0.4; // 添加一些随机性
|
||||
const hourlyTotal = Math.round(baseValue * (0.5 + timeFactor * 0.5) * randomFactor);
|
||||
const hourlyBlocked = Math.round(hourlyTotal * (0.1 + Math.random() * 0.2)); // 10-30%被屏蔽
|
||||
|
||||
totalData.push(hourlyTotal);
|
||||
blockedData.push(hourlyBlocked);
|
||||
}
|
||||
console.log('使用示例数据填充趋势图表');
|
||||
}
|
||||
|
||||
queryTrendChart.data.labels = labels;
|
||||
queryTrendChart.data.datasets[0].data = totalData;
|
||||
queryTrendChart.data.datasets[1].data = blockedData;
|
||||
queryTrendChart.update();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新运行状态
|
||||
|
||||
Reference in New Issue
Block a user