import { RefreshRounded, StorageOutlined } from "@mui/icons-material"; import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, LinearProgress, List, ListItem, ListItemText, Typography, alpha, styled, } from "@mui/material"; import { useLockFn } from "ahooks"; import dayjs from "dayjs"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { updateProxyProvider } from "tauri-plugin-mihomo-api"; import { useAppData } from "@/providers/app-data-context"; import { showNotice } from "@/services/noticeService"; import parseTraffic from "@/utils/parse-traffic"; // 样式化组件 - 类型框 const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({ display: "inline-block", border: "1px solid #ccc", borderColor: alpha(theme.palette.secondary.main, 0.5), color: alpha(theme.palette.secondary.main, 0.8), borderRadius: 4, fontSize: 10, marginRight: "4px", padding: "0 2px", lineHeight: 1.25, })); // 解析过期时间 const parseExpire = (expire?: number) => { if (!expire) return "-"; return dayjs(expire * 1000).format("YYYY-MM-DD"); }; export const ProviderButton = () => { const { t } = useTranslation(); const [open, setOpen] = useState(false); const { proxyProviders, refreshProxy, refreshProxyProviders } = useAppData(); const [updating, setUpdating] = useState>({}); // 检查是否有提供者 const hasProviders = Object.keys(proxyProviders || {}).length > 0; // 更新单个代理提供者 const updateProvider = useLockFn(async (name: string) => { try { // 设置更新状态 setUpdating((prev) => ({ ...prev, [name]: true })); await updateProxyProvider(name); // 刷新数据 await refreshProxy(); await refreshProxyProviders(); showNotice.success("components.providers.notices.updateSuccess", { name, }); } catch (err) { showNotice.error("components.providers.notices.updateFailed", { name, message: String(err), }); } finally { // 清除更新状态 setUpdating((prev) => ({ ...prev, [name]: false })); } }); // 更新所有代理提供者 const updateAllProviders = useLockFn(async () => { try { // 获取所有provider的名称 const allProviders = Object.keys(proxyProviders || {}); if (allProviders.length === 0) { showNotice.info("components.providers.notices.none"); return; } // 设置所有provider为更新中状态 const newUpdating = allProviders.reduce( (acc, key) => { acc[key] = true; return acc; }, {} as Record, ); setUpdating(newUpdating); // 改为串行逐个更新所有provider for (const name of allProviders) { try { await updateProxyProvider(name); // 每个更新完成后更新状态 setUpdating((prev) => ({ ...prev, [name]: false })); } catch (err) { console.error(`更新 ${name} 失败`, err); // 继续执行下一个,不中断整体流程 } } // 刷新数据 await refreshProxy(); await refreshProxyProviders(); showNotice.success("components.providers.notices.allUpdated"); } catch (err) { showNotice.error("components.providers.notices.genericError", { message: String(err), }); } finally { // 清除所有更新状态 setUpdating({}); } }); const handleClose = () => { setOpen(false); }; if (!hasProviders) return null; return ( <> {t("pages.proxies.provider.title")} {Object.entries(proxyProviders || {}) .sort() .map(([key, item]) => { const provider = item; const time = dayjs(provider.updatedAt); const isUpdating = updating[key]; // 订阅信息 const sub = provider.subscriptionInfo; const hasSubInfo = !!sub; const upload = sub?.Upload || 0; const download = sub?.Download || 0; const total = sub?.Total || 0; const expire = sub?.Expire || 0; // 流量使用进度 const progress = total > 0 ? Math.min( Math.round(((download + upload) * 100) / total) + 1, 100, ) : 0; return ( { const bgcolor = mode === "light" ? "#ffffff" : "#24252f"; const hoverColor = mode === "light" ? alpha(primary.main, 0.1) : alpha(primary.main, 0.2); return { backgroundColor: bgcolor, "&:hover": { backgroundColor: hoverColor, }, }; }, ]} > {key} {provider.proxies.length} {provider.vehicleType} {t("Update At")}: {time.fromNow()} } secondary={ <> {/* 订阅信息 */} {hasSubInfo && ( <> {parseTraffic(upload + download)} /{" "} {parseTraffic(total)} {parseExpire(expire)} {/* 进度条 */} 0 ? 1 : 0, }} /> )} } /> { updateProvider(key); }} disabled={isUpdating} sx={{ animation: isUpdating ? "spin 1s linear infinite" : "none", "@keyframes spin": { "0%": { transform: "rotate(0deg)" }, "100%": { transform: "rotate(360deg)" }, }, }} title={t("pages.proxies.provider.actions.update")} aria-label={t("pages.proxies.provider.actions.update")} > ); })} ); };