import { BaseDialog, Switch } from "@/components/base"; import { useClashInfo } from "@/hooks/use-clash"; import { useVerge } from "@/hooks/use-verge"; import { showNotice } from "@/services/noticeService"; import getSystem from "@/utils/get-system"; import { Shuffle } from "@mui/icons-material"; import { CircularProgress, IconButton, List, ListItem, ListItemText, Stack, TextField } from "@mui/material"; import { useLockFn, useRequest } from "ahooks"; import { forwardRef, useImperativeHandle, useState } from "react"; import { useTranslation } from "react-i18next"; const OS = getSystem(); interface ClashPortViewerProps {} interface ClashPortViewerRef { open: () => void; close: () => void; } const generateRandomPort = () => Math.floor(Math.random() * (65535 - 1025 + 1)) + 1025; export const ClashPortViewer = forwardRef( (props, ref) => { const { t } = useTranslation(); const { clashInfo, patchInfo } = useClashInfo(); const { verge, patchVerge } = useVerge(); const [open, setOpen] = useState(false); // Mixed Port const [mixedPort, setMixedPort] = useState( verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897 ); // 其他端口状态 const [socksPort, setSocksPort] = useState(verge?.verge_socks_port ?? 7898); const [socksEnabled, setSocksEnabled] = useState(verge?.verge_socks_enabled ?? false); const [httpPort, setHttpPort] = useState(verge?.verge_port ?? 7899); const [httpEnabled, setHttpEnabled] = useState(verge?.verge_http_enabled ?? false); const [redirPort, setRedirPort] = useState(verge?.verge_redir_port ?? 7895); const [redirEnabled, setRedirEnabled] = useState(verge?.verge_redir_enabled ?? false); const [tproxyPort, setTproxyPort] = useState(verge?.verge_tproxy_port ?? 7896); const [tproxyEnabled, setTproxyEnabled] = useState(verge?.verge_tproxy_enabled ?? false); // 添加保存请求,防止GUI卡死 const { loading, run: saveSettings } = useRequest( async (params: { clashConfig: any; vergeConfig: any; }) => { const { clashConfig, vergeConfig } = params; await Promise.all([ patchInfo(clashConfig), patchVerge(vergeConfig) ]); }, { manual: true, onSuccess: () => { setOpen(false); showNotice("success", t("Port settings saved")); // 调用提示函数 }, onError: () => { showNotice("error", t("Failed to save settings")); // 调用提示函数 } } ); useImperativeHandle(ref, () => ({ open: () => { setMixedPort(verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897); setSocksPort(verge?.verge_socks_port ?? 7898); setSocksEnabled(verge?.verge_socks_enabled ?? false); setHttpPort(verge?.verge_port ?? 7899); setHttpEnabled(verge?.verge_http_enabled ?? false); setRedirPort(verge?.verge_redir_port ?? 7895); setRedirEnabled(verge?.verge_redir_enabled ?? false); setTproxyPort(verge?.verge_tproxy_port ?? 7896); setTproxyEnabled(verge?.verge_tproxy_enabled ?? false); setOpen( true); }, close: () => setOpen(false), })); const onSave = useLockFn(async () => { // 端口冲突检测 const portList = [ mixedPort, socksEnabled ? socksPort : -1, httpEnabled ? httpPort : -1, redirEnabled ? redirPort : -1, tproxyEnabled ? tproxyPort : -1 ].filter(p => p !== -1); if (new Set(portList).size !== portList.length) { return; } // 验证端口范围 const isValidPort = (port: number) => port >= 1 && port <= 65535; const allPortsValid = [ mixedPort, socksEnabled ? socksPort : 0, httpEnabled ? httpPort : 0, redirEnabled ? redirPort : 0, tproxyEnabled ? tproxyPort : 0 ].every(port => port === 0 || isValidPort(port)); if (!allPortsValid) { return; } // 准备配置数据 const clashConfig = { "mixed-port": mixedPort, "socks-port": socksPort, port: httpPort, "redir-port": redirPort, "tproxy-port": tproxyPort }; const vergeConfig = { verge_mixed_port: mixedPort, verge_socks_port: socksPort, verge_socks_enabled: socksEnabled, verge_port: httpPort, verge_http_enabled: httpEnabled, verge_redir_port: redirPort, verge_redir_enabled: redirEnabled, verge_tproxy_port: tproxyPort, verge_tproxy_enabled: tproxyEnabled }; // 提交保存请求 await saveSettings({ clashConfig, vergeConfig }); }); // 优化的数字输入处理 const handleNumericChange = (setter: (value: number) => void) => (e: React.ChangeEvent) => { const value = e.target.value.replace(/\D+/, ''); if (value === '') { setter(0); return; } const num = parseInt(value, 10); if (!isNaN(num) && num >= 0 && num <= 65535) { setter(num); } }; return ( {t("Saving...")} ) : t("Save") } cancelBtn={t("Cancel")} onClose={() => setOpen(false)} onCancel={() => setOpen(false)} onOk={onSave} >
setMixedPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))} inputProps={{ style: { fontSize: 12 } }} /> setMixedPort(generateRandomPort())} title={t("Random Port")} sx={{ mr: 0.5 }} >
setSocksPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))} disabled={!socksEnabled} inputProps={{ style: { fontSize: 12 } }} /> setSocksPort(generateRandomPort())} title={t("Random Port")} disabled={!socksEnabled} sx={{ mr: 0.5 }} > setSocksEnabled(c)} sx={{ ml: 0.5 }} />
setHttpPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))} disabled={!httpEnabled} inputProps={{ style: { fontSize: 12 } }} /> setHttpPort(generateRandomPort())} title={t("Random Port")} disabled={!httpEnabled} sx={{ mr: 0.5 }} > setHttpEnabled(c)} sx={{ ml: 0.5 }} />
{OS !== "windows" && (
setRedirPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))} disabled={!redirEnabled} inputProps={{ style: { fontSize: 12 } }} /> setRedirPort(generateRandomPort())} title={t("Random Port")} disabled={!redirEnabled} sx={{ mr: 0.5 }} > setRedirEnabled(c)} sx={{ ml: 0.5 }} />
)} {OS === "linux" && (
setTproxyPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))} disabled={!tproxyEnabled} inputProps={{ style: { fontSize: 12 } }} /> setTproxyPort(generateRandomPort())} title={t("Random Port")} disabled={!tproxyEnabled} sx={{ mr: 0.5 }} > setTproxyEnabled(c)} sx={{ ml: 0.5 }} />
)}
); } );