web异常待修复
This commit is contained in:
@@ -6,8 +6,36 @@
|
||||
<meta name="description" content="DNS服务器管理控制台 - 高性能DNS服务器,支持规则屏蔽和Hosts管理">
|
||||
<title>DNS服务器管理控制台</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.css">
|
||||
<!-- Chart.js 4.x不需要单独的CSS文件 -->
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<style>
|
||||
/* 光晕效果样式 */
|
||||
.stat-value.update {
|
||||
animation: glow 1s ease-out;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0% { box-shadow: 0 0 5px rgba(75, 192, 192, 0.5); }
|
||||
50% { box-shadow: 0 0 20px rgba(75, 192, 192, 0.8); }
|
||||
100% { box-shadow: 0 0 5px rgba(75, 192, 192, 0); }
|
||||
}
|
||||
|
||||
/* 小型图表容器 */
|
||||
.mini-chart-container {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
width: 60px;
|
||||
height: 20px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 小型图表 */
|
||||
.mini-chart {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -63,42 +91,54 @@
|
||||
</div>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" style="position: relative;">
|
||||
<i class="fas fa-ban"></i>
|
||||
<div class="stat-value" id="blocked-count">--</div>
|
||||
<div class="stat-label">屏蔽请求</div>
|
||||
<div class="mini-chart-container">
|
||||
<canvas id="blocked-chart" class="mini-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" style="position: relative;">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<div class="stat-value" id="allowed-count">--</div>
|
||||
<div class="stat-label">允许请求</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" style="position: relative;">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
<div class="stat-value" id="error-count">--</div>
|
||||
<div class="stat-label">错误请求</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" style="position: relative;">
|
||||
<i class="fas fa-history"></i>
|
||||
<div class="stat-value" id="total-queries">--</div>
|
||||
<div class="stat-label">总请求数</div>
|
||||
<div class="mini-chart-container">
|
||||
<canvas id="query-chart" class="mini-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" style="position: relative;">
|
||||
<i class="fas fa-list"></i>
|
||||
<div class="stat-value" id="rules-count">--</div>
|
||||
<div class="stat-label">屏蔽规则数</div>
|
||||
<div class="mini-chart-container">
|
||||
<canvas id="rules-chart" class="mini-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" style="position: relative;">
|
||||
<i class="fas fa-globe"></i>
|
||||
<div class="stat-value" id="hosts-count">--</div>
|
||||
<div class="stat-label">Hosts条目</div>
|
||||
<div class="mini-chart-container">
|
||||
<canvas id="hosts-chart" class="mini-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-container">
|
||||
<div class="chart-card">
|
||||
<h3>24小时屏蔽统计</h3>
|
||||
<canvas id="hourly-chart"></canvas>
|
||||
<canvas id="hourly-chart" style="height: 300px;"></canvas>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<h3>请求类型分布</h3>
|
||||
@@ -411,7 +451,158 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
||||
<script src="js/vendor/chart.umd.min.js"></script>
|
||||
<script>
|
||||
// 全局数据变化检测和图表管理
|
||||
let previousStats = {};
|
||||
let miniCharts = {};
|
||||
let dataHistory = {
|
||||
rules: Array(10).fill(0),
|
||||
hosts: Array(10).fill(0),
|
||||
query: Array(10).fill(0),
|
||||
blocked: Array(10).fill(0)
|
||||
};
|
||||
let updateTimer = null;
|
||||
const UPDATE_INTERVAL = 2000;
|
||||
let previousFullData = null;
|
||||
let previousChartData = null;
|
||||
|
||||
// 初始化小型图表
|
||||
function initMiniCharts() {
|
||||
const chartConfigs = {
|
||||
'rules-chart': { label: '规则数', color: 'rgb(75, 192, 192)' },
|
||||
'hosts-chart': { label: 'Hosts数', color: 'rgb(153, 102, 255)' },
|
||||
'query-chart': { label: '查询数', color: 'rgb(255, 159, 64)' },
|
||||
'blocked-chart': { label: '屏蔽数', color: 'rgb(255, 99, 132)' }
|
||||
};
|
||||
|
||||
Object.entries(chartConfigs).forEach(([id, config]) => {
|
||||
const ctx = document.getElementById(id);
|
||||
if (!ctx) return;
|
||||
|
||||
miniCharts[id] = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array(10).fill(''),
|
||||
datasets: [{
|
||||
label: config.label,
|
||||
data: Array(10).fill(0),
|
||||
borderColor: config.color,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2,
|
||||
fill: true
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: { enabled: false }
|
||||
},
|
||||
scales: {
|
||||
x: { display: false },
|
||||
y: { display: false, beginAtZero: true }
|
||||
},
|
||||
animation: false
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 更新小型图表
|
||||
function updateMiniChart(chartId, data) {
|
||||
if (miniCharts[chartId]) {
|
||||
miniCharts[chartId].data.datasets[0].data = data;
|
||||
miniCharts[chartId].update();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新数据历史记录
|
||||
function updateDataHistory(key, value) {
|
||||
dataHistory[key].shift();
|
||||
dataHistory[key].push(value);
|
||||
}
|
||||
|
||||
// 检查数据是否变化并添加光晕效果
|
||||
function checkAndAnimate(elementId, newValue) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (!element) return;
|
||||
|
||||
const oldValue = previousStats[elementId] || 0;
|
||||
|
||||
if (newValue !== oldValue && oldValue !== 0 && oldValue !== '--') {
|
||||
element.classList.add('update');
|
||||
setTimeout(() => {
|
||||
element.classList.remove('update');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
previousStats[elementId] = newValue;
|
||||
}
|
||||
|
||||
// 启动实时更新
|
||||
function startRealTimeUpdate() {
|
||||
if (updateTimer) {
|
||||
clearInterval(updateTimer);
|
||||
}
|
||||
updateTimer = setInterval(() => {
|
||||
// 仅当当前面板是仪表盘时更新数据
|
||||
if (document.getElementById('dashboard').classList.contains('active')) {
|
||||
loadDashboardData();
|
||||
}
|
||||
}, UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
// 停止实时更新
|
||||
function stopRealTimeUpdate() {
|
||||
if (updateTimer) {
|
||||
clearInterval(updateTimer);
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示悬浮通知
|
||||
function showNotification(type, message) {
|
||||
// 创建通知元素
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `notification notification-${type}`;
|
||||
|
||||
// 设置图标
|
||||
let iconClass = 'info-circle';
|
||||
if (type === 'success') iconClass = 'check-circle';
|
||||
else if (type === 'danger') iconClass = 'exclamation-circle';
|
||||
else if (type === 'warning') iconClass = 'exclamation-triangle';
|
||||
|
||||
// 设置通知内容
|
||||
notification.innerHTML = `
|
||||
<div class="notification-icon">
|
||||
<i class="fas fa-${iconClass}"></i>
|
||||
</div>
|
||||
<div class="notification-content">${message}</div>
|
||||
<button class="notification-close">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
`;
|
||||
|
||||
// 添加关闭事件
|
||||
notification.querySelector('.notification-close').addEventListener('click', () => {
|
||||
notification.style.animation = 'slideIn 0.3s ease-out reverse';
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
});
|
||||
|
||||
// 添加到页面
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 3秒后自动关闭
|
||||
setTimeout(() => {
|
||||
notification.style.animation = 'slideIn 0.3s ease-out reverse';
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/modules/dashboard.js"></script>
|
||||
<script src="js/modules/rules.js"></script>
|
||||
@@ -419,5 +610,47 @@
|
||||
<script src="js/modules/blacklists.js"></script>
|
||||
<script src="js/modules/query.js"></script>
|
||||
<script src="js/modules/config.js"></script>
|
||||
<script>
|
||||
// 页面加载完成后初始化
|
||||
window.addEventListener('load', function() {
|
||||
initMiniCharts();
|
||||
|
||||
// 为侧边栏导航添加切换事件
|
||||
const navItems = document.querySelectorAll('.nav-item');
|
||||
navItems.forEach(item => {
|
||||
item.addEventListener('click', function() {
|
||||
const target = this.getAttribute('data-target');
|
||||
const panels = document.querySelectorAll('.panel');
|
||||
const navItems = document.querySelectorAll('.nav-item');
|
||||
|
||||
// 更新面板显示
|
||||
panels.forEach(panel => {
|
||||
panel.classList.remove('active');
|
||||
if (panel.id === target) {
|
||||
panel.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
// 更新导航高亮
|
||||
navItems.forEach(navItem => {
|
||||
navItem.classList.remove('active');
|
||||
});
|
||||
this.classList.add('active');
|
||||
|
||||
// 根据面板类型控制更新
|
||||
if (target === 'dashboard') {
|
||||
startRealTimeUpdate();
|
||||
} else {
|
||||
stopRealTimeUpdate();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 页面卸载时清理定时器
|
||||
window.addEventListener('beforeunload', function() {
|
||||
stopRealTimeUpdate();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user