增加暗色模式切换

This commit is contained in:
Alex Yang
2026-01-28 11:20:00 +08:00
parent a5dc5841fb
commit 2e85bbe7c7
10 changed files with 6491 additions and 209 deletions

View File

@@ -600,33 +600,79 @@ function updateStatsCards(stats) {
// 存储正在进行的动画状态,避免动画重叠
const animationInProgress = {};
// 解析格式化的数字字符串(如"1.2K")为实际数值
function parseFormattedNumber(str) {
if (!str || typeof str !== 'string') return 0;
// 移除逗号
str = str.replace(/,/g, '');
// 检查是否是数字字符串
const num = parseFloat(str);
if (!isNaN(num)) {
// 检查是否有K或M后缀
if (str.includes('K') || str.includes('k')) {
return num * 1000;
} else if (str.includes('M') || str.includes('m')) {
return num * 1000000;
}
return num;
}
return 0;
}
// 为数字元素添加翻页滚动特效
function animateValue(elementId, newValue) {
const element = document.getElementById(elementId);
if (!element) return;
const formattedNewValue = formatNumber(newValue);
const currentValue = element.textContent;
// 解析当前值和目标值
const currentText = element.textContent;
const currentValue = parseFormattedNumber(currentText);
const targetValue = parseFloat(newValue) || 0;
// 如果值没有变化,不执行任何操作
if (currentValue === formattedNewValue) {
if (currentValue === targetValue) {
return;
}
// 简化动画使用CSS opacity过渡实现平滑更新
element.style.transition = 'opacity 0.3s ease-in-out';
element.style.opacity = '0';
// 检查是否有正在进行的动画
if (animationInProgress[elementId]) {
return;
}
// 使用requestAnimationFrame确保平滑过渡
requestAnimationFrame(() => {
element.textContent = formattedNewValue;
element.style.opacity = '1';
// 标记动画正在进行
animationInProgress[elementId] = true;
// 动画配置
const duration = Math.min(800, Math.abs(targetValue - currentValue) * 2); // 根据数值变化大小调整动画持续时间
const startTime = performance.now();
const startValue = currentValue;
// 翻页滚动动画函数
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// 移除transition样式避免影响后续更新
setTimeout(() => {
element.style.transition = '';
}, 300);
});
// 线性插值计算当前值
const currentAnimatedValue = startValue + (targetValue - startValue) * progress;
// 更新显示使用Math.round确保整数显示
element.textContent = formatNumber(Math.round(currentAnimatedValue));
// 继续动画或结束
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// 确保最终值正确
element.textContent = formatNumber(targetValue);
// 标记动画完成
delete animationInProgress[elementId];
}
}
// 开始动画
requestAnimationFrame(animate);
}
// 更新百分比元素的函数
@@ -726,17 +772,10 @@ function updateStatsCards(stats) {
dnssecStatusElement.className = `text-sm flex items-center ${dnssecEnabled ? 'text-success' : 'text-danger'}`;
}
if (dnssecSuccessElement) {
dnssecSuccessElement.textContent = formatNumber(dnssecSuccess);
}
if (dnssecFailedElement) {
dnssecFailedElement.textContent = formatNumber(dnssecFailed);
}
if (dnssecQueriesElement) {
dnssecQueriesElement.textContent = formatNumber(dnssecQueries);
}
// 使用翻页滚动特效更新DNSSEC相关统计卡片
animateValue('dnssec-success', dnssecSuccess);
animateValue('dnssec-failed', dnssecFailed);
animateValue('dnssec-queries', dnssecQueries);
// 直接更新文本和百分比,移除动画效果
const topQueryTypeElement = document.getElementById('top-query-type');
@@ -1535,6 +1574,11 @@ function initTimeRangeToggle() {
// 初始化图表
function initCharts() {
// 检测当前是否处于深色模式
const isDarkMode = document.documentElement.classList.contains('dark');
// 根据主题模式设置不同的图例文字颜色
const legendTextColor = isDarkMode ? '#e2e8f0' : '#4B5563';
// 初始化比例图表
const ratioChartElement = document.getElementById('ratio-chart');
if (!ratioChartElement) {
@@ -1549,10 +1593,9 @@ function initCharts() {
datasets: [{
data: [0, 0, 0],
backgroundColor: ['#34D399', '#EF4444', '#F59E0B'], // 优化的现代化配色
borderWidth: 3, // 增加边框宽度,增强区块分隔
borderColor: '#FFFFFF', // 白色边框,使各个扇区更清晰
borderWidth: 0, // 移除边框宽度
hoverOffset: 15, // 增加悬停偏移效果,增强交互体验
hoverBorderWidth: 4, // 悬停时增加边框宽度
hoverBorderWidth: 0, // 移除悬停时边框宽度
hoverBackgroundColor: ['#10B981', '#DC2626', '#D97706'], // 悬停时的深色效果
borderRadius: 10, // 添加圆角效果,增强现代感
borderSkipped: false // 显示所有边框
@@ -1583,26 +1626,7 @@ function initCharts() {
lineHeight: 1.5, // 调整行高
usePointStyle: true, // 使用点样式代替方形图例,节省空间
pointStyle: 'circle', // 使用圆形点样式
color: '#4B5563', // 图例文本颜色
generateLabels: function(chart) {
// 自定义图例生成,添加更多样式控制
const data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
const dataset = data.datasets[0];
return {
text: label,
fillStyle: dataset.backgroundColor[i],
strokeStyle: dataset.borderColor,
lineWidth: dataset.borderWidth,
pointStyle: 'circle',
hidden: !chart.isDatasetVisible(0),
index: i
};
});
}
return [];
}
color: legendTextColor // 根据主题设置图例文本颜色
}
},
tooltip: {
@@ -1641,7 +1665,7 @@ function initCharts() {
display: false // 不显示标题由HTML标题代替
}
},
cutout: '70%', // 调整中心空白区域比例,增强现代感
cutout: '50%', // 调整中心空白区域比例,使环更粗
// 增强元素配置
elements: {
arc: {
@@ -1687,10 +1711,9 @@ function initCharts() {
datasets: [{
data: [1],
backgroundColor: [queryTypeColors[0]],
borderWidth: 3, // 增加边框宽度,增强区块分隔
borderColor: '#fff', // 白色边框,使各个扇区更清晰
borderWidth: 0, // 移除边框宽度
hoverOffset: 15, // 增加悬停偏移效果,增强交互体验
hoverBorderWidth: 4, // 悬停时增加边框宽度
hoverBorderWidth: 0, // 移除悬停时边框宽度
hoverBackgroundColor: queryTypeColors.map(color => {
// 生成悬停时的深色效果
const hex = color.replace('#', '');
@@ -1728,26 +1751,7 @@ function initCharts() {
lineHeight: 1.5, // 调整行高
usePointStyle: true, // 使用点样式代替方形图例,节省空间
pointStyle: 'circle', // 使用圆形点样式
color: '#4B5563', // 图例文本颜色
generateLabels: function(chart) {
// 自定义图例生成,添加更多样式控制
const data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
const dataset = data.datasets[0];
return {
text: label,
fillStyle: dataset.backgroundColor[i],
strokeStyle: dataset.borderColor,
lineWidth: dataset.borderWidth,
pointStyle: 'circle',
hidden: !chart.isDatasetVisible(0),
index: i
};
});
}
return [];
},
color: legendTextColor, // 根据主题设置图例文本颜色
// 启用图例点击交互
onClick: function(event, legendItem, legend) {
// 切换对应数据的显示
@@ -1757,7 +1761,7 @@ function initCharts() {
ci.update();
},
// 图例悬停样式
fontColor: '#4B5563',
fontColor: legendTextColor,
usePointStyle: true,
pointStyle: 'circle'
}
@@ -1798,7 +1802,7 @@ function initCharts() {
display: false // 不显示标题由HTML标题代替
}
},
cutout: '70%', // 调整中心空白区域比例,增强现代感
cutout: '50%', // 调整中心空白区域比例,使环更粗
// 增强元素配置
elements: {
arc: {
@@ -3251,3 +3255,100 @@ async function loadDashboardData() {
// 静默失败,不显示通知以免打扰用户
}
}
// 主题切换功能
function initThemeToggle() {
const themeToggleBtn = document.getElementById('theme-toggle');
if (!themeToggleBtn) return;
// 初始化主题
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.documentElement.classList.add('dark');
updateThemeIcon(true);
}
// 添加主题切换事件监听器
themeToggleBtn.addEventListener('click', toggleTheme);
}
// 切换主题
function toggleTheme() {
const isDark = document.documentElement.classList.toggle('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
updateThemeIcon(isDark);
// 更新图表图例文字颜色
updateChartLegendColors(isDark);
}
// 更新图表图例文字颜色
function updateChartLegendColors(isDark) {
const legendTextColor = isDark ? '#e2e8f0' : '#4B5563';
// 更新比例图表图例文字颜色
if (ratioChart) {
ratioChart.options.plugins.legend.labels.color = legendTextColor;
ratioChart.options.plugins.legend.labels.fontColor = legendTextColor;
// 平滑更新图表
ratioChart.update({
duration: 300,
easing: 'easeInOutQuart'
});
}
// 更新解析类型统计饼图图例文字颜色
if (queryTypeChart) {
queryTypeChart.options.plugins.legend.labels.color = legendTextColor;
queryTypeChart.options.plugins.legend.labels.fontColor = legendTextColor;
// 平滑更新图表
queryTypeChart.update({
duration: 300,
easing: 'easeInOutQuart'
});
}
// 更新DNS请求统计图表图例文字颜色
if (dnsRequestsChart) {
if (dnsRequestsChart.options.plugins.legend) {
dnsRequestsChart.options.plugins.legend.labels.color = legendTextColor;
dnsRequestsChart.options.plugins.legend.labels.fontColor = legendTextColor;
// 平滑更新图表
dnsRequestsChart.update({
duration: 300,
easing: 'easeInOutQuart'
});
}
}
// 更新详细DNS请求统计图表图例文字颜色
if (detailedDnsRequestsChart) {
if (detailedDnsRequestsChart.options.plugins.legend) {
detailedDnsRequestsChart.options.plugins.legend.labels.color = legendTextColor;
detailedDnsRequestsChart.options.plugins.legend.labels.fontColor = legendTextColor;
// 平滑更新图表
detailedDnsRequestsChart.update({
duration: 300,
easing: 'easeInOutQuart'
});
}
}
}
// 更新主题图标
function updateThemeIcon(isDark) {
const themeToggleBtn = document.getElementById('theme-toggle');
if (!themeToggleBtn) return;
const icon = themeToggleBtn.querySelector('i');
if (isDark) {
icon.classList.remove('fa-moon-o');
icon.classList.add('fa-sun-o');
} else {
icon.classList.remove('fa-sun-o');
icon.classList.add('fa-moon-o');
}
}
// 页面加载完成后初始化主题切换
window.addEventListener('DOMContentLoaded', initThemeToggle);