Revert "add: home card drag (#4215)"

This reverts commit 84989e0ea3.
This commit is contained in:
Tunglies
2025-08-07 20:04:22 +08:00
parent a3957289c8
commit 2af8c32497
3 changed files with 120 additions and 243 deletions

View File

@@ -13,7 +13,6 @@
- 增强型流量监控,支持更详细的数据分析 - 增强型流量监控,支持更详细的数据分析
- 新增流量图表多种显示模式 - 新增流量图表多种显示模式
- 新增强制刷新配置和节点缓存功能 - 新增强制刷新配置和节点缓存功能
- 添加首页卡片移动功能(测试阶段)
- 首页流量统计支持查看刻度线详情 - 首页流量统计支持查看刻度线详情
### 🚀 性能优化 ### 🚀 性能优化

View File

@@ -4,6 +4,7 @@ import {
Button, Button,
IconButton, IconButton,
useTheme, useTheme,
keyframes,
Dialog, Dialog,
DialogTitle, DialogTitle,
DialogContent, DialogContent,
@@ -28,7 +29,7 @@ import { useNavigate } from "react-router-dom";
import { ProxyTunCard } from "@/components/home/proxy-tun-card"; import { ProxyTunCard } from "@/components/home/proxy-tun-card";
import { ClashModeCard } from "@/components/home/clash-mode-card"; import { ClashModeCard } from "@/components/home/clash-mode-card";
import { EnhancedTrafficStats } from "@/components/home/enhanced-traffic-stats"; import { EnhancedTrafficStats } from "@/components/home/enhanced-traffic-stats";
import { useState, useEffect } from "react"; import { useState } from "react";
import { HomeProfileCard } from "@/components/home/home-profile-card"; import { HomeProfileCard } from "@/components/home/home-profile-card";
import { EnhancedCard } from "@/components/home/enhanced-card"; import { EnhancedCard } from "@/components/home/enhanced-card";
import { CurrentProxyCard } from "@/components/home/current-proxy-card"; import { CurrentProxyCard } from "@/components/home/current-proxy-card";
@@ -39,14 +40,19 @@ import { useLockFn } from "ahooks";
import { entry_lightweight_mode, openWebUrl } from "@/services/cmds"; import { entry_lightweight_mode, openWebUrl } from "@/services/cmds";
import { TestCard } from "@/components/home/test-card"; import { TestCard } from "@/components/home/test-card";
import { IpInfoCard } from "@/components/home/ip-info-card"; import { IpInfoCard } from "@/components/home/ip-info-card";
import {
DragDropContext, // 定义旋转动画
Droppable, const round = keyframes`
Draggable, from { transform: rotate(0deg); }
DropResult, to { transform: rotate(360deg); }
DroppableProvided, `;
DraggableProvided,
} from "react-beautiful-dnd"; // 辅助函数解析URL和过期时间
function parseUrl(url?: string) {
if (!url) return "-";
if (url.startsWith("http")) return new URL(url).host;
return "local";
}
// 定义首页卡片设置接口 // 定义首页卡片设置接口
interface HomeCardsSettings { interface HomeCardsSettings {
@@ -63,13 +69,6 @@ interface HomeCardsSettings {
[key: string]: boolean; [key: string]: boolean;
} }
// 卡片配置接口,包含排序信息
interface CardConfig {
id: string;
size: number;
enabled: boolean;
}
// 首页设置对话框组件接口 // 首页设置对话框组件接口
interface HomeSettingsDialogProps { interface HomeSettingsDialogProps {
open: boolean; open: boolean;
@@ -78,35 +77,6 @@ interface HomeSettingsDialogProps {
onSave: (cards: HomeCardsSettings) => void; onSave: (cards: HomeCardsSettings) => void;
} }
// 确保对象符合HomeCardsSettings类型的辅助函数
const ensureHomeCardsSettings = (obj: any): HomeCardsSettings => {
const defaultSettings: HomeCardsSettings = {
profile: true,
proxy: true,
network: true,
mode: true,
traffic: true,
info: false,
clashinfo: true,
systeminfo: true,
test: true,
ip: true,
};
if (!obj || typeof obj !== "object") return defaultSettings;
// 合并默认值和传入对象,确保所有必要属性都存在
return Object.keys(defaultSettings).reduce((acc, key) => {
return {
...acc,
[key]:
typeof obj[key] === "boolean"
? obj[key]
: defaultSettings[key as keyof HomeCardsSettings],
};
}, {} as HomeCardsSettings);
};
// 首页设置对话框组件 // 首页设置对话框组件
const HomeSettingsDialog = ({ const HomeSettingsDialog = ({
open, open,
@@ -118,16 +88,15 @@ const HomeSettingsDialog = ({
const [cards, setCards] = useState<HomeCardsSettings>(homeCards); const [cards, setCards] = useState<HomeCardsSettings>(homeCards);
const { patchVerge } = useVerge(); const { patchVerge } = useVerge();
const handleToggle = (key: keyof HomeCardsSettings) => { const handleToggle = (key: string) => {
setCards((prev) => ({ setCards((prev: HomeCardsSettings) => ({
...prev, ...prev,
[key]: !prev[key], [key]: !prev[key],
})); }));
}; };
const handleSave = async () => { const handleSave = async () => {
// 明确类型为HomeCardsSettings await patchVerge({ home_cards: cards });
await patchVerge({ home_cards: cards as HomeCardsSettings });
onSave(cards); onSave(cards);
onClose(); onClose();
}; };
@@ -140,7 +109,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.profile} checked={cards.profile || false}
onChange={() => handleToggle("profile")} onChange={() => handleToggle("profile")}
/> />
} }
@@ -149,7 +118,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.proxy} checked={cards.proxy || false}
onChange={() => handleToggle("proxy")} onChange={() => handleToggle("proxy")}
/> />
} }
@@ -158,7 +127,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.network} checked={cards.network || false}
onChange={() => handleToggle("network")} onChange={() => handleToggle("network")}
/> />
} }
@@ -167,7 +136,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.mode} checked={cards.mode || false}
onChange={() => handleToggle("mode")} onChange={() => handleToggle("mode")}
/> />
} }
@@ -176,7 +145,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.traffic} checked={cards.traffic || false}
onChange={() => handleToggle("traffic")} onChange={() => handleToggle("traffic")}
/> />
} }
@@ -185,7 +154,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.test} checked={cards.test || false}
onChange={() => handleToggle("test")} onChange={() => handleToggle("test")}
/> />
} }
@@ -194,7 +163,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.ip} checked={cards.ip || false}
onChange={() => handleToggle("ip")} onChange={() => handleToggle("ip")}
/> />
} }
@@ -203,7 +172,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.clashinfo} checked={cards.clashinfo || false}
onChange={() => handleToggle("clashinfo")} onChange={() => handleToggle("clashinfo")}
/> />
} }
@@ -212,7 +181,7 @@ const HomeSettingsDialog = ({
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={cards.systeminfo} checked={cards.systeminfo || false}
onChange={() => handleToggle("systeminfo")} onChange={() => handleToggle("systeminfo")}
/> />
} }
@@ -232,74 +201,41 @@ const HomeSettingsDialog = ({
export const HomePage = () => { export const HomePage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { verge, patchVerge } = useVerge(); const { verge } = useVerge();
const { current, mutateProfiles } = useProfiles(); const { current, mutateProfiles } = useProfiles();
const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
// 设置弹窗的状态 // 设置弹窗的状态
const [settingsOpen, setSettingsOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false);
// 卡片显示状态 - 确保类型正确 // 卡片显示状态
const [homeCards, setHomeCards] = useState<HomeCardsSettings>( const [homeCards, setHomeCards] = useState<HomeCardsSettings>(
ensureHomeCardsSettings(verge?.home_cards), (verge?.home_cards as HomeCardsSettings) || {
profile: true,
proxy: true,
network: true,
mode: true,
traffic: true,
clashinfo: true,
systeminfo: true,
test: true,
ip: true,
},
); );
// 卡片排序配置 - 默认为初始顺序 // 导航到订阅页面
const [cardOrder, setCardOrder] = useState<string[]>( const goToProfiles = () => {
// 明确断言类型 navigate("/profile");
(verge?.card_order as string[]) || [ };
"profile",
"proxy",
"network",
"mode",
"traffic",
"test",
"ip",
"clashinfo",
"systeminfo",
],
);
// 当homeCards变化时确保cardOrder中只包含启用的卡片 // 导航到代理页面
useEffect(() => { const goToProxies = () => {
const enabledCards = Object.entries(homeCards) navigate("/");
.filter(([_, enabled]) => enabled) };
.map(([id]) => id);
// 过滤掉已禁用的卡片 // 导航到设置页面
const filteredOrder = cardOrder.filter((id) => enabledCards.includes(id)); const goToSettings = () => {
navigate("/settings");
// 添加新启用但不在排序中的卡片
const newCards = enabledCards.filter((id) => !filteredOrder.includes(id));
setCardOrder([...filteredOrder, ...newCards]);
}, [homeCards]);
// 保存卡片排序
const saveCardOrder = useLockFn(async (order: string[]) => {
await patchVerge({ card_order: order } as any);
setCardOrder(order);
});
// 处理拖拽结束
const handleDragEnd = (result: DropResult) => {
const { destination, source, draggableId } = result;
// 拖拽到无效位置或原位置,不做处理
if (
!destination ||
(destination.droppableId === source.droppableId &&
destination.index === source.index)
) {
return;
}
// 重新排序
const newOrder = Array.from(cardOrder);
newOrder.splice(source.index, 1);
newOrder.splice(destination.index, 0, draggableId);
// 保存新顺序
saveCardOrder(newOrder);
}; };
// 文档链接函数 // 文档链接函数
@@ -307,12 +243,12 @@ export const HomePage = () => {
return openWebUrl("https://clash-verge-rev.github.io/index.html"); return openWebUrl("https://clash-verge-rev.github.io/index.html");
}); });
// 卡片设置弹窗 // 新增:打开设置弹窗
const openSettings = () => { const openSettings = () => {
setSettingsOpen(true); setSettingsOpen(true);
}; };
// 保存勾选设置 // 新增保存设置时用requestIdleCallback/setTimeout
const handleSaveSettings = (newCards: HomeCardsSettings) => { const handleSaveSettings = (newCards: HomeCardsSettings) => {
if (window.requestIdleCallback) { if (window.requestIdleCallback) {
window.requestIdleCallback(() => setHomeCards(newCards)); window.requestIdleCallback(() => setHomeCards(newCards));
@@ -321,68 +257,6 @@ export const HomePage = () => {
} }
}; };
// 获取卡片配置信息
const getCardConfig = (id: string): CardConfig => {
const configs: Record<string, CardConfig> = {
profile: { id: "profile", size: 6, enabled: homeCards.profile },
proxy: { id: "proxy", size: 6, enabled: homeCards.proxy },
network: { id: "network", size: 6, enabled: homeCards.network },
mode: { id: "mode", size: 6, enabled: homeCards.mode },
traffic: { id: "traffic", size: 12, enabled: homeCards.traffic },
test: { id: "test", size: 6, enabled: homeCards.test },
ip: { id: "ip", size: 6, enabled: homeCards.ip },
clashinfo: { id: "clashinfo", size: 6, enabled: homeCards.clashinfo },
systeminfo: { id: "systeminfo", size: 6, enabled: homeCards.systeminfo },
};
if (!configs[id]) {
console.warn(`检测到未知卡片ID: ${id},使用默认配置`);
return { id, size: 6, enabled: false };
}
return configs[id];
};
// 渲染卡片内容
const renderCardContent = (id: string) => {
switch (id) {
case "profile":
return (
<HomeProfileCard
current={current}
onProfileUpdated={mutateProfiles}
/>
);
case "proxy":
return <CurrentProxyCard />;
case "network":
return <NetworkSettingsCard />;
case "mode":
return <ClashModeEnhancedCard />;
case "traffic":
return (
<EnhancedCard
title={t("Traffic Stats")}
icon={<SpeedOutlined />}
iconColor="secondary"
>
<EnhancedTrafficStats />
</EnhancedCard>
);
case "test":
return <TestCard />;
case "ip":
return <IpInfoCard />;
case "clashinfo":
return <ClashInfoCard />;
case "systeminfo":
return <SystemInfoCard />;
default:
console.warn(`无法渲染未知卡片: ${id}`);
return null;
}
};
return ( return (
<BasePage <BasePage
title={t("Label-Home")} title={t("Label-Home")}
@@ -411,62 +285,73 @@ export const HomePage = () => {
</Box> </Box>
} }
> >
{/* 拖拽上下文 */} <Grid container spacing={1.5} columns={{ xs: 6, sm: 6, md: 12 }}>
<DragDropContext onDragEnd={handleDragEnd}> {/* 订阅和当前节点部分 */}
<Droppable {homeCards.profile && (
droppableId="home-cards" <Grid size={6}>
isDropDisabled={false} <HomeProfileCard
isCombineEnabled={false} current={current}
ignoreContainerClipping={false} onProfileUpdated={mutateProfiles}
> />
{(provided: DroppableProvided) => ( </Grid>
<Grid )}
container
spacing={1.5}
columns={{ xs: 6, sm: 6, md: 12 }}
ref={provided.innerRef}
{...provided.droppableProps}
>
{cardOrder
.filter((id) => {
const config = getCardConfig(id);
return homeCards[id] && config.enabled;
})
.map((id, index) => {
const config = getCardConfig(id);
if (!config) return null;
return ( {homeCards.proxy && (
<Draggable <Grid size={6}>
key={id} <CurrentProxyCard />
draggableId={id} </Grid>
index={index} )}
isDragDisabled={false}
> {/* 代理和网络设置区域 */}
{(provided: DraggableProvided) => ( {homeCards.network && (
<Grid <Grid size={6}>
size={config.size} <NetworkSettingsCard />
ref={provided.innerRef} </Grid>
{...provided.draggableProps} )}
{...provided.dragHandleProps}
sx={{ {homeCards.mode && (
cursor: "grab", <Grid size={6}>
"&:active": { <ClashModeEnhancedCard />
cursor: "grabbing", </Grid>
}, )}
}}
> {/* 增强的流量统计区域 */}
{renderCardContent(id)} {homeCards.traffic && (
</Grid> <Grid size={12}>
)} <EnhancedCard
</Draggable> title={t("Traffic Stats")}
); icon={<SpeedOutlined />}
})} iconColor="secondary"
{provided.placeholder} >
</Grid> <EnhancedTrafficStats />
)} </EnhancedCard>
</Droppable> </Grid>
</DragDropContext> )}
{/* 测试网站部分 */}
{homeCards.test && (
<Grid size={6}>
<TestCard />
</Grid>
)}
{/* IP信息卡片 */}
{homeCards.ip && (
<Grid size={6}>
<IpInfoCard />
</Grid>
)}
{/* Clash信息 */}
{homeCards.clashinfo && (
<Grid size={6}>
<ClashInfoCard />
</Grid>
)}
{/* 系统信息 */}
{homeCards.systeminfo && (
<Grid size={6}>
<SystemInfoCard />
</Grid>
)}
</Grid>
{/* 首页设置弹窗 */} {/* 首页设置弹窗 */}
<HomeSettingsDialog <HomeSettingsDialog

View File

@@ -822,7 +822,6 @@ interface IVergeConfig {
verge_tproxy_enabled?: boolean; verge_tproxy_enabled?: boolean;
verge_socks_enabled?: boolean; verge_socks_enabled?: boolean;
verge_http_enabled?: boolean; verge_http_enabled?: boolean;
card_order?: string[];
enable_proxy_guard?: boolean; enable_proxy_guard?: boolean;
enable_bypass_check?: boolean; enable_bypass_check?: boolean;
use_default_bypass?: boolean; use_default_bypass?: boolean;
@@ -861,12 +860,6 @@ interface IVergeConfig {
enable_external_controller?: boolean; enable_external_controller?: boolean;
} }
interface CardConfig {
id: string;
size: number;
enabled: boolean;
}
interface IWebDavFile { interface IWebDavFile {
filename: string; filename: string;
href: string; href: string;