更新web

This commit is contained in:
Alex Yang
2026-01-21 09:46:49 +08:00
parent ac96c7c10b
commit 073f1961b1
80 changed files with 75919 additions and 12379 deletions

View File

@@ -946,7 +946,7 @@ tr:hover {
/* 通知组件 */
.notification {
position: fixed;
bottom: 20px;
top: 20px;
right: 20px;
background-color: #3498db;
color: white;
@@ -954,12 +954,7 @@ tr:hover {
border-radius: 4px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
z-index: 1000;
transform: translateX(100%);
transition: transform 0.3s ease;
}
.notification.show {
transform: translateX(0);
max-width: 90%;
}
.notification.success {
@@ -1141,8 +1136,9 @@ tr:hover {
/* 跟踪器浮窗样式 */
.tracker-tooltip {
position: absolute;
top: -10px;
top: 50%;
left: 100%;
transform: translateY(-50%);
margin-left: 10px;
background-color: white;
border: 1px solid #e2e8f0;
@@ -1150,9 +1146,15 @@ tr:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
padding: 12px;
min-width: 250px;
z-index: 50;
max-width: 350px;
z-index: 9999;
font-size: 14px;
color: #333;
display: none;
opacity: 0;
transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
visibility: hidden;
word-wrap: break-word;
/* 添加箭头 */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -159,24 +159,24 @@
<!-- 主内容区 -->
<main class="flex-1 flex flex-col md:ml-64">
<!-- 顶部导航栏 -->
<header class="bg-white border-b border-gray-200 h-16 flex items-center justify-between px-6 sticky top-0 z-30">
<header class="bg-white border-b border-gray-200 h-16 flex items-center justify-between px-4 sm:px-6 sticky top-0 z-30 shadow-sm">
<div class="flex items-center">
<button id="toggle-sidebar" class="block md:hidden text-gray-500 hover:text-gray-700 focus:outline-none">
<i class="fa fa-bars text-xl"></i>
<button id="toggle-sidebar" class="block md:hidden text-gray-500 hover:text-gray-700 focus:outline-none p-1 sm:p-2 transition-all">
<i class="fa fa-bars text-lg sm:text-xl"></i>
</button>
<h2 class="ml-4 text-xl font-semibold" id="page-title">仪表盘</h2>
<h2 class="ml-2 sm:ml-4 text-base sm:text-lg md:text-xl font-semibold" id="page-title">仪表盘</h2>
</div>
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-2 sm:space-x-4">
<button class="p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100">
<i class="fa fa-bell text-lg"></i>
<button class="p-1.5 sm:p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100 transition-all">
<i class="fa fa-bell text-sm sm:text-lg"></i>
</button>
<!-- 账户下拉菜单 -->
<div class="relative group" id="account-dropdown">
<button class="flex items-center p-2 rounded-full hover:bg-gray-100 transition-colors focus:outline-none">
<img src="images/user.jpg" alt="用户头像" class="w-8 h-8 rounded-full">
<span class="ml-2 hidden md:block">管理员</span>
<button class="flex items-center p-1.5 sm:p-2 rounded-full hover:bg-gray-100 transition-colors focus:outline-none">
<img src="images/user.jpg" alt="用户头像" class="w-7 h-7 sm:w-8 sm:h-8 rounded-full">
<span class="ml-1 sm:ml-2 hidden md:block text-sm">管理员</span>
<i class="fa fa-caret-down ml-1 text-xs hidden md:block"></i>
</button>
<!-- 下拉菜单 -->
@@ -193,27 +193,27 @@
</header>
<!-- 页面内容 -->
<div class="p-6 overflow-y-auto flex-1">
<div class="p-4 sm:p-6 overflow-y-auto flex-1">
<!-- 仪表盘部分 -->
<div id="dashboard-content" class="space-y-6">
<!-- 统计卡片 -->
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 gap-6">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 sm:gap-6">
<!-- 查询总量卡片 -->
<div class="bg-blue-50 rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-blue-50 rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-primary opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-primary opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">查询总量</h3>
<div class="p-2 rounded-full bg-primary/10 text-primary">
<i class="fa fa-refresh"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">查询总量</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-primary/10 text-primary">
<i class="fa fa-refresh text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="total-queries">0</p>
<span class="text-success text-sm flex items-center">
<i class="fa fa-arrow-up mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="total-queries">0</p>
<span class="text-success text-xs sm:text-sm flex items-center">
<i class="fa fa-arrow-up mr-1 text-xs"></i>
<span id="queries-percent">0%</span>
</span>
</div>
@@ -222,21 +222,21 @@
</div>
<!-- 屏蔽数量卡片 -->
<div class="bg-red-50 rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-red-50 rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-danger opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-danger opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">屏蔽数量</h3>
<div class="p-2 rounded-full bg-danger/10 text-danger">
<i class="fa fa-ban"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">屏蔽数量</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-danger/10 text-danger">
<i class="fa fa-ban text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="blocked-queries">0</p>
<span class="text-danger text-sm flex items-center">
<i class="fa fa-arrow-up mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="blocked-queries">0</p>
<span class="text-danger text-xs sm:text-sm flex items-center">
<i class="fa fa-arrow-up mr-1 text-xs"></i>
<span id="blocked-percent">0%</span>
</span>
</div>
@@ -245,21 +245,21 @@
</div>
<!-- 正常解析卡片 -->
<div class="bg-green-50 rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-green-50 rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-success opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-success opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">正常解析</h3>
<div class="p-2 rounded-full bg-success/10 text-success">
<i class="fa fa-check"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">正常解析</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-success/10 text-success">
<i class="fa fa-check text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="allowed-queries">0</p>
<span class="text-success text-sm flex items-center">
<i class="fa fa-arrow-up mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="allowed-queries">0</p>
<span class="text-success text-xs sm:text-sm flex items-center">
<i class="fa fa-arrow-up mr-1 text-xs"></i>
<span id="allowed-percent">0%</span>
</span>
</div>
@@ -268,21 +268,21 @@
</div>
<!-- 错误数量卡片 -->
<div class="bg-yellow-50 rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-yellow-50 rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-warning opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-warning opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">错误数量</h3>
<div class="p-2 rounded-full bg-warning/10 text-warning">
<i class="fa fa-exclamation-triangle"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">错误数量</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-warning/10 text-warning">
<i class="fa fa-exclamation-triangle text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="error-queries">0</p>
<span class="text-warning text-sm flex items-center">
<i class="fa fa-arrow-up mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="error-queries">0</p>
<span class="text-warning text-xs sm:text-sm flex items-center">
<i class="fa fa-arrow-up mr-1 text-xs"></i>
<span id="error-percent">0%</span>
</span>
</div>
@@ -291,21 +291,21 @@
</div>
<!-- 平均响应时间卡片 -->
<div class="bg-white rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-white rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-info opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-info opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">平均响应时间</h3>
<div class="p-2 rounded-full bg-info/10 text-info">
<i class="fa fa-clock-o"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">平均响应时间</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-info/10 text-info">
<i class="fa fa-clock-o text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="avg-response-time">0ms</p>
<span class="text-success text-sm flex items-center">
<i class="fa fa-arrow-down mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="avg-response-time">0ms</p>
<span class="text-success text-xs sm:text-sm flex items-center">
<i class="fa fa-arrow-down mr-1 text-xs"></i>
<span id="response-time-percent">0%</span>
</span>
</div>
@@ -314,19 +314,19 @@
</div>
<!-- 最常用查询类型卡片 -->
<div class="bg-white rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-white rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-secondary opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-secondary opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">最常用查询类型</h3>
<div class="p-2 rounded-full bg-secondary/10 text-secondary">
<i class="fa fa-database"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">最常用查询类型</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-secondary/10 text-secondary">
<i class="fa fa-database text-xs sm:text-sm"></i>
</div>
</div>
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="top-query-type">A</p>
<span class="text-primary text-sm flex items-center">
<p class="text-2xl sm:text-3xl font-bold" id="top-query-type">A</p>
<span class="text-primary text-xs sm:text-sm flex items-center">
<i class="fa fa-circle text-xs mr-1"></i>
<span id="query-type-percentage">0%</span>
</span>
@@ -335,21 +335,21 @@
</div>
<!-- 活跃来源IP数卡片 -->
<div class="bg-white rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-white rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-success opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-success opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">活跃来源IP</h3>
<div class="p-2 rounded-full bg-success/10 text-success">
<i class="fa fa-globe"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">活跃来源IP</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-success/10 text-success">
<i class="fa fa-globe text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="active-ips">0</p>
<span class="text-success text-sm flex items-center">
<i class="fa fa-arrow-up mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="active-ips">0</p>
<span class="text-success text-xs sm:text-sm flex items-center">
<i class="fa fa-arrow-up mr-1 text-xs"></i>
<span id="active-ips-percent">0%</span>
</span>
</div>
@@ -358,26 +358,26 @@
</div>
<!-- DNSSEC使用率卡片 -->
<div class="bg-white rounded-lg p-4 card-shadow relative overflow-hidden">
<div class="bg-white rounded-lg p-3 sm:p-4 card-shadow relative overflow-hidden transition-all hover:shadow-md">
<!-- 颜色蒙版 -->
<div class="absolute -bottom-8 -right-8 w-24 h-24 rounded-full bg-primary opacity-10"></div>
<div class="absolute -bottom-6 -right-6 w-20 h-20 sm:w-24 sm:h-24 rounded-full bg-primary opacity-10"></div>
<div class="relative z-10">
<div class="flex items-center justify-between mb-4">
<h3 class="text-gray-500 font-medium">DNSSEC使用率</h3>
<div class="p-2 rounded-full bg-primary/10 text-primary">
<i class="fa fa-lock"></i>
<div class="flex items-center justify-between mb-3 sm:mb-4">
<h3 class="text-xs sm:text-sm text-gray-500 font-medium">DNSSEC使用率</h3>
<div class="p-1.5 sm:p-2 rounded-full bg-primary/10 text-primary">
<i class="fa fa-lock text-xs sm:text-sm"></i>
</div>
</div>
<div class="mb-2">
<div class="flex items-end justify-between">
<p class="text-3xl font-bold" id="dnssec-usage">0%</p>
<span class="text-primary text-sm flex items-center">
<i class="fa fa-check mr-1"></i>
<p class="text-2xl sm:text-3xl font-bold" id="dnssec-usage">0%</p>
<span class="text-primary text-xs sm:text-sm flex items-center">
<i class="fa fa-check mr-1 text-xs"></i>
<span id="dnssec-status">已禁用</span>
</span>
</div>
</div>
<div class="flex items-center space-x-4 text-xs text-gray-500">
<div class="flex items-center space-x-2 sm:space-x-4 text-xs sm:text-xs text-gray-500">
<div class="flex items-center">
<span class="w-2 h-2 bg-green-500 rounded-full mr-1"></span>
<span>成功: <span id="dnssec-success">0</span></span>
@@ -397,31 +397,31 @@
</div>
<!-- 图表和数据表格 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
<!-- 三个图表在同一行显示 -->
<div class="bg-white rounded-lg p-6 card-shadow lg:col-span-1 md:col-span-1">
<h3 class="text-lg font-semibold mb-6">解析与屏蔽比例</h3>
<div class="h-64 flex items-center justify-center">
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<h3 class="text-base sm:text-lg font-semibold mb-3 sm:mb-6">解析与屏蔽比例</h3>
<div class="h-48 sm:h-64 flex items-center justify-center">
<canvas id="ratio-chart"></canvas>
</div>
</div>
<div class="bg-white rounded-lg p-6 card-shadow lg:col-span-1 md:col-span-1">
<h3 class="text-lg font-semibold mb-6">解析类型统计</h3>
<div class="h-64 flex items-center justify-center">
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<h3 class="text-base sm:text-lg font-semibold mb-3 sm:mb-6">解析类型统计</h3>
<div class="h-48 sm:h-64 flex items-center justify-center">
<canvas id="query-type-chart"></canvas>
</div>
</div>
<div class="bg-white rounded-lg p-6 card-shadow lg:col-span-1 md:col-span-1">
<div class="flex items-center justify-between mb-6">
<h3 class="text-lg font-semibold">DNS请求趋势</h3>
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<div class="flex items-center justify-between mb-3 sm:mb-6">
<h3 class="text-base sm:text-lg font-semibold">DNS请求趋势</h3>
<!-- 展开按钮 -->
<button id="expand-chart-btn" class="p-2 rounded-full bg-primary/10 text-primary hover:bg-primary/20 transition-colors" title="展开详细图表">
<i class="fa fa-expand"></i>
<button id="expand-chart-btn" class="p-1.5 sm:p-2 rounded-full bg-primary/10 text-primary hover:bg-primary/20 transition-colors" title="展开详细图表">
<i class="fa fa-expand text-xs sm:text-sm"></i>
</button>
</div>
<div class="h-64">
<div class="h-48 sm:h-64">
<canvas id="dns-requests-chart"></canvas>
</div>
</div>
@@ -431,11 +431,10 @@
<div id="chart-modal" class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg w-full max-w-5xl max-h-[90vh] overflow-hidden">
<div class="flex items-center justify-between p-6 border-b border-gray-200">
<h3 class="text-xl font-semibold">DNS请求趋势详细图表</h3>
<h3 class="text-lg sm:text-xl font-semibold">DNS请求趋势详细图表</h3>
<div class="flex items-center space-x-4">
<!-- 时间范围切换按钮 -->
<div class="flex space-x-2">
<button class="time-range-btn px-4 py-2 rounded-md bg-gray-200 text-gray-700 hover:bg-gray-300 transition-colors" data-range="mixed">混合视图</button>
<button class="time-range-btn px-4 py-2 rounded-md bg-primary text-white" data-range="24h">24小时</button>
<button class="time-range-btn px-4 py-2 rounded-md bg-gray-200 text-gray-700 hover:bg-gray-300 transition-colors" data-range="7d">7天</button>
<button class="time-range-btn px-4 py-2 rounded-md bg-gray-200 text-gray-700 hover:bg-gray-300 transition-colors" data-range="30d">30天</button>
@@ -458,7 +457,7 @@
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- 被拦截域名排行 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-4">被拦截域名排行</h3>
<h3 class="text-base sm:text-lg font-semibold mb-3 sm:mb-4">被拦截域名排行</h3>
<div class="h-64 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent">
<div class="space-y-3" id="top-blocked-table">
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-danger">
@@ -512,7 +511,7 @@
<!-- 请求域名排行 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-4">请求域名排行</h3>
<h3 class="text-base sm:text-lg font-semibold mb-3 sm:mb-4">请求域名排行</h3>
<div class="h-64 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent">
<div class="space-y-3" id="top-domains-table">
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-success">
@@ -534,7 +533,7 @@
<!-- 客户端排行 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">客户端排行</h3>
<h3 class="text-base sm:text-lg font-semibold">客户端排行</h3>
<div id="top-clients-loading" class="flex items-center text-sm text-gray-500">
<i class="fa fa-spinner fa-spin mr-2"></i>
<span>加载中...</span>
@@ -602,7 +601,7 @@
<div id="shield-content" class="hidden space-y-6">
<!-- 屏蔽规则统计信息 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-6">屏蔽规则统计</h3>
<h3 class="text-base sm:text-lg font-semibold mb-4 sm:mb-6">屏蔽规则统计</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-blue-50 p-4 rounded-lg">
<div class="flex items-center justify-between mb-2">
@@ -654,17 +653,17 @@
</div>
<!-- 自定义规则管理 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-6">自定义规则管理</h3>
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<h3 class="text-base sm:text-lg font-semibold mb-4 sm:mb-6">自定义规则管理</h3>
<!-- 添加规则表单 -->
<div id="add-rule-form" class="mb-6 bg-gray-50 p-4 rounded-lg">
<div class="flex items-center space-x-4">
<input type="text" id="new-rule" placeholder="输入规则例如example.com 或 regex:/example\.com/" class="flex-1 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<button id="save-rule-btn" class="px-4 py-2 bg-success text-white rounded-md hover:bg-success/90 transition-colors">
<div id="add-rule-form" class="mb-6 bg-gray-50 p-3 sm:p-4 rounded-lg">
<div class="flex flex-col sm:flex-row items-stretch sm:items-center space-y-3 sm:space-y-0 sm:space-x-3">
<input type="text" id="new-rule" placeholder="输入规则例如example.com 或 regex:/example\.com/" class="flex-1 px-3 sm:px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<button id="save-rule-btn" class="px-3 sm:px-4 py-2 bg-success text-white rounded-md hover:bg-success/90 transition-colors text-sm">
保存
</button>
<div id="save-rule-status" class="flex items-center text-sm"></div>
<div id="save-rule-status" class="flex items-center justify-center text-xs sm:text-sm mt-2 sm:mt-0"></div>
</div>
</div>
@@ -673,14 +672,14 @@
<table class="min-w-full resizable-table">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">规则</th>
<th class="text-center py-3 px-4 text-sm font-medium text-gray-500">状态</th>
<th class="text-right py-3 px-4 text-sm font-medium text-gray-500">操作</th>
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">规则</th>
<th class="text-center py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">状态</th>
<th class="text-right py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">操作</th>
</tr>
</thead>
<tbody id="rules-table-body">
<tr>
<td colspan="3" class="py-4 text-center text-gray-500">暂无规则</td>
<td colspan="3" class="py-3 sm:py-4 text-center text-gray-500">暂无规则</td>
</tr>
</tbody>
</table>
@@ -688,27 +687,27 @@
</div>
<!-- 远程黑名单管理 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-6">远程黑名单管理</h3>
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<h3 class="text-base sm:text-lg font-semibold mb-4 sm:mb-6">远程黑名单管理</h3>
<!-- 添加黑名单表单 -->
<div id="add-blacklist-form" class="mb-6 bg-gray-50 p-4 rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div id="add-blacklist-form" class="mb-6 bg-gray-50 p-3 sm:p-4 rounded-lg">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-3 sm:gap-4">
<div>
<label for="blacklist-name" class="block text-sm font-medium text-gray-700 mb-1">名称</label>
<input type="text" id="blacklist-name" placeholder="输入黑名单名称" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<label for="blacklist-name" class="block text-xs sm:text-sm font-medium text-gray-700 mb-1">名称</label>
<input type="text" id="blacklist-name" placeholder="输入黑名单名称" class="w-full px-3 sm:px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
</div>
<div>
<label for="blacklist-url" class="block text-sm font-medium text-gray-700 mb-1">URL</label>
<input type="text" id="blacklist-url" placeholder="输入黑名单URL" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<label for="blacklist-url" class="block text-xs sm:text-sm font-medium text-gray-700 mb-1">URL</label>
<input type="text" id="blacklist-url" placeholder="输入黑名单URL" class="w-full px-3 sm:px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
</div>
<div class="flex items-end">
<div class="flex items-center space-x-2">
<button id="save-blacklist-btn" class="px-4 py-2 bg-success text-white rounded-md hover:bg-success/90 transition-colors">
<div class="flex flex-col items-start justify-end space-y-2">
<div class="flex items-center space-x-2 w-full">
<button id="save-blacklist-btn" class="flex-1 sm:flex-none px-3 sm:px-4 py-2 bg-success text-white rounded-md hover:bg-success/90 transition-colors text-sm">
保存
</button>
<div id="save-blacklist-status" class="flex items-center text-sm"></div>
</div>
<div id="save-blacklist-status" class="flex items-center justify-center text-xs sm:text-sm w-full mt-1"></div>
</div>
</div>
</div>
@@ -718,16 +717,16 @@
<table class="min-w-full resizable-table">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">名称</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">URL</th>
<th class="text-center py-3 px-4 text-sm font-medium text-gray-500">状态</th>
<th class="text-center py-3 px-4 text-sm font-medium text-gray-500"></th>
<th class="text-right py-3 px-4 text-sm font-medium text-gray-500">操作</th>
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">名称</th>
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">URL</th>
<th class="text-center py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">状态</th>
<th class="text-center py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500"></th>
<th class="text-right py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">操作</th>
</tr>
</thead>
<tbody id="blacklists-table-body">
<tr>
<td colspan="5" class="py-4 text-center text-gray-500">暂无黑名单</td>
<td colspan="5" class="py-3 sm:py-4 text-center text-gray-500">暂无黑名单</td>
</tr>
</tbody>
</table>
@@ -739,15 +738,15 @@
<div id="hosts-content" class="hidden space-y-6">
<!-- Hosts管理页面内容 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-6">Hosts条目管理</h3>
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<h3 class="text-base sm:text-lg font-semibold mb-4 sm:mb-6">Hosts条目管理</h3>
<!-- 添加hosts条目表单 -->
<div id="add-hosts-form" class="mb-6 bg-gray-50 p-4 rounded-lg">
<div class="flex items-center space-x-4">
<input type="text" id="hosts-ip" placeholder="IP地址" class="w-32 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<input type="text" id="hosts-domain" placeholder="域名" class="flex-1 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<button id="save-hosts-btn" class="px-4 py-2 bg-success text-white rounded-md hover:bg-success/90 transition-colors">
<div id="add-hosts-form" class="mb-6 bg-gray-50 p-3 sm:p-4 rounded-lg">
<div class="flex flex-col sm:flex-row items-stretch sm:items-center space-y-3 sm:space-y-0 sm:space-x-3">
<input type="text" id="hosts-ip" placeholder="IP地址" class="w-full sm:w-32 px-3 sm:px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<input type="text" id="hosts-domain" placeholder="域名" class="flex-1 px-3 sm:px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<button id="save-hosts-btn" class="px-3 sm:px-4 py-2 bg-success text-white rounded-md hover:bg-success/90 transition-colors text-sm">
保存
</button>
</div>
@@ -758,14 +757,14 @@
<table class="min-w-full resizable-table">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">IP地址</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">域名</th>
<th class="text-right py-3 px-4 text-sm font-medium text-gray-500">操作</th>
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">IP地址</th>
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">域名</th>
<th class="text-right py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">操作</th>
</tr>
</thead>
<tbody id="hosts-table-body">
<tr>
<td colspan="3" class="py-4 text-center text-gray-500">暂无Hosts条目</td>
<td colspan="3" class="py-3 sm:py-4 text-center text-gray-500">暂无Hosts条目</td>
</tr>
</tbody>
</table>
@@ -777,23 +776,23 @@
<div id="query-content" class="hidden space-y-6">
<!-- DNS查询表单 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-6">DNS查询</h3>
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<h3 class="text-base sm:text-lg font-semibold mb-4 sm:mb-6">DNS查询</h3>
<!-- 查询表单 -->
<div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4">
<div class="flex flex-col sm:flex-row space-y-3 sm:space-y-0 sm:space-x-3">
<div class="flex-1">
<input type="text" id="dns-query-domain" placeholder="输入域名例如example.com" class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<input type="text" id="dns-query-domain" placeholder="输入域名例如example.com" class="w-full px-3 sm:px-4 py-2 sm:py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
</div>
<button id="dns-query-btn" class="px-6 py-3 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors">
<i class="fa fa-search mr-2"></i>查询
<button id="dns-query-btn" class="px-4 sm:px-6 py-2 sm:py-3 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors text-sm sm:text-base">
<i class="fa fa-search mr-1 sm:mr-2 text-xs sm:text-sm"></i>查询
</button>
</div>
</div>
<!-- 查询结果展示 -->
<div id="query-result" class="bg-white rounded-lg p-6 card-shadow hidden">
<h3 class="text-lg font-semibold mb-4">查询结果</h3>
<div id="query-result" class="bg-white rounded-lg p-4 sm:p-6 card-shadow hidden">
<h3 class="text-base sm:text-lg font-semibold mb-3 sm:mb-4">查询结果</h3>
<div class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-50 p-4 rounded-lg">
@@ -832,7 +831,7 @@
</div>
<!-- 历史记录列表 -->
<div id="query-history" class="space-y-3">
<div id="query-history" class="space-y-2 sm:space-y-3">
<div class="text-center text-gray-500 py-4">
暂无查询历史
</div>
@@ -918,21 +917,21 @@
</div>
<!-- 日志搜索和过滤 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4">
<div class="flex-1">
<input type="text" id="logs-search" placeholder="搜索域名或客户端IP" class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-3">
<div class="sm:col-span-2">
<input type="text" id="logs-search" placeholder="搜索域名或客户端IP" class="w-full px-3 sm:px-4 py-2 sm:py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
</div>
<div class="w-32">
<select id="logs-result-filter" class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<div class="w-full">
<select id="logs-result-filter" class="w-full px-3 sm:px-4 py-2 sm:py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<option value="">全部结果</option>
<option value="allowed">允许</option>
<option value="blocked">屏蔽</option>
<option value="error">错误</option>
</select>
</div>
<div class="w-32">
<select id="logs-per-page" class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<div class="w-full">
<select id="logs-per-page" class="w-full px-3 sm:px-4 py-2 sm:py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<option value="10">10条/页</option>
<option value="20">20条/页</option>
<option value="30" selected>30条/页</option>
@@ -940,16 +939,18 @@
<option value="100">100条/页</option>
</select>
</div>
<button id="logs-search-btn" class="px-6 py-3 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors">
<i class="fa fa-search mr-2"></i>搜索
</button>
<div class="sm:col-span-4 mt-3">
<button id="logs-search-btn" class="w-full sm:w-auto px-4 sm:px-6 py-2 sm:py-3 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors text-sm sm:text-base">
<i class="fa fa-search mr-1 sm:mr-2 text-xs sm:text-sm"></i>搜索
</button>
</div>
</div>
</div>
<!-- 日志趋势图表 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<div class="flex items-center justify-between mb-6">
<h3 class="text-lg font-semibold">查询趋势</h3>
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<div class="flex items-center justify-between mb-4 sm:mb-6">
<h3 class="text-base sm:text-lg font-semibold">查询趋势</h3>
<div class="flex space-x-2">
<button class="time-range-btn px-4 py-2 rounded-md bg-primary text-white" data-range="24h">24小时</button>
<button class="time-range-btn px-4 py-2 rounded-md bg-gray-200 text-gray-700 hover:bg-gray-300 transition-colors" data-range="7d">7天</button>
@@ -962,8 +963,8 @@
</div>
<!-- 日志详情表格 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<div class="flex items-center justify-between mb-6">
<div class="bg-white rounded-lg p-4 sm:p-6 card-shadow">
<div class="flex items-center justify-between mb-4 sm:mb-6">
<div class="flex items-center">
<h3 class="text-lg font-semibold">查询日志详情</h3>
<button id="logs-refresh-btn" class="ml-3 p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="刷新日志">
@@ -979,32 +980,32 @@
<table class="min-w-full resizable-table">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500 cursor-pointer hover:text-primary transition-colors" data-sort="time">
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500 cursor-pointer hover:text-primary transition-colors" data-sort="time">
<div class="flex items-center">
时间
<i class="fa fa-sort ml-1 text-xs"></i>
</div>
</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500 cursor-pointer hover:text-primary transition-colors" data-sort="clientIp">
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500 cursor-pointer hover:text-primary transition-colors" data-sort="clientIp">
<div class="flex items-center">
客户端IP
<i class="fa fa-sort ml-1 text-xs"></i>
</div>
</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500 cursor-pointer hover:text-primary transition-colors" data-sort="domain">
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500 cursor-pointer hover:text-primary transition-colors" data-sort="domain">
<div class="flex items-center">
请求
<i class="fa fa-sort ml-1 text-xs"></i>
</div>
</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">响应时间</th>
<th class="text-center py-3 px-4 text-sm font-medium text-gray-500">操作</th>
<th class="text-left py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">响应时间</th>
<th class="text-center py-2 sm:py-3 px-2 sm:px-4 text-xs sm:text-sm font-medium text-gray-500">操作</th>
</tr>
</thead>
<tbody id="logs-table-body">
<tr>
<td colspan="5" class="py-8 text-center text-gray-500 border-b border-gray-100">
<i class="fa fa-file-text-o text-4xl mb-2 text-gray-300"></i>
<td colspan="5" class="py-6 sm:py-8 text-center text-gray-500 border-b border-gray-100">
<i class="fa fa-file-text-o text-3xl sm:text-4xl mb-2 text-gray-300"></i>
<div>暂无查询日志</div>
</td>
</tr>
@@ -1081,13 +1082,14 @@
</div>
</div>
<div class="mb-8">
<div class="mb-8" id="allowed-sites-section">
<h4 class="text-md font-medium mb-4">通行网站</h4>
<p class="text-sm text-gray-500 mb-4">开启以下开关后对应域名将使用GFWList目标IP进行解析</p>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div>
<div class="flex items-center">
<img src="/images/gfwlist/google.png" alt="Google" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">谷歌 (Google)</span>
</div>
<button id="gfwlist-google" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
@@ -1097,7 +1099,8 @@
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div>
<div class="flex items-center">
<img src="/images/gfwlist/youtube.png" alt="YouTube" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">YouTube</span>
</div>
<button id="gfwlist-youtube" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
@@ -1125,6 +1128,127 @@
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/amazon.png" alt="Amazon" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Amazon</span>
</div>
<button id="gfwlist-amazon" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/bbc.png" alt="BBC" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">BBC</span>
</div>
<button id="gfwlist-bbc" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/discord.png" alt="Discord" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Discord</span>
</div>
<button id="gfwlist-discord" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/dropbox.png" alt="Dropbox" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Dropbox</span>
</div>
<button id="gfwlist-dropbox" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/microsoft.png" alt="Microsoft" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Microsoft</span>
</div>
<button id="gfwlist-microsoft" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/steam.png" alt="Steam" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Steam</span>
</div>
<button id="gfwlist-steam" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/telegram.png" alt="Telegram" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Telegram</span>
</div>
<button id="gfwlist-telegram" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/tiktok.png" alt="TikTok" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">TikTok</span>
</div>
<button id="gfwlist-tiktok" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/v2ex.png" alt="V2EX" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">V2EX</span>
</div>
<button id="gfwlist-v2ex" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/wikimedia.png" alt="Wikimedia" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Wikimedia</span>
</div>
<button id="gfwlist-wikimedia" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center">
<img src="/images/gfwlist/yahoo.png" alt="Yahoo" class="w-5 h-5 mr-2">
<span class="text-sm font-medium text-gray-700">Yahoo</span>
</div>
<button id="gfwlist-yahoo" type="button" class="toggle-btn bg-gray-300 hover:bg-gray-400 text-white font-medium py-1 px-3 rounded-md transition-colors duration-200">
<i class="fa fa-toggle-off"></i>
</button>
</div>
</div>
</div>
</div>
@@ -1152,6 +1276,13 @@
<label for="dns-port" class="block text-sm font-medium text-gray-700 mb-1">端口</label>
<input type="number" id="dns-port" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="53">
</div>
<div>
<label for="dns-run-mode" class="block text-sm font-medium text-gray-700 mb-1">运行模式</label>
<select id="dns-run-mode" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<option value="parallel">并行模式</option>
<option value="fast-ip">最快的上游DNS服务器</option>
</select>
</div>
<div class="md:col-span-2">
<label for="dns-upstream-servers" class="block text-sm font-medium text-gray-700 mb-1">上游DNS服务器 (每行一个)</label>
<textarea id="dns-upstream-servers" rows="4" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="8.8.8.8&#10;1.1.1.1"></textarea>

View File

@@ -31,11 +31,10 @@ async function apiRequest(endpoint, method = 'GET', data = null) {
// 竞争:请求或超时
const response = await Promise.race([fetch(url, options), timeoutPromise]);
// 获取响应文本,用于调试和错误处理
// 获取响应文本
const responseText = await response.text();
if (!response.ok) {
// 优化错误响应处理
console.warn(`API请求失败: ${response.status}`);
// 处理401未授权错误重定向到登录页面
@@ -45,73 +44,28 @@ async function apiRequest(endpoint, method = 'GET', data = null) {
return { error: '未授权访问' };
}
// 尝试解析JSON,但如果失败,直接使用原始文本作为错误信息
// 尝试解析JSON错误响应
try {
const errorData = JSON.parse(responseText);
return { error: errorData.error || responseText || `请求失败: ${response.status}` };
} catch (parseError) {
// 当响应不是有效的JSON时(如中文错误信息),直接使用原始文本
console.warn('非JSON格式错误响应:', responseText);
// 当响应不是有效的JSON时直接使用原始文本作为错误信息
return { error: responseText || `请求失败: ${response.status}` };
}
}
// 尝试解析成功响应
// 如果响应文本为空返回null
if (!responseText || responseText.trim() === '') {
return null;
}
// 尝试解析JSON响应
try {
// 首先检查响应文本是否为空
if (!responseText || responseText.trim() === '') {
console.warn('空响应文本');
return null; // 返回null表示空响应
}
// 尝试解析JSON
const parsedData = JSON.parse(responseText);
// 检查解析后的数据是否有效
if (parsedData === null) {
console.warn('解析后的数据为null');
return null;
}
// 允许返回空数组,但不允许返回空对象
if (typeof parsedData === 'object' && !Array.isArray(parsedData) && Object.keys(parsedData).length === 0) {
console.warn('解析后的数据为空对象');
return null;
}
// 限制所有数字为两位小数
const formatNumbers = (obj) => {
if (typeof obj === 'number') {
return parseFloat(obj.toFixed(2));
} else if (Array.isArray(obj)) {
return obj.map(formatNumbers);
} else if (obj && typeof obj === 'object') {
const formattedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
formattedObj[key] = formatNumbers(obj[key]);
}
}
return formattedObj;
}
return obj;
};
const formattedData = formatNumbers(parsedData);
return formattedData;
return parsedData;
} catch (parseError) {
// 详细记录错误信息和响应内容
console.error('JSON解析错误:', parseError);
console.error('原始响应文本:', responseText);
console.error('响应长度:', responseText.length);
console.error('响应前100字符:', responseText.substring(0, 100));
// 如果是位置66附近的错误特别标记
if (parseError.message.includes('position 66')) {
console.error('位置66附近的字符:', responseText.substring(60, 75));
}
// 返回错误对象,让上层处理
return { error: 'JSON解析错误' };
}
} catch (error) {
@@ -156,66 +110,6 @@ const api = {
// 获取查询类型统计
getQueryTypeStats: () => apiRequest('/query/type?t=' + Date.now()),
// 获取屏蔽规则 - 已禁用
getShieldRules: () => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({}); // 返回空对象而非API调用
},
// 添加屏蔽规则 - 已禁用
addShieldRule: (rule) => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 删除屏蔽规则 - 已禁用
deleteShieldRule: (rule) => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 更新远程规则 - 已禁用
updateRemoteRules: () => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 获取黑名单列表 - 已禁用
getBlacklists: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve([]); // 返回空数组而非API调用
},
// 添加黑名单 - 已禁用
addBlacklist: (url) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 删除黑名单 - 已禁用
deleteBlacklist: (url) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 获取Hosts内容 - 已禁用
getHosts: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ content: '' }); // 返回空内容而非API调用
},
// 保存Hosts内容 - 已禁用
saveHosts: (content) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 刷新Hosts - 已禁用
refreshHosts: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 查询DNS记录 - 兼容多种参数格式
queryDNS: async function(domain, recordType) {
try {
@@ -231,36 +125,22 @@ const api = {
params = { domain, recordType };
}
// 尝试不同的API端点
const endpoints = ['/api/dns/query', '/dns/query', '/api/query', '/query'];
let lastError;
// 使用正确的API端点
const response = await fetch('/api/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
for (const endpoint of endpoints) {
try {
console.log(`尝试API端点: ${endpoint}`);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
if (response.ok) {
const data = await response.json();
console.log('DNS查询成功:', data);
return data;
} else {
lastError = new Error(`HTTP error! status: ${response.status} for endpoint: ${endpoint}`);
}
} catch (error) {
lastError = error;
console.log(`端点 ${endpoint} 调用失败,尝试下一个`);
}
if (response.ok) {
const data = await response.json();
console.log('DNS查询成功:', data);
return data;
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 如果所有端点都失败,抛出最后一个错误
throw lastError || new Error('所有API端点调用失败');
} catch (error) {
console.error('DNS查询API调用失败:', error);

View File

@@ -60,6 +60,7 @@ function populateConfigForm(config) {
// DNS配置 - 使用函数安全设置值,避免 || 操作符可能的错误处理
setElementValue('dns-port', getSafeValue(dnsServerConfig.Port, 53));
setElementValue('dns-run-mode', getSafeValue(dnsServerConfig.QueryMode, 'parallel'));
setElementValue('dns-upstream-servers', getSafeArray(dnsServerConfig.UpstreamServers).join('\n'));
setElementValue('dns-dnssec-upstream-servers', getSafeArray(dnsServerConfig.DNSSECUpstreamServers).join('\n'));
//setElementValue('dns-stats-file', getSafeValue(dnsServerConfig.StatsFile, 'data/stats.json'));
@@ -69,7 +70,7 @@ function populateConfigForm(config) {
setElementValue('dns-cache-size', getSafeValue(dnsServerConfig.CacheSize, 100));
setElementValue('dns-max-cache-ttl', getSafeValue(dnsServerConfig.MaxCacheTTL, 120));
setElementValue('dns-min-cache-ttl', getSafeValue(dnsServerConfig.MinCacheTTL, 5));
setElementValue('dns-enable-ipv6', getSafeValue(dnsServerConfig.EnableIPv6, false));
setElementValue('dns-enable-ipv6', getSafeValue(dnsServerConfig.enableIPv6, false));
// HTTP配置
setElementValue('http-port', getSafeValue(httpServerConfig.Port, 8080));
// 屏蔽配置
@@ -96,6 +97,8 @@ function setElementValue(elementId, value) {
}
} else if (element.tagName === 'TEXTAREA') {
element.value = value;
} else if (element.tagName === 'SELECT') {
element.value = value;
} else if (element.tagName === 'BUTTON' && element.classList.contains('toggle-btn')) {
const icon = element.querySelector('i');
if (icon) {
@@ -220,6 +223,7 @@ function collectFormData() {
return {
dnsserver: {
port: dnsPort,
queryMode: getElementValue('dns-run-mode') || 'parallel',
upstreamServers: upstreamServers,
dnssecUpstreamServers: dnssecUpstreamServers,
timeout: timeout,
@@ -313,12 +317,12 @@ function showNotification(message, type = 'info') {
// 创建新通知
const notification = document.createElement('div');
notification.className = `notification fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ease-in-out translate-y-0 opacity-0`;
notification.className = `notification fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ease-in-out translate-y-0 opacity-0`;
// 设置通知样式兼容Tailwind和原生CSS
notification.style.cssText += `
position: fixed;
bottom: 16px;
top: 16px;
right: 16px;
padding: 16px 24px;
border-radius: 8px;
@@ -331,15 +335,17 @@ function showNotification(message, type = 'info') {
if (type === 'success') {
notification.style.backgroundColor = '#10b981';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-check-circle mr-2"></i>${message}`;
} else if (type === 'error') {
notification.style.backgroundColor = '#ef4444';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-exclamation-circle mr-2"></i>${message}`;
} else {
notification.style.backgroundColor = '#3b82f6';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-info-circle mr-2"></i>${message}`;
}
notification.textContent = message;
document.body.appendChild(notification);
// 显示通知
@@ -383,13 +389,28 @@ async function loadGFWListConfig() {
// 填充GFWList配置表单
function populateGFWListForm(config) {
const gfwListConfig = config.gfwList || {};
const enabled = getSafeValue(gfwListConfig.enabled, false);
setElementValue('gfwlist-enabled', getSafeValue(gfwListConfig.enabled, false));
setElementValue('gfwlist-enabled', enabled);
setElementValue('gfwlist-target-ip', getSafeValue(gfwListConfig.ip, ''));
setElementValue('gfwlist-google', getSafeValue(config.allowGoogle, false));
setElementValue('gfwlist-youtube', getSafeValue(config.allowYouTube, false));
setElementValue('gfwlist-facebook', getSafeValue(config.allowFacebook, false));
setElementValue('gfwlist-twitter', getSafeValue(config.allowTwitter, false));
setElementValue('gfwlist-amazon', getSafeValue(config.allowAmazon, false));
setElementValue('gfwlist-bbc', getSafeValue(config.allowBBC, false));
setElementValue('gfwlist-discord', getSafeValue(config.allowDiscord, false));
setElementValue('gfwlist-dropbox', getSafeValue(config.allowDropbox, false));
setElementValue('gfwlist-microsoft', getSafeValue(config.allowMicrosoft, false));
setElementValue('gfwlist-steam', getSafeValue(config.allowSteam, false));
setElementValue('gfwlist-telegram', getSafeValue(config.allowTelegram, false));
setElementValue('gfwlist-tiktok', getSafeValue(config.allowTikTok, false));
setElementValue('gfwlist-v2ex', getSafeValue(config.allowV2EX, false));
setElementValue('gfwlist-wikimedia', getSafeValue(config.allowWikimedia, false));
setElementValue('gfwlist-yahoo', getSafeValue(config.allowYahoo, false));
// 更新通行网站部分的显示效果
updateAllowedSitesSection(enabled);
}
// 保存GFWList配置
@@ -424,10 +445,53 @@ function collectGFWListFormData() {
allowGoogle: getElementValue('gfwlist-google'),
allowYouTube: getElementValue('gfwlist-youtube'),
allowFacebook: getElementValue('gfwlist-facebook'),
allowTwitter: getElementValue('gfwlist-twitter')
allowTwitter: getElementValue('gfwlist-twitter'),
allowAmazon: getElementValue('gfwlist-amazon'),
allowBBC: getElementValue('gfwlist-bbc'),
allowDiscord: getElementValue('gfwlist-discord'),
allowDropbox: getElementValue('gfwlist-dropbox'),
allowMicrosoft: getElementValue('gfwlist-microsoft'),
allowSteam: getElementValue('gfwlist-steam'),
allowTelegram: getElementValue('gfwlist-telegram'),
allowTikTok: getElementValue('gfwlist-tiktok'),
allowV2EX: getElementValue('gfwlist-v2ex'),
allowWikimedia: getElementValue('gfwlist-wikimedia'),
allowYahoo: getElementValue('gfwlist-yahoo')
};
}
// 更新通行网站部分的显示效果
function updateAllowedSitesSection(enabled) {
const section = document.getElementById('allowed-sites-section');
if (!section) return;
const siteCards = section.querySelectorAll('.bg-gray-50');
const siteLabels = section.querySelectorAll('.text-gray-700');
const siteToggles = section.querySelectorAll('.toggle-btn');
if (enabled) {
// GFWList已启用显示彩色且可点击
section.classList.remove('opacity-50');
siteCards.forEach(card => {
card.style.filter = 'grayscale(0%)';
});
siteToggles.forEach(toggle => {
toggle.disabled = false;
toggle.classList.remove('cursor-not-allowed');
});
} else {
// GFWList已禁用显示灰色且不可点击
section.classList.add('opacity-50');
siteCards.forEach(card => {
card.style.filter = 'grayscale(100%)';
});
siteToggles.forEach(toggle => {
toggle.disabled = true;
toggle.classList.add('cursor-not-allowed');
});
}
}
// 重启GFWList服务
async function handleRestartGFWListService() {
if (!confirm('确定要重启DNS服务吗重启期间服务可能会短暂不可用。')) return;
@@ -460,6 +524,11 @@ function setupGFWListEventListeners() {
// 切换按钮状态
const currentState = this.classList.contains('bg-success');
setElementValue(this.id, !currentState);
// 如果是GFWList启用开关更新通行网站部分的显示效果
if (this.id === 'gfwlist-enabled') {
updateAllowedSitesSection(!currentState);
}
});
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -1232,11 +1232,11 @@ async function updateLogsTable(logs) {
// 构建跟踪器浮窗内容
const trackerTooltip = isTracker ? `
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
<div class="font-semibold mb-1">已知跟踪器</div>
<div class="mb-1">名称: ${trackerInfo.name}</div>
<div class="mb-1">类别: ${trackersDatabase.categories[trackerInfo.categoryId] || '未知'}</div>
${trackerInfo.url ? `<div class="mb-1">URL: <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
${trackerInfo.source ? `<div class="mb-1">源: ${trackerInfo.source}</div>` : ''}
<div class="font-semibold mb-2">已知跟踪器</div>
<div class="mb-1"><strong>名称:</strong> ${trackerInfo.name || '未知'}</div>
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'}</div>
${trackerInfo.url ? `<div class="mb-1"><strong>URL:</strong> <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
${trackerInfo.source ? `<div class="mb-1"><strong>源:</strong> ${trackerInfo.source}</div>` : ''}
</div>
` : '';
@@ -1307,14 +1307,15 @@ async function updateLogsTable(logs) {
const iconContainer = row.querySelector('.tracker-icon-container');
const tooltip = iconContainer.querySelector('.tracker-tooltip');
if (iconContainer && tooltip) {
tooltip.style.display = 'none';
// 移除内联样式使用CSS类控制显示
tooltip.removeAttribute('style');
iconContainer.addEventListener('mouseenter', () => {
tooltip.style.display = 'block';
tooltip.classList.add('visible');
});
iconContainer.addEventListener('mouseleave', () => {
tooltip.style.display = 'none';
tooltip.classList.remove('visible');
});
}
}
@@ -1945,6 +1946,17 @@ async function showLogDetailModal(log) {
</div>
`;
// 构建跟踪器浮窗内容
const trackerTooltip = isTracker ? `
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
<div class="font-semibold mb-2">已知跟踪器</div>
<div class="mb-1"><strong>名称:</strong> ${trackerInfo.name || '未知'}</div>
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'}</div>
${trackerInfo.url ? `<div class="mb-1"><strong>URL:</strong> <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
${trackerInfo.source ? `<div class="mb-1"><strong>源:</strong> ${trackerInfo.source}</div>` : ''}
</div>
` : '';
// 跟踪器信息
const trackerDiv = document.createElement('div');
trackerDiv.className = 'col-span-1 md:col-span-2 space-y-1';
@@ -1953,13 +1965,34 @@ async function showLogDetailModal(log) {
<div class="text-sm font-medium text-gray-900">
${isTracker ? `
<div class="flex items-center">
<i class="fa fa-eye text-red-500 mr-1"></i>
<span>${trackerInfo.name} (${trackersDatabase.categories[trackerInfo.categoryId] || '未知'})</span>
<div class="tracker-icon-container relative">
<i class="fa fa-eye text-red-500 mr-1" title="已知跟踪器"></i>
${trackerTooltip}
</div>
<span>${trackerInfo.name} (${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'})</span>
</div>
` : '无'}
</div>
`;
// 添加跟踪器图标悬停事件
if (isTracker) {
const iconContainer = trackerDiv.querySelector('.tracker-icon-container');
const tooltip = iconContainer.querySelector('.tracker-tooltip');
if (iconContainer && tooltip) {
// 移除内联样式使用CSS类控制显示
tooltip.removeAttribute('style');
iconContainer.addEventListener('mouseenter', () => {
tooltip.classList.add('visible');
});
iconContainer.addEventListener('mouseleave', () => {
tooltip.classList.remove('visible');
});
}
}
// 解析记录
const recordsDiv = document.createElement('div');
recordsDiv.className = 'col-span-1 md:col-span-2 space-y-1';

View File

@@ -1,115 +1,5 @@
// main.js - 主脚本文件
// 页面导航功能
function setupNavigation() {
// 侧边栏菜单项
const menuItems = document.querySelectorAll('nav a');
const contentSections = [
document.getElementById('dashboard-content'),
document.getElementById('shield-content'),
document.getElementById('hosts-content'),
document.getElementById('gfwlist-content'),
document.getElementById('query-content'),
document.getElementById('logs-content'),
document.getElementById('config-content')
];
const pageTitle = document.getElementById('page-title');
menuItems.forEach((item, index) => {
item.addEventListener('click', (e) => {
// 允许浏览器自动更新地址栏中的hash不阻止默认行为
// 移动端点击菜单项后自动关闭侧边栏
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 移动端侧边栏切换
const toggleSidebar = document.getElementById('toggle-sidebar');
const closeSidebarBtn = document.getElementById('close-sidebar');
const sidebar = document.getElementById('mobile-sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
// 打开侧边栏函数
function openSidebar() {
console.log('Opening sidebar...');
if (sidebar) {
sidebar.classList.remove('-translate-x-full');
sidebar.classList.add('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.remove('hidden');
sidebarOverlay.classList.add('block');
}
// 防止页面滚动
document.body.style.overflow = 'hidden';
console.log('Sidebar opened successfully');
}
// 关闭侧边栏函数
function closeSidebar() {
console.log('Closing sidebar...');
if (sidebar) {
sidebar.classList.add('-translate-x-full');
sidebar.classList.remove('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.add('hidden');
sidebarOverlay.classList.remove('block');
}
// 恢复页面滚动
document.body.style.overflow = '';
console.log('Sidebar closed successfully');
}
// 切换侧边栏函数
function toggleSidebarVisibility() {
console.log('Toggling sidebar visibility...');
console.log('Current sidebar classes:', sidebar ? sidebar.className : 'sidebar not found');
if (sidebar && sidebar.classList.contains('-translate-x-full')) {
console.log('Sidebar is hidden, opening...');
openSidebar();
} else {
console.log('Sidebar is visible, closing...');
closeSidebar();
}
}
// 绑定切换按钮事件
if (toggleSidebar) {
toggleSidebar.addEventListener('click', toggleSidebarVisibility);
}
// 绑定关闭按钮事件
if (closeSidebarBtn) {
closeSidebarBtn.addEventListener('click', closeSidebar);
}
// 绑定遮罩层点击事件
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', closeSidebar);
}
// 移动端点击菜单项后自动关闭侧边栏
menuItems.forEach(item => {
item.addEventListener('click', () => {
// 检查是否是移动设备视图
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 添加键盘事件监听按ESC键关闭侧边栏
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeSidebar();
}
});
}
// 页面初始化函数 - 根据当前hash值初始化对应页面
function initPageByHash() {
const hash = window.location.hash.substring(1);
@@ -186,41 +76,6 @@ function initPageByHash() {
}
}
// 初始化函数
function init() {
// 设置导航
setupNavigation();
// 初始化页面
initPageByHash();
// 添加hashchange事件监听处理浏览器前进/后退按钮
window.addEventListener('hashchange', initPageByHash);
// 定期更新系统状态
setInterval(updateSystemStatus, 5000);
}
// 更新系统状态
function updateSystemStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = `正常运行中 | ${formatUptime(data.uptime)}`;
}
})
.catch(error => {
console.error('更新系统状态失败:', error);
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = '连接异常';
uptimeElement.classList.add('text-danger');
}
});
}
// 格式化运行时间
function formatUptime(milliseconds) {
// 简化版的格式化实际使用时需要根据API返回的数据格式调整
@@ -240,6 +95,29 @@ function formatUptime(milliseconds) {
}
}
// 更新系统状态
function updateSystemStatus() {
api.getStatus()
.then(data => {
if (data.error) {
throw new Error(data.error);
}
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = `正常运行中 | ${formatUptime(data.uptime)}`;
uptimeElement.classList.remove('text-danger');
}
})
.catch(error => {
console.error('更新系统状态失败:', error);
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = '连接异常';
uptimeElement.classList.add('text-danger');
}
});
}
// 账户功能 - 下拉菜单、注销和修改密码
function setupAccountFeatures() {
// 下拉菜单功能
@@ -392,6 +270,107 @@ function setupAccountFeatures() {
}
}
// 页面导航功能
function setupNavigation() {
// 侧边栏菜单项
const menuItems = document.querySelectorAll('nav a');
const pageTitle = document.getElementById('page-title');
menuItems.forEach((item, index) => {
item.addEventListener('click', (e) => {
// 允许浏览器自动更新地址栏中的hash不阻止默认行为
// 移动端点击菜单项后自动关闭侧边栏
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 移动端侧边栏切换
const toggleSidebar = document.getElementById('toggle-sidebar');
const closeSidebarBtn = document.getElementById('close-sidebar');
const sidebar = document.getElementById('mobile-sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
// 打开侧边栏函数
function openSidebar() {
console.log('Opening sidebar...');
if (sidebar) {
sidebar.classList.remove('-translate-x-full');
sidebar.classList.add('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.remove('hidden');
sidebarOverlay.classList.add('block');
// 防止页面滚动
document.body.style.overflow = 'hidden';
}
console.log('Sidebar opened successfully');
}
// 关闭侧边栏函数
function closeSidebar() {
console.log('Closing sidebar...');
if (sidebar) {
sidebar.classList.add('-translate-x-full');
sidebar.classList.remove('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.add('hidden');
sidebarOverlay.classList.remove('block');
// 恢复页面滚动
document.body.style.overflow = '';
}
console.log('Sidebar closed successfully');
}
// 切换侧边栏函数
function toggleSidebarVisibility() {
console.log('Toggling sidebar visibility...');
console.log('Current sidebar classes:', sidebar ? sidebar.className : 'sidebar not found');
if (sidebar && sidebar.classList.contains('-translate-x-full')) {
console.log('Sidebar is hidden, opening...');
openSidebar();
} else {
console.log('Sidebar is visible, closing...');
closeSidebar();
}
}
// 绑定切换按钮事件
if (toggleSidebar) {
toggleSidebar.addEventListener('click', toggleSidebarVisibility);
}
// 绑定关闭按钮事件
if (closeSidebarBtn) {
closeSidebarBtn.addEventListener('click', closeSidebar);
}
// 绑定遮罩层点击事件
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', closeSidebar);
}
// 移动端点击菜单项后自动关闭侧边栏
menuItems.forEach(item => {
item.addEventListener('click', () => {
// 检查是否是移动设备视图
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 添加键盘事件监听按ESC键关闭侧边栏
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeSidebar();
}
});
}
// 初始化函数
function init() {
// 设置导航

View File

@@ -1248,7 +1248,7 @@ function showNotification(message, type = 'info') {
// 创建新通知
const notification = document.createElement('div');
notification.className = `notification fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 ease-in-out translate-y-0 opacity-0`;
notification.className = `notification fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 ease-in-out translate-y-0 opacity-0`;
// 设置通知样式
if (type === 'success') {