web异常待修复
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"blockedDomainsCount": {},
|
||||
"resolvedDomainsCount": {},
|
||||
"lastSaved": "2025-11-24T12:53:57.372276148+08:00"
|
||||
}
|
||||
130
data/stats.json
130
data/stats.json
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"stats": {
|
||||
"Queries": 10160,
|
||||
"Blocked": 984,
|
||||
"Allowed": 9642,
|
||||
"Errors": 19,
|
||||
"LastQuery": "2025-11-24T10:58:52.576912236+08:00"
|
||||
"Queries": 10356,
|
||||
"Blocked": 1160,
|
||||
"Allowed": 9740,
|
||||
"Errors": 29,
|
||||
"LastQuery": "2025-11-24T11:14:45.256891441+08:00"
|
||||
},
|
||||
"blockedDomains": {
|
||||
"acd.op.hicloud.com": {
|
||||
@@ -27,16 +27,31 @@
|
||||
"Count": 6,
|
||||
"LastSeen": "2025-11-23T19:06:10.691565251+08:00"
|
||||
},
|
||||
"adjus.com": {
|
||||
"Domain": "adjus.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T11:11:17.334769536+08:00"
|
||||
},
|
||||
"adjust.com": {
|
||||
"Domain": "adjust.com",
|
||||
"Count": 64,
|
||||
"LastSeen": "2025-11-24T11:10:54.322973064+08:00"
|
||||
},
|
||||
"adjust.com3": {
|
||||
"Domain": "adjust.com3",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T10:52:50.65657129+08:00"
|
||||
"LastSeen": "2025-11-24T11:11:02.160342476+08:00"
|
||||
},
|
||||
"adjust.net": {
|
||||
"Domain": "adjust.net",
|
||||
"Count": 8,
|
||||
"LastSeen": "2025-11-24T01:47:49.401607336+08:00"
|
||||
},
|
||||
"adjust1.cs": {
|
||||
"Domain": "adjust1.cs",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T11:11:12.680852315+08:00"
|
||||
},
|
||||
"agpicnsp-drcn.dbankcdn.com": {
|
||||
"Domain": "agpicnsp-drcn.dbankcdn.com",
|
||||
"Count": 6,
|
||||
@@ -44,8 +59,8 @@
|
||||
},
|
||||
"api-drcn.theme.dbankcloud.cn": {
|
||||
"Domain": "api-drcn.theme.dbankcloud.cn",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T09:38:52.070607203+08:00"
|
||||
"Count": 4,
|
||||
"LastSeen": "2025-11-24T11:09:56.504837268+08:00"
|
||||
},
|
||||
"api.weathercn.com": {
|
||||
"Domain": "api.weathercn.com",
|
||||
@@ -124,8 +139,8 @@
|
||||
},
|
||||
"datacollabo-drcn.platform.dbankcloud.cn": {
|
||||
"Domain": "datacollabo-drcn.platform.dbankcloud.cn",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T03:13:34.528874084+08:00"
|
||||
"Count": 4,
|
||||
"LastSeen": "2025-11-24T11:14:45.02112075+08:00"
|
||||
},
|
||||
"datasync-drcn.cloud.dbankcloud.cn": {
|
||||
"Domain": "datasync-drcn.cloud.dbankcloud.cn",
|
||||
@@ -134,8 +149,8 @@
|
||||
},
|
||||
"dnkeeper.platform.dbankcloud.cn": {
|
||||
"Domain": "dnkeeper.platform.dbankcloud.cn",
|
||||
"Count": 42,
|
||||
"LastSeen": "2025-11-24T10:14:41.401054875+08:00"
|
||||
"Count": 46,
|
||||
"LastSeen": "2025-11-24T11:14:43.701601859+08:00"
|
||||
},
|
||||
"event-drcn.push.dbankcloud.cn": {
|
||||
"Domain": "event-drcn.push.dbankcloud.cn",
|
||||
@@ -149,8 +164,8 @@
|
||||
},
|
||||
"events.op.hicloud.com": {
|
||||
"Domain": "events.op.hicloud.com",
|
||||
"Count": 10,
|
||||
"LastSeen": "2025-11-24T09:38:58.251391494+08:00"
|
||||
"Count": 12,
|
||||
"LastSeen": "2025-11-24T11:09:58.304001135+08:00"
|
||||
},
|
||||
"f00b1b869deb32d2ad60ba514bb876ea.b.hon.cc.cdnhwc8.com": {
|
||||
"Domain": "f00b1b869deb32d2ad60ba514bb876ea.b.hon.cc.cdnhwc8.com",
|
||||
@@ -159,8 +174,8 @@
|
||||
},
|
||||
"h5hosting.dbankcdn.com": {
|
||||
"Domain": "h5hosting.dbankcdn.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T03:13:34.79141854+08:00"
|
||||
"Count": 4,
|
||||
"LastSeen": "2025-11-24T11:14:45.257305828+08:00"
|
||||
},
|
||||
"hiboard-drcn.ai.dbankcloud.cn": {
|
||||
"Domain": "hiboard-drcn.ai.dbankcloud.cn",
|
||||
@@ -184,8 +199,8 @@
|
||||
},
|
||||
"hwid-drcn.platform.hicloud.com": {
|
||||
"Domain": "hwid-drcn.platform.hicloud.com",
|
||||
"Count": 10,
|
||||
"LastSeen": "2025-11-24T10:54:44.90022211+08:00"
|
||||
"Count": 12,
|
||||
"LastSeen": "2025-11-24T11:14:43.701391787+08:00"
|
||||
},
|
||||
"hwid.platform.hicloud.com": {
|
||||
"Domain": "hwid.platform.hicloud.com",
|
||||
@@ -204,8 +219,8 @@
|
||||
},
|
||||
"magazine-drcn.theme.dbankcloud.cn": {
|
||||
"Domain": "magazine-drcn.theme.dbankcloud.cn",
|
||||
"Count": 10,
|
||||
"LastSeen": "2025-11-24T09:38:52.08277503+08:00"
|
||||
"Count": 12,
|
||||
"LastSeen": "2025-11-24T11:09:56.50440237+08:00"
|
||||
},
|
||||
"metrics1-drcn.dt.dbankcloud.cn": {
|
||||
"Domain": "metrics1-drcn.dt.dbankcloud.cn",
|
||||
@@ -224,8 +239,8 @@
|
||||
},
|
||||
"rcm-cus-drcn.platform.dbankcloud.cn": {
|
||||
"Domain": "rcm-cus-drcn.platform.dbankcloud.cn",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T03:13:34.528406169+08:00"
|
||||
"Count": 4,
|
||||
"LastSeen": "2025-11-24T11:14:45.020111224+08:00"
|
||||
},
|
||||
"sdkserver-drcn.op.dbankcloud.cn": {
|
||||
"Domain": "sdkserver-drcn.op.dbankcloud.cn",
|
||||
@@ -274,8 +289,8 @@
|
||||
},
|
||||
"tsms-drcn.security.dbankcloud.cn": {
|
||||
"Domain": "tsms-drcn.security.dbankcloud.cn",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T03:13:34.528102875+08:00"
|
||||
"Count": 4,
|
||||
"LastSeen": "2025-11-24T11:14:45.019603773+08:00"
|
||||
},
|
||||
"userk-drcn.cloud.dbankcloud.cn": {
|
||||
"Domain": "userk-drcn.cloud.dbankcloud.cn",
|
||||
@@ -296,8 +311,8 @@
|
||||
},
|
||||
"abt-drcn.platform.dbankcloud.com": {
|
||||
"Domain": "abt-drcn.platform.dbankcloud.com",
|
||||
"Count": 4,
|
||||
"LastSeen": "2025-11-24T08:06:49.435747722+08:00"
|
||||
"Count": 5,
|
||||
"LastSeen": "2025-11-24T11:14:45.021921328+08:00"
|
||||
},
|
||||
"acd.op.hicloud.com": {
|
||||
"Domain": "acd.op.hicloud.com",
|
||||
@@ -314,11 +329,41 @@
|
||||
"Count": 16,
|
||||
"LastSeen": "2025-11-24T00:32:55.878106815+08:00"
|
||||
},
|
||||
"adjus.com.amazehome.xyz": {
|
||||
"Domain": "adjus.com.amazehome.xyz",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T11:11:17.263847943+08:00"
|
||||
},
|
||||
"adjust.amazehome.xyz": {
|
||||
"Domain": "adjust.amazehome.xyz",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T11:11:07.080717636+08:00"
|
||||
},
|
||||
"adjust.com.amazehome.xyz": {
|
||||
"Domain": "adjust.com.amazehome.xyz",
|
||||
"Count": 56,
|
||||
"LastSeen": "2025-11-24T11:10:54.256320033+08:00"
|
||||
},
|
||||
"adjust.com3.amazehome.xyz": {
|
||||
"Domain": "adjust.com3.amazehome.xyz",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T11:11:02.068019996+08:00"
|
||||
},
|
||||
"adjust.net.amazehome.xyz": {
|
||||
"Domain": "adjust.net.amazehome.xyz",
|
||||
"Count": 8,
|
||||
"LastSeen": "2025-11-24T01:47:49.399009577+08:00"
|
||||
},
|
||||
"adjust1.amazehome.xyz": {
|
||||
"Domain": "adjust1.amazehome.xyz",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T11:11:08.456696388+08:00"
|
||||
},
|
||||
"adjust1.cs.amazehome.xyz": {
|
||||
"Domain": "adjust1.cs.amazehome.xyz",
|
||||
"Count": 1,
|
||||
"LastSeen": "2025-11-24T11:11:12.61355646+08:00"
|
||||
},
|
||||
"aeventlog.beacon.qq.com": {
|
||||
"Domain": "aeventlog.beacon.qq.com",
|
||||
"Count": 24,
|
||||
@@ -341,8 +386,8 @@
|
||||
},
|
||||
"apd-pcdnwxstat.teg.tencent-cloud.net": {
|
||||
"Domain": "apd-pcdnwxstat.teg.tencent-cloud.net",
|
||||
"Count": 33,
|
||||
"LastSeen": "2025-11-24T10:36:53.271030952+08:00"
|
||||
"Count": 35,
|
||||
"LastSeen": "2025-11-24T11:08:27.539325599+08:00"
|
||||
},
|
||||
"api-drcn.theme.dbankcloud.cn": {
|
||||
"Domain": "api-drcn.theme.dbankcloud.cn",
|
||||
@@ -471,8 +516,8 @@
|
||||
},
|
||||
"contentcenter-drcn.dbankcdn.cn": {
|
||||
"Domain": "contentcenter-drcn.dbankcdn.cn",
|
||||
"Count": 14,
|
||||
"LastSeen": "2025-11-24T10:01:25.221069297+08:00"
|
||||
"Count": 16,
|
||||
"LastSeen": "2025-11-24T11:14:45.021702297+08:00"
|
||||
},
|
||||
"contentcenter-drcn.dbankcdn.com": {
|
||||
"Domain": "contentcenter-drcn.dbankcdn.com",
|
||||
@@ -646,8 +691,8 @@
|
||||
},
|
||||
"nearby-find-api-drcn.hms.dbankcloud.com": {
|
||||
"Domain": "nearby-find-api-drcn.hms.dbankcloud.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T10:14:41.380422058+08:00"
|
||||
"Count": 3,
|
||||
"LastSeen": "2025-11-24T11:14:43.679722257+08:00"
|
||||
},
|
||||
"nsp-hicloud-cloudbackupnorth9-p06-drcn.obs.dualstack.cn-north-9.myhuaweicloud.cn": {
|
||||
"Domain": "nsp-hicloud-cloudbackupnorth9-p06-drcn.obs.dualstack.cn-north-9.myhuaweicloud.cn",
|
||||
@@ -661,13 +706,13 @@
|
||||
},
|
||||
"oauth-login-drcn.platform.dbankcloud.com": {
|
||||
"Domain": "oauth-login-drcn.platform.dbankcloud.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T10:14:41.381115773+08:00"
|
||||
"Count": 3,
|
||||
"LastSeen": "2025-11-24T11:14:43.683804604+08:00"
|
||||
},
|
||||
"openlocation-drcn.platform.dbankcloud.com": {
|
||||
"Domain": "openlocation-drcn.platform.dbankcloud.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-24T10:14:41.381175213+08:00"
|
||||
"Count": 3,
|
||||
"LastSeen": "2025-11-24T11:14:43.683651239+08:00"
|
||||
},
|
||||
"paydns.wechatpay.cn": {
|
||||
"Domain": "paydns.wechatpay.cn",
|
||||
@@ -2661,13 +2706,13 @@
|
||||
},
|
||||
"so.com": {
|
||||
"Domain": "so.com",
|
||||
"Count": 15,
|
||||
"LastSeen": "2025-11-24T10:54:53.278047903+08:00"
|
||||
"Count": 29,
|
||||
"LastSeen": "2025-11-24T11:09:22.651757829+08:00"
|
||||
},
|
||||
"so.com.amazehome.xyz": {
|
||||
"Domain": "so.com.amazehome.xyz",
|
||||
"Count": 5,
|
||||
"LastSeen": "2025-11-24T10:54:53.031986202+08:00"
|
||||
"Count": 16,
|
||||
"LastSeen": "2025-11-24T11:09:22.522520185+08:00"
|
||||
},
|
||||
"so.qss-lb.com": {
|
||||
"Domain": "so.qss-lb.com",
|
||||
@@ -2744,7 +2789,8 @@
|
||||
"2025-11-24-07": 40,
|
||||
"2025-11-24-08": 63,
|
||||
"2025-11-24-09": 20,
|
||||
"2025-11-24-10": 132
|
||||
"2025-11-24-10": 132,
|
||||
"2025-11-24-11": 88
|
||||
},
|
||||
"lastSaved": "2025-11-24T11:00:47.366358059+08:00"
|
||||
"lastSaved": "2025-11-24T11:18:29.299605227+08:00"
|
||||
}
|
||||
1751
dns-server.log
1751
dns-server.log
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,72 @@
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
/* 统计数据更新动画 */
|
||||
.stat-value.update {
|
||||
position: relative;
|
||||
animation: stat-pulse 1s ease;
|
||||
}
|
||||
|
||||
@keyframes stat-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.4);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 10px rgba(76, 175, 80, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格行淡入淡出动画 */
|
||||
table tr.fade-in {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
table tr.fade-out {
|
||||
animation: fadeOut 0.3s ease-in;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 通知样式 */
|
||||
.notification {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 15px 20px;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
// 初始化仪表盘面板
|
||||
function initDashboardPanel() {
|
||||
// 初始化小型图表
|
||||
if (typeof initMiniCharts === 'function') {
|
||||
initMiniCharts();
|
||||
}
|
||||
// 加载统计数据
|
||||
loadDashboardData();
|
||||
// 启动实时更新
|
||||
@@ -29,19 +33,19 @@ function updateStatCards() {
|
||||
if (data && data.dns) {
|
||||
// 屏蔽请求
|
||||
const blockedCount = data.dns.Blocked || data.dns.blocked || 0;
|
||||
updateStatCard('blocked-count', blockedCount);
|
||||
smoothUpdateStatCard('blocked-count', blockedCount);
|
||||
|
||||
// 允许请求
|
||||
const allowedCount = data.dns.Allowed || data.dns.allowed || 0;
|
||||
updateStatCard('allowed-count', allowedCount);
|
||||
smoothUpdateStatCard('allowed-count', allowedCount);
|
||||
|
||||
// 错误请求
|
||||
const errorCount = data.dns.Errors || data.dns.errors || 0;
|
||||
updateStatCard('error-count', errorCount);
|
||||
smoothUpdateStatCard('error-count', errorCount);
|
||||
|
||||
// 总请求数
|
||||
const totalCount = blockedCount + allowedCount + errorCount;
|
||||
updateStatCard('total-queries', totalCount);
|
||||
smoothUpdateStatCard('total-queries', totalCount);
|
||||
|
||||
// 更新数据历史记录和小型图表
|
||||
if (typeof updateDataHistory === 'function') {
|
||||
@@ -56,15 +60,14 @@ function updateStatCards() {
|
||||
}
|
||||
} else {
|
||||
// 处理其他可能的数据格式
|
||||
// 修复语法错误,使用传统的对象属性访问方式
|
||||
const blockedValue = data && (data.Blocked !== undefined ? data.Blocked : (data.blocked !== undefined ? data.blocked : 0));
|
||||
const allowedValue = data && (data.Allowed !== undefined ? data.Allowed : (data.allowed !== undefined ? data.allowed : 0));
|
||||
const errorValue = data && (data.Errors !== undefined ? data.Errors : (data.errors !== undefined ? data.errors : 0));
|
||||
updateStatCard('blocked-count', blockedValue);
|
||||
updateStatCard('allowed-count', allowedValue);
|
||||
updateStatCard('error-count', errorValue);
|
||||
smoothUpdateStatCard('blocked-count', blockedValue);
|
||||
smoothUpdateStatCard('allowed-count', allowedValue);
|
||||
smoothUpdateStatCard('error-count', errorValue);
|
||||
const totalCount = blockedValue + allowedValue + errorValue;
|
||||
updateStatCard('total-queries', totalCount);
|
||||
smoothUpdateStatCard('total-queries', totalCount);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -75,13 +78,32 @@ function updateStatCards() {
|
||||
apiRequest('/shield')
|
||||
.then(data => {
|
||||
let rulesCount = 0;
|
||||
|
||||
// 增强的数据格式处理,确保能正确处理各种返回格式
|
||||
if (Array.isArray(data)) {
|
||||
rulesCount = data.length;
|
||||
} else if (data && data.rules && Array.isArray(data.rules)) {
|
||||
rulesCount = data.rules.length;
|
||||
} else if (data && data.domainRules) {
|
||||
// 处理可能的规则分类格式
|
||||
let domainRulesCount = 0;
|
||||
let regexRulesCount = 0;
|
||||
|
||||
if (Array.isArray(data.domainRules)) {
|
||||
domainRulesCount = data.domainRules.length;
|
||||
} else if (typeof data.domainRules === 'object') {
|
||||
domainRulesCount = Object.keys(data.domainRules).length;
|
||||
}
|
||||
|
||||
updateStatCard('rules-count', rulesCount);
|
||||
if (data.regexRules && Array.isArray(data.regexRules)) {
|
||||
regexRulesCount = data.regexRules.length;
|
||||
}
|
||||
|
||||
rulesCount = domainRulesCount + regexRulesCount;
|
||||
}
|
||||
|
||||
// 确保至少显示0而不是--
|
||||
smoothUpdateStatCard('rules-count', rulesCount);
|
||||
|
||||
// 更新数据历史记录和小型图表
|
||||
if (typeof updateDataHistory === 'function') {
|
||||
@@ -94,6 +116,41 @@ function updateStatCards() {
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取规则数失败:', error);
|
||||
// 即使出错也要设置为0,避免显示--
|
||||
smoothUpdateStatCard('rules-count', 0);
|
||||
});
|
||||
|
||||
// 获取Hosts条目数量
|
||||
apiRequest('/shield/hosts')
|
||||
.then(data => {
|
||||
let hostsCount = 0;
|
||||
|
||||
// 处理各种可能的数据格式
|
||||
if (Array.isArray(data)) {
|
||||
hostsCount = data.length;
|
||||
} else if (data && data.hosts && Array.isArray(data.hosts)) {
|
||||
hostsCount = data.hosts.length;
|
||||
} else if (data && typeof data === 'object' && data !== null) {
|
||||
// 如果是对象格式,计算键的数量
|
||||
hostsCount = Object.keys(data).length;
|
||||
}
|
||||
|
||||
// 确保至少显示0而不是--
|
||||
smoothUpdateStatCard('hosts-count', hostsCount);
|
||||
|
||||
// 更新数据历史记录和小型图表
|
||||
if (typeof updateDataHistory === 'function') {
|
||||
updateDataHistory('hosts', hostsCount);
|
||||
}
|
||||
|
||||
if (typeof updateMiniChart === 'function' && typeof dataHistory !== 'undefined') {
|
||||
updateMiniChart('hosts-chart', dataHistory.hosts);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取Hosts数量失败:', error);
|
||||
// 即使出错也要设置为0,避免显示--
|
||||
smoothUpdateStatCard('hosts-count', 0);
|
||||
});
|
||||
|
||||
// 获取Hosts条目数
|
||||
@@ -106,7 +163,7 @@ function updateStatCards() {
|
||||
hostsCount = data.hosts.length;
|
||||
}
|
||||
|
||||
updateStatCard('hosts-count', hostsCount);
|
||||
smoothUpdateStatCard('hosts-count', hostsCount);
|
||||
|
||||
// 更新数据历史记录和小型图表
|
||||
if (typeof updateDataHistory === 'function') {
|
||||
@@ -122,6 +179,7 @@ function updateStatCards() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 更新单个统计卡片
|
||||
function updateStatCard(elementId, value) {
|
||||
const element = document.getElementById(elementId);
|
||||
@@ -139,6 +197,61 @@ function updateStatCard(elementId, value) {
|
||||
}
|
||||
}
|
||||
|
||||
// 平滑更新统计卡片(数字递增动画)
|
||||
function smoothUpdateStatCard(elementId, newValue) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (!element) return;
|
||||
|
||||
// 获取旧值
|
||||
const oldValue = previousStats[elementId] || 0;
|
||||
|
||||
// 如果值相同,不更新
|
||||
if (newValue === oldValue) return;
|
||||
|
||||
// 如果是初始值,直接更新
|
||||
if (oldValue === 0 || oldValue === '--') {
|
||||
updateStatCard(elementId, newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置动画持续时间
|
||||
const duration = 500; // 500ms
|
||||
const startTime = performance.now();
|
||||
|
||||
// 动画函数
|
||||
function animate(currentTime) {
|
||||
const elapsedTime = currentTime - startTime;
|
||||
const progress = Math.min(elapsedTime / duration, 1);
|
||||
|
||||
// 使用缓动函数
|
||||
const easeOutQuad = 1 - (1 - progress) * (1 - progress);
|
||||
|
||||
// 计算当前值
|
||||
const currentValue = Math.floor(oldValue + (newValue - oldValue) * easeOutQuad);
|
||||
|
||||
// 更新显示
|
||||
element.textContent = formatNumber(currentValue);
|
||||
|
||||
// 继续动画
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animate);
|
||||
} else {
|
||||
// 动画完成,设置最终值
|
||||
element.textContent = formatNumber(newValue);
|
||||
// 添加光晕效果
|
||||
element.classList.add('update');
|
||||
setTimeout(() => {
|
||||
element.classList.remove('update');
|
||||
}, 1000);
|
||||
// 更新记录
|
||||
previousStats[elementId] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 开始动画
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// 加载24小时统计数据
|
||||
function loadHourlyStats() {
|
||||
apiRequest('/hourly-stats')
|
||||
@@ -524,7 +637,6 @@ function renderRequestsPieChart(labels, data) {
|
||||
// 加载最常屏蔽的域名
|
||||
function loadTopBlockedDomains() {
|
||||
// 首先获取表格元素并显示加载状态
|
||||
// 修复语法错误,使用传统的DOM访问方式
|
||||
const topBlockedTable = document.getElementById('top-blocked-table');
|
||||
const tbody = topBlockedTable ? topBlockedTable.querySelector('tbody') : null;
|
||||
if (tbody) {
|
||||
@@ -537,12 +649,9 @@ function loadTopBlockedDomains() {
|
||||
// 处理多种可能的数据格式,特别优化对用户提供格式的支持
|
||||
let processedData = [];
|
||||
|
||||
console.log('最常屏蔽域名API返回数据:', data);
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
// 数组格式:直接使用,并过滤出有效的域名数据
|
||||
processedData = data.filter(item => item && (item.domain || item.name || item.Domain || item.Name) && (item.count !== undefined || item.Count !== undefined || item.hits !== undefined || item.Hits !== undefined));
|
||||
console.log('处理后的域名数据:', processedData);
|
||||
} else if (data && data.domains && Array.isArray(data.domains)) {
|
||||
// 嵌套在domains属性中
|
||||
processedData = data.domains;
|
||||
@@ -554,7 +663,7 @@ function loadTopBlockedDomains() {
|
||||
}));
|
||||
}
|
||||
|
||||
renderTopBlockedDomains(processedData);
|
||||
smoothRenderTable('#top-blocked-table', processedData, renderDomainRow);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取最常屏蔽域名失败:', error);
|
||||
@@ -570,48 +679,6 @@ function loadTopBlockedDomains() {
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染最常屏蔽的域名表格
|
||||
function renderTopBlockedDomains(domains) {
|
||||
// 修复语法错误,使用传统的DOM访问方式
|
||||
const topBlockedTable = document.getElementById('top-blocked-table');
|
||||
const tbody = topBlockedTable ? topBlockedTable.querySelector('tbody') : null;
|
||||
if (!tbody) return;
|
||||
|
||||
console.log('准备渲染的域名数据:', domains);
|
||||
|
||||
if (!domains || domains.length === 0) {
|
||||
showEmpty(tbody, '暂无屏蔽记录');
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
domains.forEach((domain, index) => {
|
||||
if (!domain) return;
|
||||
|
||||
// 支持不同的字段名和格式,特别针对用户提供的数据格式优化
|
||||
const domainName = domain.domain || domain.name || domain.Domain || domain.Name || '未知域名';
|
||||
const count = domain.count !== undefined ? domain.count :
|
||||
(domain.Count !== undefined ? domain.Count :
|
||||
(domain.hits !== undefined ? domain.hits :
|
||||
(domain.Hits !== undefined ? domain.Hits : 0)));
|
||||
|
||||
console.log(`渲染域名 ${index + 1}:`, {domainName, count});
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${domainName}</td>
|
||||
<td>${formatNumber(count)}</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
// 初始化表格排序
|
||||
if (typeof initTableSort === 'function') {
|
||||
initTableSort('top-blocked-table');
|
||||
}
|
||||
}
|
||||
|
||||
// 加载最常解析的域名
|
||||
function loadTopResolvedDomains() {
|
||||
apiRequest('/top-resolved')
|
||||
@@ -633,7 +700,7 @@ function loadTopResolvedDomains() {
|
||||
}));
|
||||
}
|
||||
|
||||
renderTopResolvedDomains(processedData);
|
||||
smoothRenderTable('#top-resolved-table', processedData, renderDomainRow);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取最常解析域名失败:', error);
|
||||
@@ -650,31 +717,177 @@ function loadTopResolvedDomains() {
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染最常解析的域名表格
|
||||
function renderTopResolvedDomains(domains) {
|
||||
const tbody = document.getElementById('top-resolved-table').querySelector('tbody');
|
||||
// 渲染域名行
|
||||
function renderDomainRow(item, index) {
|
||||
if (!item) return null;
|
||||
|
||||
// 支持不同的字段名和格式
|
||||
const domainName = item.domain || item.name || item.Domain || item.Name || '未知域名';
|
||||
const count = item.count !== undefined ? item.count :
|
||||
(item.Count !== undefined ? item.Count :
|
||||
(item.hits !== undefined ? item.hits :
|
||||
(item.Hits !== undefined ? item.Hits : 0)));
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.className = 'fade-in'; // 添加淡入动画类
|
||||
row.dataset.domain = domainName;
|
||||
row.dataset.count = count;
|
||||
|
||||
row.innerHTML = `
|
||||
<td>${domainName}</td>
|
||||
<td class="count-cell">${formatNumber(count)}</td>
|
||||
`;
|
||||
|
||||
// 设置动画延迟,创建级联效果
|
||||
row.style.animationDelay = `${index * 50}ms`;
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
// 平滑渲染表格数据
|
||||
function smoothRenderTable(tableId, newData, rowRenderer) {
|
||||
const table = document.getElementById(tableId);
|
||||
const tbody = table ? table.querySelector('tbody') : null;
|
||||
if (!tbody) return;
|
||||
|
||||
if (!domains || domains.length === 0) {
|
||||
showEmpty(tbody, '暂无解析记录');
|
||||
if (!newData || newData.length === 0) {
|
||||
showEmpty(tbody, '暂无数据记录');
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = '';
|
||||
// 创建映射以提高查找效率
|
||||
const oldRows = Array.from(tbody.querySelectorAll('tr'));
|
||||
const rowMap = new Map();
|
||||
|
||||
domains.forEach((domain, index) => {
|
||||
// 支持不同的字段名和格式
|
||||
const domainName = domain.domain || domain.name || domain.Domain || domain.Name || '未知域名';
|
||||
const count = domain.count || domain.Count || domain.hits || domain.Hits || 0;
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${domainName}</td>
|
||||
<td>${formatNumber(count)}</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
oldRows.forEach(row => {
|
||||
if (!row.querySelector('td:first-child')) return;
|
||||
const key = row.dataset.domain || row.querySelector('td:first-child').textContent;
|
||||
rowMap.set(key, row);
|
||||
});
|
||||
|
||||
// 初始化表格排序
|
||||
initTableSort('top-resolved-table');
|
||||
// 准备新的数据行
|
||||
const newRows = [];
|
||||
const updatedRows = new Set();
|
||||
|
||||
// 处理每一条新数据
|
||||
newData.forEach((item, index) => {
|
||||
const key = item.domain || item.name || item.Domain || item.Name || '未知域名';
|
||||
|
||||
if (rowMap.has(key)) {
|
||||
// 数据项已存在,更新它
|
||||
const existingRow = rowMap.get(key);
|
||||
const oldCount = parseInt(existingRow.dataset.count) || 0;
|
||||
const count = item.count !== undefined ? item.count :
|
||||
(item.Count !== undefined ? item.Count :
|
||||
(item.hits !== undefined ? item.hits :
|
||||
(item.Hits !== undefined ? item.Hits : 0)));
|
||||
|
||||
// 更新数据属性
|
||||
existingRow.dataset.count = count;
|
||||
|
||||
// 如果计数变化,应用平滑更新
|
||||
if (oldCount !== count) {
|
||||
const countCell = existingRow.querySelector('.count-cell');
|
||||
if (countCell) {
|
||||
smoothUpdateNumber(countCell, oldCount, count);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新位置
|
||||
existingRow.style.animationDelay = `${index * 50}ms`;
|
||||
newRows.push(existingRow);
|
||||
updatedRows.add(key);
|
||||
} else {
|
||||
// 新数据项,创建新行
|
||||
const newRow = rowRenderer(item, index);
|
||||
if (newRow) {
|
||||
// 先设置透明度为0,避免在错误位置闪烁
|
||||
newRow.style.opacity = '0';
|
||||
newRows.push(newRow);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 移除不再存在的数据行
|
||||
oldRows.forEach(row => {
|
||||
if (!row.querySelector('td:first-child')) return;
|
||||
const key = row.dataset.domain || row.querySelector('td:first-child').textContent;
|
||||
if (!updatedRows.has(key)) {
|
||||
// 添加淡出动画
|
||||
row.classList.add('fade-out');
|
||||
setTimeout(() => {
|
||||
if (row.parentNode === tbody) {
|
||||
tbody.removeChild(row);
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
// 批量更新表格内容,减少重排
|
||||
requestAnimationFrame(() => {
|
||||
// 保留未移除的行并按新顺序插入
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
newRows.forEach(row => {
|
||||
// 如果是新行,添加到文档片段
|
||||
if (!row.parentNode || row.parentNode !== tbody) {
|
||||
fragment.appendChild(row);
|
||||
}
|
||||
// 如果是已有行,移除它以便按新顺序重新插入
|
||||
else if (tbody.contains(row)) {
|
||||
tbody.removeChild(row);
|
||||
fragment.appendChild(row);
|
||||
}
|
||||
});
|
||||
|
||||
// 将文档片段添加到表格
|
||||
tbody.appendChild(fragment);
|
||||
|
||||
// 触发动画
|
||||
setTimeout(() => {
|
||||
newRows.forEach(row => {
|
||||
row.style.opacity = '1';
|
||||
});
|
||||
}, 10);
|
||||
|
||||
// 初始化表格排序
|
||||
if (typeof initTableSort === 'function') {
|
||||
initTableSort(tableId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 平滑更新数字
|
||||
function smoothUpdateNumber(element, oldValue, newValue) {
|
||||
// 如果值相同,不更新
|
||||
if (oldValue === newValue) return;
|
||||
|
||||
// 设置动画持续时间
|
||||
const duration = 500;
|
||||
const startTime = performance.now();
|
||||
|
||||
function animate(currentTime) {
|
||||
const elapsedTime = currentTime - startTime;
|
||||
const progress = Math.min(elapsedTime / duration, 1);
|
||||
|
||||
// 使用缓动函数
|
||||
const easeOutQuad = 1 - (1 - progress) * (1 - progress);
|
||||
|
||||
// 计算当前值
|
||||
const currentValue = Math.floor(oldValue + (newValue - oldValue) * easeOutQuad);
|
||||
|
||||
// 更新显示
|
||||
element.textContent = formatNumber(currentValue);
|
||||
|
||||
// 继续动画
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animate);
|
||||
} else {
|
||||
// 动画完成
|
||||
element.textContent = formatNumber(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 开始动画
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
Reference in New Issue
Block a user