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

1190 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DNS服务器管理中心</title>
<!-- 引入Font Awesome图标 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--primary-color: #3498db;
--secondary-color: #2c3e50;
--success-color: #2ecc71;
--danger-color: #e74c3c;
--warning-color: #f39c12;
--info-color: #3498db;
--light-color: #ecf0f1;
--dark-color: #34495e;
--gray-100: #f8f9fa;
--gray-200: #e9ecef;
--gray-300: #dee2e6;
--gray-400: #ced4da;
--gray-500: #adb5bd;
--gray-600: #6c757d;
--gray-700: #495057;
--gray-800: #343a40;
--gray-900: #212529;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--transition-fast: 0.2s ease;
--transition-normal: 0.3s ease;
--border-radius-sm: 0.25rem;
--border-radius: 0.375rem;
--border-radius-md: 0.5rem;
--border-radius-lg: 0.75rem;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background-color: var(--gray-100);
color: var(--gray-800);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
padding: 2rem 0;
border-radius: var(--border-radius-lg);
margin-bottom: 2rem;
box-shadow: var(--shadow-md);
text-align: center;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
font-weight: 700;
}
header p {
font-size: 1.1rem;
opacity: 0.9;
}
.tabs {
background-color: white;
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow);
margin-bottom: 2rem;
overflow: hidden;
}
.tab-nav {
display: flex;
background-color: var(--gray-50);
border-bottom: 1px solid var(--gray-200);
}
.tab-btn {
flex: 1;
padding: 1rem 1.5rem;
background: none;
border: none;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
color: var(--gray-600);
transition: all var(--transition-fast);
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.tab-btn:hover {
background-color: var(--gray-100);
color: var(--primary-color);
}
.tab-btn.active {
color: var(--primary-color);
background-color: white;
box-shadow: inset 0 3px 0 var(--primary-color);
}
.tab-content {
display: none;
padding: 2rem;
animation: fadeIn 0.3s ease-in-out;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.card {
background-color: white;
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow);
padding: 1.5rem;
margin-bottom: 1.5rem;
transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.card-header {
border-bottom: 1px solid var(--gray-200);
padding-bottom: 1rem;
margin-bottom: 1rem;
}
.card-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--gray-800);
display: flex;
align-items: center;
gap: 0.5rem;
}
.card-body {
padding: 0.5rem 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background-color: white;
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow);
padding: 1rem;
text-align: center;
transition: transform var(--transition-fast);
border-top: 4px solid var(--primary-color);
}
.stat-card:hover {
transform: translateY(-3px);
}
.stat-card i {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: var(--primary-color);
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--gray-800);
margin-bottom: 0.25rem;
position: relative;
transition: all 0.3s ease;
}
.stat-value.update {
animation: glow 1s ease-out;
}
@keyframes glow {
0% {
text-shadow: 0 0 5px var(--primary-color), 0 0 10px var(--primary-color);
transform: scale(1.1);
}
100% {
text-shadow: none;
transform: scale(1);
}
}
.mini-chart-container {
height: 60px;
margin-top: 0.5rem;
opacity: 0.8;
}
.stat-label {
font-size: 0.9rem;
color: var(--gray-600);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.chart-container {
position: relative;
height: 300px;
margin: 1rem 0;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 1.5rem;
}
input[type="text"], select {
flex: 1;
padding: 0.75rem 1rem;
border: 1px solid var(--gray-300);
border-radius: var(--border-radius);
font-size: 1rem;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
background-color: white;
}
input[type="text"]:focus, select:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all var(--transition-fast);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-success {
background-color: var(--success-color);
color: white;
}
.btn-success:hover {
background-color: #27ae60;
}
.btn-danger {
background-color: var(--danger-color);
color: white;
}
.btn-danger:hover {
background-color: #c0392b;
}
.btn-outline {
background-color: transparent;
color: var(--primary-color);
border: 1px solid var(--primary-color);
}
.btn-outline:hover {
background-color: var(--primary-color);
color: white;
}
.btn-secondary {
background-color: var(--secondary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.btn-secondary:hover {
background-color: #1a252f;
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-sm {
padding: 4px 8px;
font-size: 0.875rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-text {
display: block;
margin-top: 0.25rem;
font-size: 0.875rem;
color: var(--gray-600);
}
.mb-3 {
margin-bottom: 1rem;
}
.mt-3 {
margin-top: 1rem;
}
.rule-items {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.rule-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem;
background-color: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: 4px;
}
.rule-text {
flex: 1;
word-break: break-all;
margin-right: 0.5rem;
}
/* 悬浮通知样式 */
.notification {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
padding: 1rem 1.5rem;
border-radius: var(--border-radius);
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: 0.75rem;
animation: slideIn 0.3s ease-out;
max-width: 400px;
}
.notification-success {
background-color: white;
border-left: 4px solid var(--success-color);
color: var(--gray-800);
}
.notification-danger {
background-color: white;
border-left: 4px solid var(--danger-color);
color: var(--gray-800);
}
.notification-warning {
background-color: white;
border-left: 4px solid var(--warning-color);
color: var(--gray-800);
}
.notification-info {
background-color: white;
border-left: 4px solid var(--info-color);
color: var(--gray-800);
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.notification-icon {
font-size: 1.25rem;
}
.notification-success .notification-icon {
color: var(--success-color);
}
.notification-danger .notification-icon {
color: var(--danger-color);
}
.notification-warning .notification-icon {
color: var(--warning-color);
}
.notification-info .notification-icon {
color: var(--info-color);
}
.notification-content {
flex: 1;
font-weight: 500;
}
.notification-close {
background: none;
border: none;
font-size: 1.25rem;
cursor: pointer;
color: var(--gray-500);
padding: 0;
margin-left: 0.5rem;
transition: color var(--transition-fast);
}
.notification-close:hover {
color: var(--gray-700);
}
.list-container {
max-height: 400px;
overflow-y: auto;
border-radius: var(--border-radius);
border: 1px solid var(--gray-200);
background-color: white;
}
.list-item {
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--gray-100);
display: flex;
align-items: center;
justify-content: space-between;
transition: background-color var(--transition-fast);
}
.list-item:hover {
background-color: var(--gray-50);
}
.list-item:last-child {
border-bottom: none;
}
.list-content {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.list-title {
font-weight: 500;
color: var(--gray-800);
}
.list-description {
font-size: 0.875rem;
color: var(--gray-600);
}
.list-actions {
display: flex;
gap: 0.5rem;
}
.btn-sm {
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
border-radius: var(--border-radius-sm);
}
.btn-edit {
background-color: var(--info-color);
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
}
.btn-edit:hover {
background-color: #2980b9;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
cursor: pointer;
width: 18px;
height: 18px;
}
.list-meta {
display: flex;
gap: 1rem;
font-size: 0.75rem;
color: var(--gray-500);
}
.list-meta span {
display: flex;
align-items: center;
gap: 0.25rem;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: var(--gray-500);
}
.empty-state i {
font-size: 3rem;
margin-bottom: 1rem;
opacity: 0.5;
}
.empty-state p {
font-size: 1rem;
}
.alert {
padding: 1rem;
border-radius: var(--border-radius);
margin-bottom: 1rem;
border-left: 4px solid;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border-color: var(--success-color);
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
border-color: var(--danger-color);
}
.alert-info {
background-color: #d1ecf1;
color: #0c5460;
border-color: var(--info-color);
}
.status-info {
margin-top: 2rem;
padding: 1.5rem;
background-color: var(--gray-50);
border-radius: var(--border-radius-lg);
border-left: 4px solid var(--info-color);
box-shadow: var(--shadow);
}
.status-info h3 {
margin-bottom: 1rem;
}
.status-info p {
margin: 0.5rem 0;
line-height: 1.6;
}
.status-info .stat-card {
border-top: 4px solid var(--primary-color);
}
.status-info p {
margin-bottom: 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.status-info strong {
color: var(--gray-700);
}
pre {
background-color: var(--gray-900);
color: var(--gray-100);
padding: 1.5rem;
border-radius: var(--border-radius);
overflow-x: auto;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.5;
}
.badge {
display: inline-block;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
font-weight: 600;
border-radius: 9999px;
text-transform: uppercase;
}
.badge-primary {
background-color: var(--primary-color);
color: white;
}
.badge-success {
background-color: var(--success-color);
color: white;
}
.badge-danger {
background-color: var(--danger-color);
color: white;
}
.badge-warning {
background-color: var(--warning-color);
color: white;
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 1rem;
}
header h1 {
font-size: 2rem;
}
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 1rem;
}
.input-group {
flex-direction: column;
}
.tab-nav {
overflow-x: auto;
white-space: nowrap;
}
.tab-btn {
flex: none;
}
.tab-content {
padding: 1rem;
}
}
/* 加载动画 */
.loader {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid var(--gray-200);
border-radius: 50%;
border-top-color: var(--primary-color);
animation: spin 0.8s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 滚动条美化 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--gray-100);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--gray-400);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--gray-500);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1><i class="fas fa-server"></i> DNS服务器管理中心</h1>
<p>高性能DNS服务器支持规则屏蔽和Hosts管理</p>
</header>
<div class="tabs">
<div class="tab-nav">
<button class="tab-btn active" onclick="openTab(event, 'dashboard')">
<i class="fas fa-tachometer-alt"></i> 概览
</button>
<button class="tab-btn" onclick="openTab(event, 'check-filter')">
<i class="fas fa-search"></i> 检查过滤
</button>
<button class="tab-btn" onclick="openTab(event, 'hosts')">
<i class="fas fa-list-ul"></i> Hosts管理
</button>
<button class="tab-btn" onclick="openTab(event, 'query')">
<i class="fas fa-search"></i> DNS查询
</button>
</div>
<!-- 概览面板 -->
<div id="dashboard" class="tab-content active">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem;">
<h2>服务器状态</h2>
<div style="display: flex; gap: 1rem;">
<div style="background-color: white; padding: 0.5rem 1rem; border-radius: var(--border-radius-md); box-shadow: var(--shadow); display: flex; align-items: center; gap: 0.5rem;">
<i class="fas fa-ban" style="color: var(--primary-color);"></i>
<span style="font-size: 1rem; font-weight: 600;">规则: <span id="rules-count-inline">--</span></span>
</div>
<div style="background-color: white; padding: 0.5rem 1rem; border-radius: var(--border-radius-md); box-shadow: var(--shadow); display: flex; align-items: center; gap: 0.5rem;">
<i class="fas fa-file-alt" style="color: var(--primary-color);"></i>
<span style="font-size: 1rem; font-weight: 600;">Hosts: <span id="hosts-count-inline">--</span></span>
</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<i class="fas fa-question-circle"></i>
<div class="stat-value" id="query-count">--</div>
<div class="stat-label">DNS查询次数</div>
<div class="mini-chart-container">
<canvas id="query-chart"></canvas>
</div>
</div>
<div class="stat-card">
<i class="fas fa-times-circle"></i>
<div class="stat-value" id="blocked-count">--</div>
<div class="stat-label">屏蔽次数</div>
<div class="mini-chart-container">
<canvas id="blocked-chart"></canvas>
</div>
</div>
</div>
<h2 style="margin-bottom: 1.5rem; margin-top: 2rem;">TOP域名统计</h2>
<div class="grid-2">
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-ban"></i> TOP 10 屏蔽域名</h3>
</div>
<div class="card-body">
<div id="top-blocked-domains" class="list-container">
<div class="empty-state">
<i class="fas fa-info-circle"></i>
<p>加载中...</p>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-globe"></i> TOP 10 解析域名</h3>
</div>
<div class="card-body">
<div id="top-resolved-domains" class="list-container">
<div class="empty-state">
<i class="fas fa-info-circle"></i>
<p>加载中...</p>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-chart-line"></i> 24小时屏蔽统计</h3>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="blockChart"></canvas>
</div>
</div>
</div>
<div class="status-info">
<h3 style="margin-bottom: 1rem; font-size: 1.25rem; color: var(--gray-700);">服务器信息</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1rem; margin-bottom: 1.5rem;">
<div class="stat-card">
<i class="fas fa-ban"></i>
<div class="stat-value" id="rules-count">--</div>
<div class="stat-label">屏蔽规则数</div>
<div class="mini-chart-container">
<canvas id="rules-chart"></canvas>
</div>
</div>
<div class="stat-card">
<i class="fas fa-file-alt"></i>
<div class="stat-value" id="hosts-count">--</div>
<div class="stat-label">Hosts条目数</div>
<div class="mini-chart-container">
<canvas id="hosts-chart"></canvas>
</div>
</div>
</div>
<p><strong>服务器地址:</strong> <span id="server-address">--</span></p>
<p><strong>当前时间:</strong> <span id="current-time">--</span></p>
<p><strong>运行状态:</strong> <span class="badge badge-success">正常运行</span></p>
</div>
</div>
<!-- 检查过滤面板 -->
<div id="check-filter" class="tab-content">
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-search"></i> 检查过滤</h3>
</div>
<div class="card-body">
<p style="margin-bottom: 1rem;">检查主机名是否被过滤。</p>
<div class="input-group">
<input type="text" id="check-domain" placeholder="主机名或域名" value="example.com">
<select id="check-record-type">
<option value="A">A</option>
<option value="AAAA">AAAA</option>
<option value="CNAME">CNAME</option>
<option value="MX">MX</option>
<option value="TXT">TXT</option>
<option value="NS">NS</option>
</select>
<button id="check-btn" class="btn-primary" onclick="checkDomainFilter()">
<i class="fas fa-search"></i> 检查
</button>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-info-circle"></i> 检查结果</h3>
</div>
<div class="card-body">
<div id="check-result" class="list-container" style="max-height: 500px;">
<div class="empty-state">
<i class="fas fa-search"></i>
<p>请输入域名并点击检查按钮</p>
</div>
</div>
</div>
</div>
</div>
<!-- Hosts管理面板 -->
<div id="hosts" class="tab-content">
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-plus-circle"></i> 添加Hosts条目</h3>
</div>
<div class="card-body">
<div class="input-group">
<input type="text" id="hosts-ip" placeholder="IP地址例如127.0.0.1">
<input type="text" id="hosts-domain" placeholder="域名例如localhost">
<button id="add-hosts-btn" class="btn-primary">
<i class="fas fa-plus"></i> 添加
</button>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-list"></i> 当前Hosts条目</h3>
</div>
<div class="card-body">
<div id="hosts-container" class="list-container">
<div class="empty-state">
<i class="fas fa-info-circle"></i>
<p>Hosts列表加载中...</p>
</div>
</div>
</div>
</div>
</div>
<!-- DNS查询面板 -->
<div id="query" class="tab-content">
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-search"></i> DNS查询</h3>
</div>
<div class="card-body">
<div class="input-group">
<input type="text" id="query-domain" placeholder="输入要查询的域名">
<button id="query-btn" class="btn-primary">
<i class="fas fa-search"></i> 查询
</button>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-code"></i> 查询结果</h3>
</div>
<div class="card-body">
<pre id="query-result-text">请输入域名并点击查询按钮</pre>
</div>
</div>
</div>
</div>
</div>
<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)
};
// 检查域名过滤状态
function checkDomainFilter() {
const domain = document.getElementById('check-domain').value.trim();
const recordType = document.getElementById('check-record-type').value;
const resultContainer = document.getElementById('check-result');
if (!domain) {
showNotification('warning', '请输入域名');
return;
}
// 显示加载状态
resultContainer.innerHTML = `
<div class="empty-state">
<i class="fas fa-spinner fa-spin"></i>
<p>正在检查域名过滤状态...</p>
</div>
`;
// 发送请求到后端API
fetch(`/api/query?domain=${encodeURIComponent(domain)}&type=${recordType}`)
.then(response => {
if (!response.ok) {
throw new Error('网络响应错误');
}
return response.json();
})
.then(data => {
// 渲染结果
renderCheckResult(data, domain);
})
.catch(error => {
resultContainer.innerHTML = `
<div class="alert alert-danger">
<i class="fas fa-exclamation-circle"></i>
检查失败: ${error.message}
</div>
`;
});
}
// 渲染检查结果
function renderCheckResult(data, domain) {
const resultContainer = document.getElementById('check-result');
// 清空结果容器
resultContainer.innerHTML = '';
// 创建基本信息卡片
const basicInfo = document.createElement('div');
basicInfo.className = 'list-item';
let statusIcon, statusText, statusClass;
if (data.isBlocked) {
statusIcon = '<i class="fas fa-ban" style="color: var(--danger-color);"></i>';
statusText = '已屏蔽';
statusClass = 'badge badge-danger';
} else {
statusIcon = '<i class="fas fa-check-circle" style="color: var(--success-color);"></i>';
statusText = '未屏蔽';
statusClass = 'badge badge-success';
}
basicInfo.innerHTML = `
<div class="list-content">
<div class="list-title">
${statusIcon} 域名检查结果: ${domain}
</div>
<div class="list-meta">
<span>状态: <span class="${statusClass}">${statusText}</span></span>
<span>记录类型: ${data.recordType || 'A'}</span>
</div>
</div>
`;
resultContainer.appendChild(basicInfo);
// 如果被屏蔽,显示详细信息
if (data.isBlocked && data.blockDetails) {
// 显示屏蔽规则信息
const ruleInfo = document.createElement('div');
ruleInfo.className = 'list-item';
ruleInfo.innerHTML = `
<div class="list-content">
<div class="list-title"><i class="fas fa-info-circle" style="color: var(--info-color);"></i> 屏蔽规则详情</div>
</div>
`;
resultContainer.appendChild(ruleInfo);
// 显示具体的屏蔽规则
const blockDetails = data.blockDetails;
// 规则内容
const ruleItem = document.createElement('div');
ruleItem.className = 'list-item';
ruleItem.innerHTML = `
<div class="list-content">
<div class="list-title">匹配规则</div>
<div class="list-description">${blockDetails.ruleContent || '未提供'}</div>
</div>
`;
resultContainer.appendChild(ruleItem);
// 规则类型
const typeItem = document.createElement('div');
typeItem.className = 'list-item';
typeItem.innerHTML = `
<div class="list-content">
<div class="list-title">规则类型</div>
<div class="list-description">
${getRuleTypeText(blockDetails.ruleType)}
</div>
</div>
`;
resultContainer.appendChild(typeItem);
// 屏蔽列表
if (blockDetails.listName) {
const listItem = document.createElement('div');
listItem.className = 'list-item';
listItem.innerHTML = `
<div class="list-content">
<div class="list-title">所属屏蔽列表</div>
<div class="list-description">${blockDetails.listName}</div>
</div>
`;
resultContainer.appendChild(listItem);
}
}
}
// 标签切换功能
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tab-content");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].classList.remove("active");
}
tablinks = document.getElementsByClassName("tab-btn");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].classList.remove("active");
}
document.getElementById(tabName).classList.add("active");
evt.currentTarget.classList.add("active");
}
// 显示通知
function showNotification(type, message) {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
let icon;
switch(type) {
case 'success':
icon = 'check-circle';
break;
case 'danger':
icon = 'exclamation-circle';
break;
case 'warning':
icon = 'exclamation-triangle';
break;
case 'info':
default:
icon = 'info-circle';
}
notification.innerHTML = `
<div class="notification-icon">
<i class="fas fa-${icon}"></i>
</div>
<div class="notification-content">${message}</div>
<button class="notification-close" onclick="this.parentElement.remove()">
<i class="fas fa-times"></i>
</button>
`;
document.body.appendChild(notification);
// 3秒后自动关闭
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateX(100%)';
notification.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// 获取规则类型文本描述
function getRuleTypeText(type) {
const types = {
'domain': '域名匹配',
'regex': '正则表达式',
'wildcard': '通配符',
'suffix': '后缀匹配',
'prefix': '前缀匹配'
};
return types[type] || type;
}
</script>
</body>
</html>