Files
dns-server/static/js/app.js
Alex Yang 85320611cb web重做
2025-11-24 01:53:26 +08:00

252 lines
7.6 KiB
JavaScript

// 全局配置
const API_BASE_URL = '/api';
// DOM 加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 初始化面板切换
initPanelNavigation();
// 初始化通知组件
initNotification();
// 加载初始数据
loadInitialData();
// 定时更新数据
setInterval(loadInitialData, 60000); // 每分钟更新一次
});
// 初始化面板导航
function initPanelNavigation() {
const navItems = document.querySelectorAll('.nav-item');
const panels = document.querySelectorAll('.panel');
navItems.forEach(item => {
item.addEventListener('click', function() {
// 移除所有活动类
navItems.forEach(nav => nav.classList.remove('active'));
panels.forEach(panel => panel.classList.remove('active'));
// 添加当前活动类
this.classList.add('active');
const target = this.getAttribute('data-target');
document.getElementById(target).classList.add('active');
// 面板激活时执行相应的初始化函数
if (window[`init${target.charAt(0).toUpperCase() + target.slice(1)}Panel`]) {
window[`init${target.charAt(0).toUpperCase() + target.slice(1)}Panel`]();
}
});
});
}
// 初始化通知组件
function initNotification() {
window.showNotification = function(message, type = 'info') {
const notification = document.getElementById('notification');
const notificationMessage = document.getElementById('notification-message');
// 设置消息和类型
notificationMessage.textContent = message;
notification.className = 'notification show ' + type;
// 自动关闭
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
};
}
// 加载初始数据
function loadInitialData() {
// 加载服务器状态
fetch(`${API_BASE_URL}/status`)
.then(response => response.json())
.then(data => {
// 更新服务器状态指示器
const statusDot = document.querySelector('.status-dot');
const serverStatus = document.getElementById('server-status');
if (data && data.status === 'running') {
statusDot.classList.add('connected');
serverStatus.textContent = '运行中';
} else {
statusDot.classList.remove('connected');
serverStatus.textContent = '离线';
}
})
.catch(error => {
console.error('获取服务器状态失败:', error);
// 更新状态为离线
const statusDot = document.querySelector('.status-dot');
const serverStatus = document.getElementById('server-status');
statusDot.classList.remove('connected');
serverStatus.textContent = '离线';
});
// 加载统计数据
fetch(`${API_BASE_URL}/stats`)
.then(response => response.json())
.then(data => {
// 更新统计数据
if (data && data.dns) {
updateStatCards(data.dns);
}
})
.catch(error => {
console.error('获取统计数据失败:', error);
window.showNotification('获取统计数据失败', 'error');
});
}
// 更新统计卡片数据
function updateStatCards(stats) {
const statElements = {
'blocked-count': stats.blocked || 0,
'allowed-count': stats.allowed || 0,
'error-count': stats.error || 0,
'total-queries': stats.totalQueries || 0,
'rules-count': stats.rulesCount || 0,
'hosts-count': stats.hostsCount || 0
};
for (const [id, value] of Object.entries(statElements)) {
const element = document.getElementById(id);
if (element) {
element.textContent = formatNumber(value);
}
}
}
// 通用API请求函数
function apiRequest(endpoint, method = 'GET', data = null) {
const headers = {
'Content-Type': 'application/json'
};
const config = {
method,
headers
};
if (data && (method === 'POST' || method === 'PUT' || method === 'DELETE')) {
config.body = JSON.stringify(data);
}
return fetch(`${API_BASE_URL}${endpoint}`, config)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
});
}
// 数字格式化函数
function formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
}
// 确认对话框函数
function confirmAction(message, onConfirm) {
if (confirm(message)) {
onConfirm();
}
}
// 加载状态函数
function showLoading(element) {
if (element) {
element.innerHTML = '<td colspan="100%" class="loading">加载中...</td>';
}
}
// 错误状态函数
function showError(element, message) {
if (element) {
element.innerHTML = `<td colspan="100%" style="color: #e74c3c;">${message}</td>`;
}
}
// 空状态函数
function showEmpty(element, message) {
if (element) {
element.innerHTML = `<td colspan="100%" style="color: #7f8c8d; font-style: italic;">${message}</td>`;
}
}
// 表格排序功能
function initTableSort(tableId) {
const table = document.getElementById(tableId);
if (!table) return;
const headers = table.querySelectorAll('thead th');
headers.forEach(header => {
header.addEventListener('click', function() {
const columnIndex = Array.from(headers).indexOf(this);
const isAscending = this.getAttribute('data-sort') !== 'asc';
// 重置所有标题
headers.forEach(h => h.setAttribute('data-sort', ''));
this.setAttribute('data-sort', isAscending ? 'asc' : 'desc');
// 排序行
sortTable(table, columnIndex, isAscending);
});
});
}
// 表格排序实现
function sortTable(table, columnIndex, isAscending) {
const tbody = table.querySelector('tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
// 排序行
rows.sort((a, b) => {
const aValue = a.cells[columnIndex].textContent.trim();
const bValue = b.cells[columnIndex].textContent.trim();
// 尝试数字排序
const aNum = parseFloat(aValue);
const bNum = parseFloat(bValue);
if (!isNaN(aNum) && !isNaN(bNum)) {
return isAscending ? aNum - bNum : bNum - aNum;
}
// 字符串排序
return isAscending
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
});
// 重新添加行
rows.forEach(row => tbody.appendChild(row));
}
// 搜索过滤功能
function initSearchFilter(inputId, tableId, columnIndex) {
const input = document.getElementById(inputId);
const table = document.getElementById(tableId);
if (!input || !table) return;
input.addEventListener('input', function() {
const filter = this.value.toLowerCase();
const rows = table.querySelectorAll('tbody tr');
rows.forEach(row => {
const cell = row.cells[columnIndex];
if (cell) {
const text = cell.textContent.toLowerCase();
row.style.display = text.includes(filter) ? '' : 'none';
}
});
});
}