import React from "react"; import { RefreshCw, AlertCircle, Clock } from "lucide-react"; import { useTranslation } from "react-i18next"; import { type AppId } from "@/lib/api"; import { useUsageQuery } from "@/lib/query/queries"; import { UsageData, Provider } from "@/types"; interface UsageFooterProps { provider: Provider; providerId: string; appId: AppId; usageEnabled: boolean; // 是否启用了用量查询 isCurrent: boolean; // 是否为当前激活的供应商 inline?: boolean; // 是否内联显示(在按钮左侧) } const UsageFooter: React.FC = ({ provider, providerId, appId, usageEnabled, isCurrent, inline = false, }) => { const { t } = useTranslation(); // 统一的用量查询(自动查询仅对当前激活的供应商启用) const autoQueryInterval = isCurrent ? provider.meta?.usage_script?.autoQueryInterval || 0 : 0; const { data: usage, isFetching: loading, lastQueriedAt, refetch, } = useUsageQuery(providerId, appId, { enabled: usageEnabled, autoQueryInterval, }); // 🆕 定期更新当前时间,用于刷新相对时间显示 const [now, setNow] = React.useState(Date.now()); React.useEffect(() => { if (!lastQueriedAt) return; // 每30秒更新一次当前时间,触发相对时间显示的刷新 const interval = setInterval(() => { setNow(Date.now()); }, 30000); // 30秒 return () => clearInterval(interval); }, [lastQueriedAt]); // 只在启用用量查询且有数据时显示 if (!usageEnabled || !usage) return null; // 错误状态 if (!usage.success) { if (inline) { return (
{t("usage.queryFailed")}
); } return (
{usage.error || t("usage.queryFailed")}
{/* 刷新按钮 */}
); } const usageDataList = usage.data || []; // 无数据时不显示 if (usageDataList.length === 0) return null; // 内联模式:仅显示第一个套餐的核心数据(分上下两行) if (inline) { const firstUsage = usageDataList[0]; const isExpired = firstUsage.isValid === false; return (
{/* 第一行:更新时间和刷新按钮 */}
{/* 上次查询时间 */} {lastQueriedAt ? formatRelativeTime(lastQueriedAt, now, t) : t("usage.never", { defaultValue: "从未更新" })} {/* 刷新按钮 */}
{/* 第二行:用量和剩余 */}
{/* 已用 */} {firstUsage.used !== undefined && (
{t("usage.used")} {firstUsage.used.toFixed(2)}
)} {/* 剩余 */} {firstUsage.remaining !== undefined && (
{t("usage.remaining")} {firstUsage.remaining.toFixed(2)}
)} {/* 单位 */} {firstUsage.unit && ( {firstUsage.unit} )}
); } return (
{/* 标题行:包含刷新按钮和自动查询时间 */}
{t("usage.planUsage")}
{/* 自动查询时间提示 */} {lastQueriedAt && ( {formatRelativeTime(lastQueriedAt, now, t)} )}
{/* 套餐列表 */}
{usageDataList.map((usageData, index) => ( ))}
); }; // 单个套餐数据展示组件 const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => { const { t } = useTranslation(); const { planName, extra, isValid, invalidMessage, total, used, remaining, unit, } = data; // 判断套餐是否失效(isValid 为 false 或未定义时视为有效) const isExpired = isValid === false; return (
{/* 标题部分:25% */}
{planName ? ( 💰 {planName} ) : ( )}
{/* 扩展字段:30% */}
{extra && ( {extra} )} {isExpired && ( {invalidMessage || t("usage.invalid")} )}
{/* 用量信息:45% */}
{/* 总额度 */} {total !== undefined && ( <> {t("usage.total")} {total === -1 ? "∞" : total.toFixed(2)} | )} {/* 已用额度 */} {used !== undefined && ( <> {t("usage.used")} {used.toFixed(2)} | )} {/* 剩余额度 - 突出显示 */} {remaining !== undefined && ( <> {t("usage.remaining")} {remaining.toFixed(2)} )} {unit && ( {unit} )}
); }; // 格式化相对时间 function formatRelativeTime( timestamp: number, now: number, t: (key: string, options?: { count?: number }) => string, ): string { const diff = Math.floor((now - timestamp) / 1000); // 秒 if (diff < 60) { return t("usage.justNow"); } else if (diff < 3600) { const minutes = Math.floor(diff / 60); return t("usage.minutesAgo", { count: minutes }); } else if (diff < 86400) { const hours = Math.floor(diff / 3600); return t("usage.hoursAgo", { count: hours }); } else { const days = Math.floor(diff / 86400); return t("usage.daysAgo", { count: days }); } } export default UsageFooter;