From babfdee02bcf49b13ae51ae69430a879e24f02c9 Mon Sep 17 00:00:00 2001 From: lxh2875931338 Date: Tue, 28 Jul 2026 19:30:26 +0800 Subject: [PATCH] =?UTF-8?q?2.2=E6=8A=80=E6=9C=AF=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E7=89=88=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=B7=B2=E5=86=99?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/app.js | 190 +++++++++++++++++++++++----------------------- static/index.html | 87 ++++++++++----------- 2 files changed, 137 insertions(+), 140 deletions(-) diff --git a/static/app.js b/static/app.js index 6a04455..d64c224 100644 --- a/static/app.js +++ b/static/app.js @@ -171,7 +171,7 @@ function filterProxies(list, keyword) { (p) => p.name.toLowerCase().includes(kw) || p.localIP.includes(kw) || - String(p.remotePort).includes(kw) + String(p.remotePort).includes(kw), ); } @@ -197,7 +197,11 @@ async function fetchLogsApi() { try { const data = await apiFetch("/frpc/log"); if (data.code === 0) { - return { lines: data.data.lines || [], total: data.data.total || 0, error: data.data.error || "" }; + return { + lines: data.data.lines || [], + total: data.data.total || 0, + error: data.data.error || "", + }; } return { lines: [], total: 0, error: "加载失败" }; } catch (e) { @@ -205,93 +209,6 @@ async function fetchLogsApi() { } } -// ---- 新增:Ping 延迟检测 ---- -const pingLatency = ref(null); // null 表示未接入/失败 -const pingInterval = ref(null); -const PING_INTERVAL_MS = 30000; // 30 秒轮询一次 -const PING_TIMEOUT_MS = 5000; // 5 秒超时 - -// 计算 ping 状态 class -const pingStatusClass = computed(() => { - if (pingLatency.value === null) return 'ping-fail'; - if (pingLatency.value < 1000) return 'ping-good'; - if (pingLatency.value < 5000) return 'ping-slow'; - return 'ping-fail'; -}); - -// 根据状态返回对应 SVG 图标(内联) -const pingIcon = computed(() => { - if (pingLatency.value === null) { - // 未接入/失败 —— 断开图标 - return ` - - - - - - `; - } - if (pingLatency.value < 1000) { - // 延迟好 —— 实心信号图标(青色/绿色) - return ` - - - `; - } - // 延迟一般(1000-5000ms)—— 空心信号图标(黄色/橙色) - return ` - - - `; -}); - -// 执行 Ping 检测 -async function doPing() { - if (!frpcRunning.value) { - pingLatency.value = null; - return; - } - const addr = globalConfig.serverAddr || 'frp.example.com'; - const start = performance.now(); - try { - const res = await fetch(`/api/ping?target=${encodeURIComponent(addr)}`, { - signal: AbortSignal.timeout(PING_TIMEOUT_MS), - }); - if (!res.ok) throw new Error('Ping failed'); - const end = performance.now(); - pingLatency.value = Math.round(end - start); - } catch { - pingLatency.value = null; - } -} - -// 启动 Ping 轮询 -function startPingPolling() { - if (pingInterval.value) return; - doPing(); - pingInterval.value = setInterval(doPing, PING_INTERVAL_MS); -} - -function stopPingPolling() { - if (pingInterval.value) { - clearInterval(pingInterval.value); - pingInterval.value = null; - } -} - -// 在登录后启动,退出时停止 -// 在 loadAllData 成功后调用 startPingPolling() -// 在 doLogout 中调用 stopPingPolling() -// 监听 frpcRunning 变化:如果为 false,清空 pingLatency -watch(frpcRunning, (running) => { - if (!running) { - pingLatency.value = null; - stopPingPolling(); - } else if (loggedIn.value) { - startPingPolling(); - } -}); - // ============================================================ // 7. UI State // ============================================================ @@ -381,6 +298,80 @@ const app = createApp({ let logTimer = null; let logFetching = false; + // ---- Ping 延迟检测(移到 setup 内部) ---- + const pingLatency = ref(null); + let pingTimer = null; + const PING_INTERVAL_MS = 30000; + const PING_TIMEOUT_MS = 5000; + + const pingStatusClass = computed(() => { + if (pingLatency.value === null) return "ping-fail"; + if (pingLatency.value < 1000) return "ping-good"; + if (pingLatency.value < 5000) return "ping-slow"; + return "ping-fail"; + }); + + const pingIcon = computed(() => { + if (pingLatency.value === null) { + // 未接入/失败 —— 断开图标 + return ` + + + + + + `; + } + if (pingLatency.value < 1000) { + // 延迟好 —— 实心信号图标(青色/绿色) + return ` + + + `; + } + // 延迟一般(1000-5000ms)—— 空心信号图标(黄色/橙色) + return ` + + + `; + }); + + async function doPing() { + // 如果 frpc 未运行,直接显示 --ms + if (!frpcRunning.value) { + pingLatency.value = null; + return; + } + const addr = globalConfig.serverAddr || "frp.example.com"; + const start = performance.now(); + try { + const res = await fetch( + `/api/ping?target=${encodeURIComponent(addr)}`, + { + signal: AbortSignal.timeout(PING_TIMEOUT_MS), + }, + ); + if (!res.ok) throw new Error("Ping failed"); + const end = performance.now(); + pingLatency.value = Math.round(end - start); + } catch { + pingLatency.value = null; + } + } + + function startPingPolling() { + if (pingTimer) return; + doPing(); + pingTimer = setInterval(doPing, PING_INTERVAL_MS); + } + + function stopPingPolling() { + if (pingTimer) { + clearInterval(pingTimer); + pingTimer = null; + } + } + // ---- 认证过期处理 ---- const handleAuthExpired = () => { doLogout(); @@ -388,7 +379,6 @@ const app = createApp({ // ---- 初始化 ---- onMounted(() => { - // 监听认证过期事件 window.addEventListener("auth:expired", handleAuthExpired); const saved = loadAuthState(); @@ -415,6 +405,15 @@ const app = createApp({ } }); + watch(frpcRunning, (running) => { + if (!running) { + stopPingPolling(); + pingLatency.value = null; // 显示 --ms + } else if (loggedIn.value) { + startPingPolling(); + } + }); + async function checkUsers() { try { const res = await fetch("/api/check/users"); @@ -454,7 +453,6 @@ const app = createApp({ logFetching = true; try { const result = await fetchLogsApi(); - // ---- 如果 API 返回 401,停止轮询(事件已触发登出) ---- if (result.code === 401) { logFetching = false; return; @@ -586,6 +584,7 @@ const app = createApp({ const doLogout = () => { if (expireTimer) clearInterval(expireTimer); stopLogPolling(); + stopPingPolling(); contentVisible.value = false; loggedIn.value = false; token.value = ""; @@ -745,7 +744,7 @@ const app = createApp({ // ---- 计算属性 ---- const filteredProxies = computed(() => - filterProxies(proxies.value, searchKeyword.value) + filterProxies(proxies.value, searchKeyword.value), ); const showDefaultTip = computed(() => { @@ -810,8 +809,13 @@ const app = createApp({ scrollLogToBottom, showToken, + + // ---- 新增 Ping 相关导出 ---- + pingLatency, + pingStatusClass, + pingIcon, }; }, }); -app.mount("#app"); \ No newline at end of file +app.mount("#app"); diff --git a/static/index.html b/static/index.html index 63de43e..5ced711 100644 --- a/static/index.html +++ b/static/index.html @@ -87,6 +87,7 @@
+
@@ -96,12 +97,10 @@ {{ frpcRunning ? 'frpc 运行中' : 'frpc 已停止' }} - - + - {{ pingLatency !== null ? pingLatency + 'ms' : '--ms' - }} + {{ pingLatency !== null ? pingLatency + 'ms' : '--ms' }}
@@ -168,18 +167,16 @@
-

全局配置信息

管理 frpc 连接参数与传输设置

-
首次使用请修改「服务器地址」和「认证令牌」为您的真实 frps 配置
- +
服务器连接 @@ -206,13 +203,12 @@
- +
传输配置
-
@@ -237,7 +233,6 @@
-
@@ -253,7 +248,7 @@
- +
日志配置 @@ -276,14 +271,11 @@
-
-
-
共 {{ logTotal }} 行 @@ -299,7 +291,6 @@
-
暂无日志,frpc 尚未产生输出 @@ -309,7 +300,6 @@
-
-
-
-
- - -
-
-

{{ dialogMode === 'add' ? '新增隧道' : '编辑隧道' }}

-
-
-
- - -
-
-
-
-
+
+ + + +
+
+

{{ dialogMode === 'add' ? '新增隧道' : '编辑隧道' }}

+
+
+
+ + +
+
+
+
+
+
+
+
+ + +
-
- - -
-
-
- + + +
+
- + +