隧道 {tunnelStats.enabled}/{tunnelStats.total}
- {lastQualityUpdate && (
+ {lastQualityUpdate ? (
-
- 每秒探测 · 更新于 {lastQualityUpdate}
+ {monitorTunnelQualityEnabled ? (
+ <>
+
+ 每秒探测 · 更新于 {lastQualityUpdate}
+ >
+ ) : (
+ <>
+
+ 实时质量检测已关闭 · 最近更新于 {lastQualityUpdate}
+ >
+ )}
- )}
+ ) : !monitorTunnelQualityEnabled ? (
+
+
+ 实时质量检测已关闭
+
+ ) : null}
@@ -870,11 +963,17 @@ export function TunnelMonitorView({ viewMode = "grid" }: TunnelMonitorViewProps)
{quality?.timestamp ? (
-
+ {monitorTunnelQualityEnabled ? (
+
+ ) : (
+
+ )}
{new Date(quality.timestamp).toLocaleTimeString("zh-CN")}
) : (
- -
+
+ {monitorTunnelQualityEnabled ? "-" : "实时检测已关闭"}
+
)}
diff --git a/vite-frontend/src/pages/settings.tsx b/vite-frontend/src/pages/settings.tsx
index 55e3217..0f4d1ce 100644
--- a/vite-frontend/src/pages/settings.tsx
+++ b/vite-frontend/src/pages/settings.tsx
@@ -31,6 +31,12 @@ interface PanelAddress {
}
const FORWARD_COMPACT_MODE_CONFIG_KEY = "forward_compact_mode";
+const MONITOR_TUNNEL_QUALITY_ENABLED_CONFIG_KEY = "monitor_tunnel_quality_enabled";
+const MONITOR_TUNNEL_QUALITY_ENABLED_EVENT =
+ "monitorTunnelQualityEnabledChanged";
+
+const parseBooleanConfig = (value: unknown, defaultValue: boolean) =>
+ typeof value === "string" ? value === "true" : defaultValue;
export const SettingsPage = () => {
const navigate = useNavigate();
@@ -43,6 +49,10 @@ export const SettingsPage = () => {
const [forwardCompactMode, setForwardCompactMode] = useState(false);
const [forwardCompactModeSaving, setForwardCompactModeSaving] =
useState(false);
+ const [monitorTunnelQualityEnabled, setMonitorTunnelQualityEnabled] =
+ useState(true);
+ const [monitorTunnelQualitySaving, setMonitorTunnelQualitySaving] =
+ useState(false);
const admin = isAdmin();
@@ -50,9 +60,16 @@ export const SettingsPage = () => {
setPanelAddresses(newAddress);
};
+ useEffect(() => {
+ (window as any).setPanelAddresses = setPanelAddressesFunc;
+
+ return () => {
+ delete (window as any).setPanelAddresses;
+ };
+ }, []);
+
// 加载面板地址列表
const loadPanelAddresses = async () => {
- (window as any).setPanelAddresses = setPanelAddressesFunc;
getPanelAddresses();
};
@@ -72,7 +89,6 @@ export const SettingsPage = () => {
return;
}
- (window as any).setPanelAddresses = setPanelAddressesFunc;
savePanelAddress(newName.trim(), newAddress.trim());
setNewName("");
setNewAddress("");
@@ -81,14 +97,12 @@ export const SettingsPage = () => {
// 设置当前面板地址
const setCurrentPanel = async (name: string) => {
- (window as any).setPanelAddresses = setPanelAddressesFunc;
setCurrentPanelAddress(name);
reinitializeBaseURL();
};
// 删除面板地址
const handleDeletePanelAddress = async (name: string) => {
- (window as any).setPanelAddresses = setPanelAddressesFunc;
deletePanelAddress(name);
reinitializeBaseURL();
toast.success("删除成功");
@@ -98,22 +112,27 @@ export const SettingsPage = () => {
useEffect(() => {
loadPanelAddresses();
loadForwardCompactMode();
+ loadMonitorTunnelQualityEnabled();
}, []);
const loadForwardCompactMode = async () => {
try {
const res = await getConfigByName(FORWARD_COMPACT_MODE_CONFIG_KEY);
- const enabled =
- res.code === 0 &&
- typeof res.data?.value === "string" &&
- res.data.value === "true";
-
- setForwardCompactMode(enabled);
+ setForwardCompactMode(parseBooleanConfig(res.data?.value, false));
} catch {
setForwardCompactMode(false);
}
};
+ const loadMonitorTunnelQualityEnabled = async () => {
+ try {
+ const res = await getConfigByName(MONITOR_TUNNEL_QUALITY_ENABLED_CONFIG_KEY);
+ setMonitorTunnelQualityEnabled(parseBooleanConfig(res.data?.value, true));
+ } catch {
+ setMonitorTunnelQualityEnabled(true);
+ }
+ };
+
const handleForwardCompactModeChange = async (enabled: boolean) => {
if (!admin || forwardCompactModeSaving) {
return;
@@ -148,6 +167,40 @@ export const SettingsPage = () => {
}
};
+ const handleMonitorTunnelQualityEnabledChange = async (enabled: boolean) => {
+ if (!admin || monitorTunnelQualitySaving) {
+ return;
+ }
+
+ const previous = monitorTunnelQualityEnabled;
+
+ setMonitorTunnelQualityEnabled(enabled);
+ setMonitorTunnelQualitySaving(true);
+ try {
+ const response = await updateConfig(
+ MONITOR_TUNNEL_QUALITY_ENABLED_CONFIG_KEY,
+ enabled ? "true" : "false",
+ );
+
+ if (response.code === 0) {
+ toast.success(`实时隧道质量检测已${enabled ? "开启" : "关闭"}`);
+ window.dispatchEvent(
+ new CustomEvent(MONITOR_TUNNEL_QUALITY_ENABLED_EVENT, {
+ detail: { enabled },
+ }),
+ );
+ } else {
+ setMonitorTunnelQualityEnabled(previous);
+ toast.error(response.msg || "保存隧道质量检测配置失败");
+ }
+ } catch {
+ setMonitorTunnelQualityEnabled(previous);
+ toast.error("保存隧道质量检测配置失败");
+ } finally {
+ setMonitorTunnelQualitySaving(false);
+ }
+ };
+
const handleUpdateChannelChange = (channel: UpdateReleaseChannel) => {
setUpdateChannel(channel);
setUpdateReleaseChannel(channel);
@@ -216,28 +269,54 @@ export const SettingsPage = () => {
显示设置
-
-
-
-
- 规则页面精简模式
-
-
- 开启后,规则页面列表使用 2.1.6-alpha8 样式。{" "}
-
+
+
+
+
+
+ 规则页面精简模式
+
+
+ 开启后,规则页面列表使用 2.1.6-alpha8 样式。{" "}
+
+
+
-
+ {!admin && (
+
+ 仅管理员可修改该全局配置。
+
+ )}
+
+
+
+
+
+
+ 实时隧道质量检测
+
+
+ 关闭后,前端停止自动刷新,后端停止实时隧道质量探测。
+
+
+
+
+ {!admin && (
+
+ 仅管理员可修改该全局配置。
+
+ )}
- {!admin && (
-
- 仅管理员可修改该全局配置。
-
- )}