修复config.go中的URL错误

This commit is contained in:
Alex Yang
2025-11-24 22:39:23 +08:00
parent a59f655769
commit b4525678e7
14 changed files with 422 additions and 117393 deletions

View File

@@ -19,6 +19,15 @@ body {
position: relative;
}
/* 基础响应式变量 */
:root {
--sidebar-width: 250px;
--sidebar-mobile-width: 70px;
--header-height: 130px;
--content-padding: 1rem;
--card-min-width: 300px;
}
/* 主容器样式 */
.container {
display: flex;
@@ -70,17 +79,79 @@ header p {
display: flex;
flex: 1;
min-height: 0;
transition: all 0.3s ease;
}
/* 侧边栏样式 */
.sidebar {
width: 250px;
width: var(--sidebar-width);
background-color: #2c3e50;
color: white;
padding: 1rem 0;
flex-shrink: 0;
overflow-y: auto;
height: calc(100vh - 130px); /* 减去header的高度 */
height: calc(100vh - var(--header-height)); /* 减去header的高度 */
transition: width 0.3s ease;
position: relative;
}
/* 移动设备侧边栏切换按钮 */
.sidebar-toggle {
position: fixed;
top: calc(var(--header-height) + 10px);
left: 10px;
z-index: 100;
background-color: #2c3e50;
color: white;
border: none;
border-radius: 4px;
padding: 8px 12px;
cursor: pointer;
display: none;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
/* 响应式布局 - 平板设备 */
@media (max-width: 992px) {
.sidebar {
width: var(--sidebar-mobile-width);
}
.nav-item span {
display: none;
}
.nav-item i {
margin-right: 0;
}
.sidebar-toggle {
display: block;
}
}
/* 响应式布局 - 移动设备 */
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: -var(--sidebar-width);
top: var(--header-height);
z-index: 99;
height: calc(100vh - var(--header-height));
}
.sidebar.open {
left: 0;
width: var(--sidebar-width);
}
.sidebar.open .nav-item span {
display: block;
}
.sidebar.open .nav-item i {
margin-right: 1rem;
}
}
.nav-menu {
@@ -114,11 +185,39 @@ header p {
/* 主内容区域样式 */
.content {
flex: 1;
padding: 1rem;
padding: var(--content-padding);
overflow-y: auto;
background-color: #f8f9fa;
min-width: 0; /* 防止flex子元素溢出 */
height: calc(100vh - 130px); /* 减去header的高度 */
height: calc(100vh - var(--header-height)); /* 减去header的高度 */
transition: padding-left 0.3s ease;
}
/* 平板设备适配 - 侧边栏折叠时调整内容区域 */
@media (max-width: 992px) {
.content {
padding-left: calc(var(--content-padding) + 10px);
}
}
/* 移动设备适配 - 侧边栏隐藏时的内容区域 */
@media (max-width: 768px) {
.content {
padding-left: var(--content-padding);
}
/* 响应式头部样式 */
header.header-container {
padding: 1rem;
}
.logo h1 {
font-size: 1.5rem;
}
header p {
font-size: 0.9rem;
}
}
/* 面板样式 */
@@ -258,18 +357,30 @@ header p {
/* 统计卡片网格 */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
gap: clamp(1rem, 3vw, 1.5rem); /* 根据屏幕宽度动态调整间距 */
margin-bottom: 2rem;
}
/* 图表容器 */
.charts-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: clamp(1rem, 3vw, 1.5rem); /* 根据屏幕宽度动态调整间距 */
margin-bottom: 1.5rem;
}
.stat-card {
background-color: white;
border-radius: 8px;
padding: 1.5rem;
padding: clamp(1rem, 3vw, 1.5rem); /* 根据屏幕宽度动态调整内边距 */
text-align: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
min-width: 0; /* 防止内容溢出 */
display: flex;
flex-direction: column;
justify-content: center;
}
.stat-card:hover {
@@ -277,6 +388,49 @@ header p {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
/* 卡片布局的响应式优化 */
@media (max-width: 640px) {
/* 在极小屏幕上,调整卡片网格为单列显示 */
.stats-grid,
.charts-container,
.tables-container {
grid-template-columns: 1fr;
}
/* 卡片更紧凑的内边距 */
.stat-card,
.chart-card,
.table-card {
padding: 1rem;
min-height: 120px;
}
/* 优化统计卡片的图标大小 */
.stat-card i {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
/* 优化统计卡片的数值和标签 */
.stat-value {
font-size: clamp(1.2rem, 5vw, 1.5rem);
}
.stat-label {
font-size: clamp(0.7rem, 3vw, 0.8rem);
}
/* 优化图表卡片标题 */
.chart-card h3 {
font-size: clamp(1rem, 4vw, 1.1rem);
}
/* 优化面板标题 */
.panel-header h2 {
font-size: clamp(1.2rem, 5vw, 1.3rem);
}
}
.stat-card i {
font-size: 2rem;
margin-bottom: 1rem;
@@ -321,10 +475,61 @@ header p {
/* 表格容器 */
.tables-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: 1.5rem;
}
/* 表格卡片样式 */
.table-card {
background-color: white;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
min-width: 0; /* 防止子元素溢出 */
}
/* 表格响应式样式 */
@media (max-width: 768px) {
/* 调整卡片内边距 */
.table-card,
.stat-card,
.chart-card {
padding: 1rem;
}
/* 调整表格单元格内边距 */
th, td {
padding: 0.5rem;
font-size: 0.9rem;
}
/* 调整表格卡片标题 */
.table-card h3,
.chart-card h3 {
font-size: 1.1rem;
}
/* 调整统计卡片数值和标签 */
.stat-value {
font-size: 1.5rem;
}
.stat-label {
font-size: 0.8rem;
}
/* 调整面板标题 */
.panel-header h2 {
font-size: 1.3rem;
}
/* 调整按钮大小 */
.btn {
padding: 0.4rem 0.8rem;
font-size: 0.85rem;
}
}
.table-card {
background-color: white;
border-radius: 8px;
@@ -346,6 +551,7 @@ header p {
margin-bottom: 16px;
display: block;
width: 100%;
-webkit-overflow-scrolling: touch; /* iOS平滑滚动 */
}
table {
@@ -353,12 +559,51 @@ table {
border-collapse: collapse;
background-color: #ffffff;
margin: 0;
table-layout: fixed; /* 固定布局,有助于响应式设计 */
}
th, td {
padding: 0.75rem 1rem;
text-align: left;
border-bottom: 1px solid #e9ecef;
word-break: break-word; /* 长文本自动换行 */
}
/* 移动设备上表格的优化 */
@media (max-width: 768px) {
/* 确保表格可以水平滚动 */
.table-wrapper {
max-width: 100%;
margin-left: -1rem;
margin-right: -1rem;
border-radius: 0;
}
/* 表格单元格内容截断处理 */
td {
font-size: 0.85rem;
max-width: 150px; /* 限制单元格最大宽度 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 当用户触摸单元格时显示完整内容 */
td:active {
white-space: normal;
word-break: break-word;
}
/* 优化百分比条在小屏幕上的显示 */
.count-cell {
position: relative;
padding-right: 50px; /* 为百分比文本留出空间 */
}
.percentage-text {
font-size: 10px;
right: 5px;
}
}
th {

View File

@@ -115,28 +115,71 @@ function fetchHostsCount() {
// 空实现,保留函数声明以避免引用错误
}
// 通用API请求函数
function apiRequest(endpoint, method = 'GET', data = null) {
// 通用API请求函数 - 添加错误处理和重试机制
function apiRequest(endpoint, method = 'GET', data = null, maxRetries = 3) {
const headers = {
'Content-Type': 'application/json'
};
const config = {
method,
headers
headers,
timeout: 10000, // 设置超时时间为10秒
};
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();
});
let retries = 0;
function makeRequest() {
return fetch(`${API_BASE_URL}${endpoint}`, config)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 检查响应是否完整
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
// 使用.text()先获取响应文本处理可能的JSON解析错误
return response.text().then(text => {
try {
return JSON.parse(text);
} catch (e) {
console.error('JSON解析错误:', e, '响应文本:', text);
// 针对ERR_INCOMPLETE_CHUNKED_ENCODING错误进行重试
if (retries < maxRetries) {
retries++;
console.warn(`请求失败,正在进行第${retries}次重试...`);
return new Promise(resolve => setTimeout(() => resolve(makeRequest()), 1000 * retries));
}
throw new Error('JSON解析失败且重试次数已达上限');
}
});
}
return response.json();
})
.catch(error => {
console.error('API请求错误:', error);
// 检查是否为网络错误或ERR_INCOMPLETE_CHUNKED_ENCODING相关错误
if ((error.name === 'TypeError' && error.message.includes('Failed to fetch')) ||
error.message.includes('incomplete chunked encoding')) {
if (retries < maxRetries) {
retries++;
console.warn(`网络错误,正在进行第${retries}次重试...`);
return new Promise(resolve => setTimeout(() => resolve(makeRequest()), 1000 * retries));
}
}
throw error;
});
}
return makeRequest();
}
// 数字格式化函数

View File

@@ -6,6 +6,67 @@ let domainDataCache = {
let domainUpdateTimer = null;
const DOMAIN_UPDATE_INTERVAL = 5000; // 域名排行更新间隔设为5秒比统计数据更新慢一些
// 初始化小型图表 - 修复Canvas重用问题
function initMiniCharts() {
// 获取所有图表容器
const chartContainers = document.querySelectorAll('.chart-card canvas');
// 全局图表实例存储
window.chartInstances = window.chartInstances || {};
chartContainers.forEach(canvas => {
// 获取图表数据属性
const chartId = canvas.id;
const chartType = canvas.dataset.chartType || 'line';
const chartData = JSON.parse(canvas.dataset.chartData || '{}');
// 设置图表上下文
const ctx = canvas.getContext('2d');
// 销毁已存在的图表实例避免Canvas重用错误
if (window.chartInstances[chartId]) {
window.chartInstances[chartId].destroy();
}
// 创建新图表
window.chartInstances[chartId] = new Chart(ctx, {
type: chartType,
data: chartData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
cornerRadius: 4
}
},
scales: {
x: {
grid: {
display: false
}
},
y: {
beginAtZero: true,
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
}
},
animation: {
duration: 1000,
easing: 'easeOutQuart'
}
}
});
});
}
// 初始化仪表盘面板
function initDashboardPanel() {
// 初始化小型图表
@@ -20,6 +81,60 @@ function initDashboardPanel() {
}
// 启动域名排行的独立更新
startDomainUpdate();
// 初始化响应式侧边栏
initResponsiveSidebar();
}
// 初始化响应式侧边栏
function initResponsiveSidebar() {
// 创建侧边栏切换按钮
const toggleBtn = document.createElement('button');
toggleBtn.className = 'sidebar-toggle';
toggleBtn.innerHTML = '<i class="fas fa-bars"></i>';
document.body.appendChild(toggleBtn);
// 侧边栏切换逻辑
toggleBtn.addEventListener('click', function() {
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('open');
// 更新按钮图标
const icon = toggleBtn.querySelector('i');
if (sidebar.classList.contains('open')) {
icon.className = 'fas fa-times';
} else {
icon.className = 'fas fa-bars';
}
});
// 在侧边栏打开时点击内容区域关闭侧边栏
const content = document.querySelector('.content');
content.addEventListener('click', function() {
const sidebar = document.querySelector('.sidebar');
const toggleBtn = document.querySelector('.sidebar-toggle');
if (sidebar.classList.contains('open') && window.innerWidth <= 768) {
sidebar.classList.remove('open');
if (toggleBtn) {
const icon = toggleBtn.querySelector('i');
icon.className = 'fas fa-bars';
}
}
});
// 窗口大小变化时调整侧边栏状态
window.addEventListener('resize', function() {
const sidebar = document.querySelector('.sidebar');
const toggleBtn = document.querySelector('.sidebar-toggle');
if (window.innerWidth > 768) {
sidebar.classList.remove('open');
if (toggleBtn) {
const icon = toggleBtn.querySelector('i');
icon.className = 'fas fa-bars';
}
}
});
}
// 加载仪表盘数据