diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 516846c..dc0ce38 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -361,11 +361,13 @@ "title": "Antigravity Quota", "empty_title": "No Antigravity Auth Files", "empty_desc": "Upload an Antigravity credential to view remaining quota.", - "idle": "Not loaded. Click Refresh.", + "idle": "Not loaded. Click Refresh Button.", "loading": "Loading quota...", "load_failed": "Failed to load quota: {{message}}", "missing_auth_index": "Auth file missing auth_index", - "empty_models": "No quota data available" + "empty_models": "No quota data available", + "refresh_button": "Refresh Quota", + "fetch_all": "Fetch All" }, "vertex_import": { "title": "Vertex JSON Login", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 083f5f9..27c0974 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -361,11 +361,13 @@ "title": "Antigravity 额度", "empty_title": "暂无 Antigravity 认证", "empty_desc": "上传 Antigravity 认证文件后即可查看额度。", - "idle": "尚未加载额度,请点击刷新。", + "idle": "尚未加载额度,请点击刷新按钮。", "loading": "正在加载额度...", "load_failed": "额度获取失败:{{message}}", "missing_auth_index": "认证文件缺少 auth_index", - "empty_models": "暂无额度数据" + "empty_models": "暂无额度数据", + "refresh_button": "刷新额度", + "fetch_all": "获取全部" }, "vertex_import": { "title": "Vertex JSON 登录", diff --git a/src/pages/AuthFilesPage.tsx b/src/pages/AuthFilesPage.tsx index 9d64329..5b150a7 100644 --- a/src/pages/AuthFilesPage.tsx +++ b/src/pages/AuthFilesPage.tsx @@ -172,8 +172,7 @@ const ANTIGRAVITY_QUOTA_GROUPS: AntigravityQuotaGroupDefinition[] = [ } ]; -let antigravityQuotaCache: Record = {}; -let antigravityQuotaCacheLoaded = false; + // 标准化 auth_index 值(与 usage.ts 中的 normalizeAuthIndex 保持一致) function normalizeAuthIndexValue(value: unknown): string | null { @@ -406,6 +405,9 @@ export function AuthFilesPage() { {} ); const [antigravityLoading, setAntigravityLoading] = useState(false); + const [antigravityLoadingScope, setAntigravityLoadingScope] = useState< + 'page' | 'all' | null + >(null); // 详情弹窗相关 const [detailModalOpen, setDetailModalOpen] = useState(false); @@ -572,73 +574,77 @@ export function AuthFilesPage() { [t] ); - const loadAntigravityQuota = useCallback(async () => { - if (antigravityLoadingRef.current) return; - antigravityLoadingRef.current = true; - const requestId = ++antigravityRequestIdRef.current; - setAntigravityLoading(true); + const loadAntigravityQuota = useCallback( + async (targets: AuthFileItem[], scope: 'page' | 'all') => { + if (antigravityLoadingRef.current) return; + antigravityLoadingRef.current = true; + const requestId = ++antigravityRequestIdRef.current; + setAntigravityLoading(true); + setAntigravityLoadingScope(scope); - try { - if (antigravityFiles.length === 0) { - setAntigravityQuota({}); - return; - } + try { + if (targets.length === 0) return; - const loadingState: Record = {}; - antigravityFiles.forEach((file) => { - loadingState[file.name] = { status: 'loading', groups: [] }; - }); - setAntigravityQuota(loadingState); + setAntigravityQuota((prev) => { + const nextState = { ...prev }; + targets.forEach((file) => { + nextState[file.name] = { status: 'loading', groups: [] }; + }); + return nextState; + }); - const results = await Promise.all( - antigravityFiles.map(async (file) => { - const rawAuthIndex = file['auth_index'] ?? file.authIndex; - const authIndex = normalizeAuthIndexValue(rawAuthIndex); - if (!authIndex) { - return { - name: file.name, - status: 'error' as const, - error: t('antigravity_quota.missing_auth_index') - }; - } + const results = await Promise.all( + targets.map(async (file) => { + const rawAuthIndex = file['auth_index'] ?? file.authIndex; + const authIndex = normalizeAuthIndexValue(rawAuthIndex); + if (!authIndex) { + return { + name: file.name, + status: 'error' as const, + error: t('antigravity_quota.missing_auth_index') + }; + } - try { - const groups = await fetchAntigravityQuota(authIndex); - return { name: file.name, status: 'success' as const, groups }; - } catch (err: unknown) { - const message = err instanceof Error ? err.message : t('common.unknown_error'); - return { name: file.name, status: 'error' as const, error: message }; - } - }) - ); + try { + const groups = await fetchAntigravityQuota(authIndex); + return { name: file.name, status: 'success' as const, groups }; + } catch (err: unknown) { + const message = err instanceof Error ? err.message : t('common.unknown_error'); + return { name: file.name, status: 'error' as const, error: message }; + } + }) + ); - if (requestId !== antigravityRequestIdRef.current) return; + if (requestId !== antigravityRequestIdRef.current) return; - const nextState: Record = {}; - results.forEach((result) => { - if (result.status === 'success') { - nextState[result.name] = { - status: 'success', - groups: result.groups - }; - } else { - nextState[result.name] = { - status: 'error', - groups: [], - error: result.error - }; + setAntigravityQuota((prev) => { + const nextState = { ...prev }; + results.forEach((result) => { + if (result.status === 'success') { + nextState[result.name] = { + status: 'success', + groups: result.groups + }; + } else { + nextState[result.name] = { + status: 'error', + groups: [], + error: result.error + }; + } + }); + return nextState; + }); + } finally { + if (requestId === antigravityRequestIdRef.current) { + setAntigravityLoading(false); + setAntigravityLoadingScope(null); + antigravityLoadingRef.current = false; } - }); - setAntigravityQuota(nextState); - antigravityQuotaCache = nextState; - antigravityQuotaCacheLoaded = true; - } finally { - if (requestId === antigravityRequestIdRef.current) { - setAntigravityLoading(false); - antigravityLoadingRef.current = false; } - } - }, [antigravityFiles, fetchAntigravityQuota, t]); + }, + [fetchAntigravityQuota, t] + ); useEffect(() => { loadFiles(); @@ -651,17 +657,17 @@ export function AuthFilesPage() { setAntigravityQuota({}); return; } - if (antigravityQuotaCacheLoaded) { - setAntigravityQuota(antigravityQuotaCache); - return; - } - loadAntigravityQuota(); - }, [ - antigravityFiles, - loadAntigravityQuota, - antigravityQuotaCacheLoaded, - antigravityQuotaCache - ]); + setAntigravityQuota((prev) => { + const nextState: Record = {}; + antigravityFiles.forEach((file) => { + const cached = prev[file.name]; + if (cached) { + nextState[file.name] = cached; + } + }); + return nextState; + }); + }, [antigravityFiles]); // 定时刷新状态数据(每240秒) useInterval(loadKeyStats, 240_000); @@ -1201,9 +1207,7 @@ export function AuthFilesPage() { const displayType = item.type || item.provider || 'antigravity'; const typeColor = getTypeColor(displayType); const quotaState = antigravityQuota[item.name]; - const quotaStatus = - quotaState?.status ?? - (antigravityLoading || !antigravityQuotaCacheLoaded ? 'loading' : 'idle'); + const quotaStatus = quotaState?.status ?? 'idle'; const quotaGroups = quotaState?.groups ?? []; return ( @@ -1397,15 +1401,26 @@ export function AuthFilesPage() { - {t('common.refresh')} - +
+ + +
} > {antigravityFiles.length === 0 ? (