mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 17:15:38 +08:00
perf: optimize all home page components
This commit is contained in:
@@ -7,17 +7,16 @@ import {
|
||||
Tooltip,
|
||||
alpha,
|
||||
useTheme,
|
||||
Button,
|
||||
Fade,
|
||||
} from "@mui/material";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useMemo, memo, FC } from "react";
|
||||
import ProxyControlSwitches from "@/components/shared/ProxyControlSwitches";
|
||||
import { Notice } from "@/components/base";
|
||||
import {
|
||||
LanguageRounded,
|
||||
ComputerRounded,
|
||||
TroubleshootRounded,
|
||||
HelpOutlineRounded,
|
||||
SvgIconComponent,
|
||||
} from "@mui/icons-material";
|
||||
import useSWR from "swr";
|
||||
import {
|
||||
@@ -26,15 +25,131 @@ import {
|
||||
getRunningMode,
|
||||
} from "@/services/cmds";
|
||||
|
||||
export const ProxyTunCard = () => {
|
||||
const LOCAL_STORAGE_TAB_KEY = "clash-verge-proxy-active-tab";
|
||||
|
||||
interface TabButtonProps {
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
icon: SvgIconComponent;
|
||||
label: string;
|
||||
hasIndicator?: boolean;
|
||||
}
|
||||
|
||||
// 抽取Tab组件以减少重复代码
|
||||
const TabButton: FC<TabButtonProps> = memo(({
|
||||
isActive,
|
||||
onClick,
|
||||
icon: Icon,
|
||||
label,
|
||||
hasIndicator = false
|
||||
}) => (
|
||||
<Paper
|
||||
elevation={isActive ? 2 : 0}
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
px: 2,
|
||||
py: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 1,
|
||||
bgcolor: isActive ? "primary.main" : "background.paper",
|
||||
color: isActive ? "primary.contrastText" : "text.primary",
|
||||
borderRadius: 1.5,
|
||||
flex: 1,
|
||||
maxWidth: 160,
|
||||
transition: "all 0.2s ease-in-out",
|
||||
position: "relative",
|
||||
"&:hover": {
|
||||
transform: "translateY(-1px)",
|
||||
boxShadow: 1,
|
||||
},
|
||||
"&:after": isActive
|
||||
? {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
bottom: -9,
|
||||
left: "50%",
|
||||
width: 2,
|
||||
height: 9,
|
||||
bgcolor: "primary.main",
|
||||
transform: "translateX(-50%)",
|
||||
}
|
||||
: {},
|
||||
}}
|
||||
>
|
||||
<Icon fontSize="small" />
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontWeight: isActive ? 600 : 400 }}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
{hasIndicator && (
|
||||
<Box
|
||||
sx={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
bgcolor: isActive ? "#fff" : "success.main",
|
||||
position: "absolute",
|
||||
top: 8,
|
||||
right: 8,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
));
|
||||
|
||||
interface TabDescriptionProps {
|
||||
description: string;
|
||||
tooltipTitle: string;
|
||||
}
|
||||
|
||||
// 抽取描述文本组件
|
||||
const TabDescription: FC<TabDescriptionProps> = memo(({ description, tooltipTitle }) => (
|
||||
<Fade in={true} timeout={200}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
sx={{
|
||||
width: "95%",
|
||||
textAlign: "center",
|
||||
color: "text.secondary",
|
||||
p: 0.8,
|
||||
borderRadius: 1,
|
||||
borderColor: "primary.main",
|
||||
borderWidth: 1,
|
||||
borderStyle: "solid",
|
||||
backgroundColor: "background.paper",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 0.5,
|
||||
wordBreak: "break-word",
|
||||
hyphens: "auto",
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<HelpOutlineRounded
|
||||
sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
</Fade>
|
||||
));
|
||||
|
||||
export const ProxyTunCard: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<string>("system");
|
||||
const [activeTab, setActiveTab] = useState<string>(() =>
|
||||
localStorage.getItem(LOCAL_STORAGE_TAB_KEY) || "system"
|
||||
);
|
||||
|
||||
// 获取代理状态信息
|
||||
const { data: sysproxy } = useSWR("getSystemProxy", getSystemProxy);
|
||||
const { data: autoproxy } = useSWR("getAutotemProxy", getAutotemProxy);
|
||||
const { data: runningMode } = useSWR("getRunningMode", getRunningMode);
|
||||
|
||||
// 是否以sidecar模式运行
|
||||
@@ -42,26 +157,34 @@ export const ProxyTunCard = () => {
|
||||
|
||||
// 处理错误
|
||||
const handleError = (err: Error) => {
|
||||
setError(err.message);
|
||||
Notice.error(err.message || err.toString(), 3000);
|
||||
};
|
||||
|
||||
// 用户提示文本
|
||||
const getTabDescription = (tab: string) => {
|
||||
switch (tab) {
|
||||
case "system":
|
||||
return sysproxy?.enable
|
||||
? t("System Proxy Enabled")
|
||||
: t("System Proxy Disabled");
|
||||
case "tun":
|
||||
return isSidecarMode
|
||||
? t("TUN Mode Service Required")
|
||||
: t("TUN Mode Intercept Info");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
// 处理标签切换并保存到localStorage
|
||||
const handleTabChange = (tab: string) => {
|
||||
setActiveTab(tab);
|
||||
localStorage.setItem(LOCAL_STORAGE_TAB_KEY, tab);
|
||||
};
|
||||
|
||||
// 用户提示文本 - 使用useMemo避免重复计算
|
||||
const tabDescription = useMemo(() => {
|
||||
if (activeTab === "system") {
|
||||
return {
|
||||
text: sysproxy?.enable
|
||||
? t("System Proxy Enabled")
|
||||
: t("System Proxy Disabled"),
|
||||
tooltip: t("System Proxy Info")
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
text: isSidecarMode
|
||||
? t("TUN Mode Service Required")
|
||||
: t("TUN Mode Intercept Info"),
|
||||
tooltip: t("Tun Mode Info")
|
||||
};
|
||||
}
|
||||
}, [activeTab, sysproxy?.enable, isSidecarMode, t]);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||
{/* 选项卡 */}
|
||||
@@ -75,112 +198,19 @@ export const ProxyTunCard = () => {
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
elevation={activeTab === "system" ? 2 : 0}
|
||||
onClick={() => setActiveTab("system")}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
px: 2,
|
||||
py: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 1,
|
||||
bgcolor:
|
||||
activeTab === "system" ? "primary.main" : "background.paper",
|
||||
color:
|
||||
activeTab === "system" ? "primary.contrastText" : "text.primary",
|
||||
borderRadius: 1.5,
|
||||
flex: 1,
|
||||
maxWidth: 160,
|
||||
transition: "all 0.2s ease-in-out",
|
||||
position: "relative",
|
||||
"&:hover": {
|
||||
transform: "translateY(-1px)",
|
||||
boxShadow: 1,
|
||||
},
|
||||
"&:after":
|
||||
activeTab === "system"
|
||||
? {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
bottom: -9,
|
||||
left: "50%",
|
||||
width: 2,
|
||||
height: 9,
|
||||
bgcolor: "primary.main",
|
||||
transform: "translateX(-50%)",
|
||||
}
|
||||
: {},
|
||||
}}
|
||||
>
|
||||
<ComputerRounded fontSize="small" />
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontWeight: activeTab === "system" ? 600 : 400 }}
|
||||
>
|
||||
{t("System Proxy")}
|
||||
</Typography>
|
||||
{sysproxy?.enable && (
|
||||
<Box
|
||||
sx={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
bgcolor: activeTab === "system" ? "#fff" : "success.main",
|
||||
position: "absolute",
|
||||
top: 8,
|
||||
right: 8,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
<Paper
|
||||
elevation={activeTab === "tun" ? 2 : 0}
|
||||
onClick={() => setActiveTab("tun")}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
px: 2,
|
||||
py: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 1,
|
||||
bgcolor: activeTab === "tun" ? "primary.main" : "background.paper",
|
||||
color:
|
||||
activeTab === "tun" ? "primary.contrastText" : "text.primary",
|
||||
borderRadius: 1.5,
|
||||
flex: 1,
|
||||
maxWidth: 160,
|
||||
transition: "all 0.2s ease-in-out",
|
||||
position: "relative",
|
||||
"&:hover": {
|
||||
transform: "translateY(-1px)",
|
||||
boxShadow: 1,
|
||||
},
|
||||
"&:after":
|
||||
activeTab === "tun"
|
||||
? {
|
||||
content: '""',
|
||||
position: "absolute",
|
||||
bottom: -9,
|
||||
left: "50%",
|
||||
width: 2,
|
||||
height: 9,
|
||||
bgcolor: "primary.main",
|
||||
transform: "translateX(-50%)",
|
||||
}
|
||||
: {},
|
||||
}}
|
||||
>
|
||||
<TroubleshootRounded fontSize="small" />
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontWeight: activeTab === "tun" ? 600 : 400 }}
|
||||
>
|
||||
{t("Tun Mode")}
|
||||
</Typography>
|
||||
</Paper>
|
||||
<TabButton
|
||||
isActive={activeTab === "system"}
|
||||
onClick={() => handleTabChange("system")}
|
||||
icon={ComputerRounded}
|
||||
label={t("System Proxy")}
|
||||
hasIndicator={sysproxy?.enable}
|
||||
/>
|
||||
<TabButton
|
||||
isActive={activeTab === "tun"}
|
||||
onClick={() => handleTabChange("tun")}
|
||||
icon={TroubleshootRounded}
|
||||
label={t("Tun Mode")}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
{/* 说明文本区域 */}
|
||||
@@ -194,71 +224,10 @@ export const ProxyTunCard = () => {
|
||||
overflow: "visible",
|
||||
}}
|
||||
>
|
||||
{activeTab === "system" && (
|
||||
<Fade in={true} timeout={200}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
sx={{
|
||||
width: "95%",
|
||||
textAlign: "center",
|
||||
color: "text.secondary",
|
||||
p: 0.8,
|
||||
borderRadius: 1,
|
||||
borderColor: "primary.main",
|
||||
borderWidth: 1,
|
||||
borderStyle: "solid",
|
||||
backgroundColor: "background.paper",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 0.5,
|
||||
wordBreak: "break-word",
|
||||
hyphens: "auto",
|
||||
}}
|
||||
>
|
||||
{getTabDescription("system")}
|
||||
<Tooltip title={t("System Proxy Info")}>
|
||||
<HelpOutlineRounded
|
||||
sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
</Fade>
|
||||
)}
|
||||
|
||||
{activeTab === "tun" && (
|
||||
<Fade in={true} timeout={200}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
sx={{
|
||||
width: "95%",
|
||||
textAlign: "center",
|
||||
color: "text.secondary",
|
||||
p: 0.8,
|
||||
borderRadius: 1,
|
||||
borderColor: "primary.main",
|
||||
borderWidth: 1,
|
||||
borderStyle: "solid",
|
||||
backgroundColor: "background.paper",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 0.5,
|
||||
wordBreak: "break-word",
|
||||
hyphens: "auto",
|
||||
}}
|
||||
>
|
||||
{getTabDescription("tun")}
|
||||
<Tooltip title={t("Tun Mode Info")}>
|
||||
<HelpOutlineRounded
|
||||
sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
</Fade>
|
||||
)}
|
||||
<TabDescription
|
||||
description={tabDescription.text}
|
||||
tooltipTitle={tabDescription.tooltip}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 控制开关部分 */}
|
||||
|
||||
Reference in New Issue
Block a user