diff --git a/src/pages/AiProvidersOpenAIEditLayout.tsx b/src/pages/AiProvidersOpenAIEditLayout.tsx index 89fe536..98a8547 100644 --- a/src/pages/AiProvidersOpenAIEditLayout.tsx +++ b/src/pages/AiProvidersOpenAIEditLayout.tsx @@ -148,6 +148,7 @@ export function AiProvidersOpenAIEditLayout() { const config = useConfigStore((state) => state.config); const fetchConfig = useConfigStore((state) => state.fetchConfig); + const updateConfigValue = useConfigStore((state) => state.updateConfigValue); const isCacheValid = useConfigStore((state) => state.isCacheValid); const [providers, setProviders] = useState( @@ -258,15 +259,25 @@ export function AiProvidersOpenAIEditLayout() { setLoading(true); } - fetchConfig('openai-compatibility') + providersApi + .getOpenAIProviders() .then((value) => { if (cancelled) return; - setProviders(Array.isArray(value) ? (value as OpenAIProviderConfig[]) : []); + const nextProviders = value || []; + setProviders(nextProviders); + updateConfigValue('openai-compatibility', nextProviders); }) - .catch((err: unknown) => { + .catch(async (err: unknown) => { if (cancelled) return; - const message = getErrorMessage(err) || t('notification.refresh_failed'); - showNotification(`${t('notification.load_failed')}: ${message}`, 'error'); + try { + const fallback = await fetchConfig('openai-compatibility'); + if (cancelled) return; + setProviders(Array.isArray(fallback) ? (fallback as OpenAIProviderConfig[]) : []); + } catch { + if (cancelled) return; + const message = getErrorMessage(err) || t('notification.refresh_failed'); + showNotification(`${t('notification.load_failed')}: ${message}`, 'error'); + } }) .finally(() => { if (cancelled) return; @@ -276,7 +287,7 @@ export function AiProvidersOpenAIEditLayout() { return () => { cancelled = true; }; - }, [fetchConfig, isCacheValid, showNotification, t]); + }, [fetchConfig, isCacheValid, showNotification, t, updateConfigValue]); useEffect(() => { if (loading) return; @@ -481,15 +492,13 @@ export function AiProvidersOpenAIEditLayout() { let syncedProviders = nextList; try { - const latest = await fetchConfig('openai-compatibility', true); - if (Array.isArray(latest)) { - syncedProviders = latest as OpenAIProviderConfig[]; - } + syncedProviders = await providersApi.getOpenAIProviders(); } catch { // 保存成功后刷新失败时,回退到本地计算结果,避免页面数据为空或回退 } setProviders(syncedProviders); + updateConfigValue('openai-compatibility', syncedProviders); showNotification( editIndex !== null ? t('notification.openai_provider_updated') @@ -508,7 +517,6 @@ export function AiProvidersOpenAIEditLayout() { allowNextNavigation, draftKey, editIndex, - fetchConfig, form, handleBack, providers, @@ -516,6 +524,7 @@ export function AiProvidersOpenAIEditLayout() { showNotification, t, testModel, + updateConfigValue, ]); return ( diff --git a/src/pages/AiProvidersPage.tsx b/src/pages/AiProvidersPage.tsx index 8a3c437..dd76570 100644 --- a/src/pages/AiProvidersPage.tsx +++ b/src/pages/AiProvidersPage.tsx @@ -89,10 +89,11 @@ export function AiProvidersPage() { } setError(''); try { - const [configResult, vertexResult, ampcodeResult] = await Promise.allSettled([ + const [configResult, vertexResult, ampcodeResult, openaiResult] = await Promise.allSettled([ fetchConfig(), providersApi.getVertexConfigs(), ampcodeApi.getAmpcode(), + providersApi.getOpenAIProviders(), ]); if (configResult.status !== 'fulfilled') { @@ -116,6 +117,12 @@ export function AiProvidersPage() { updateConfigValue('ampcode', ampcodeResult.value); clearCache('ampcode'); } + + if (openaiResult.status === 'fulfilled') { + setOpenaiProviders(openaiResult.value || []); + updateConfigValue('openai-compatibility', openaiResult.value || []); + clearCache('openai-compatibility'); + } } catch (err: unknown) { const message = getErrorMessage(err) || t('notification.refresh_failed'); setError(message); diff --git a/src/pages/UsagePage.tsx b/src/pages/UsagePage.tsx index 80034c6..0ee7f79 100644 --- a/src/pages/UsagePage.tsx +++ b/src/pages/UsagePage.tsx @@ -16,7 +16,9 @@ import { LoadingSpinner } from '@/components/ui/LoadingSpinner'; import { Select } from '@/components/ui/Select'; import { useMediaQuery } from '@/hooks/useMediaQuery'; import { useHeaderRefresh } from '@/hooks/useHeaderRefresh'; +import { providersApi } from '@/services/api'; import { useThemeStore, useConfigStore } from '@/stores'; +import type { OpenAIProviderConfig } from '@/types'; import { StatCards, UsageChart, @@ -121,6 +123,11 @@ export function UsagePage() { const resolvedTheme = useThemeStore((state) => state.resolvedTheme); const isDark = resolvedTheme === 'dark'; const config = useConfigStore((state) => state.config); + const openaiCompatibilityConfig = config?.openaiCompatibility; + const [openaiProvidersWithAuthIndex, setOpenaiProvidersWithAuthIndex] = useState<{ + source: OpenAIProviderConfig[] | undefined; + providers: OpenAIProviderConfig[]; + } | null>(null); // Data hook const { @@ -145,6 +152,32 @@ export function UsagePage() { const [chartLines, setChartLines] = useState(loadChartLines); const [timeRange, setTimeRange] = useState(loadTimeRange); + useEffect(() => { + let cancelled = false; + const source = openaiCompatibilityConfig; + + providersApi + .getOpenAIProviders() + .then((providers) => { + if (cancelled) return; + setOpenaiProvidersWithAuthIndex({ source, providers: providers || [] }); + }) + .catch(() => { + if (cancelled) return; + setOpenaiProvidersWithAuthIndex(null); + }); + + return () => { + cancelled = true; + }; + }, [openaiCompatibilityConfig]); + + const openaiProviderState = openaiProvidersWithAuthIndex; + const openaiProvidersForUsage = + openaiProviderState && openaiProviderState.source === openaiCompatibilityConfig + ? openaiProviderState.providers + : openaiCompatibilityConfig ?? []; + const timeRangeOptions = useMemo( () => TIME_RANGE_OPTIONS.map((opt) => ({ @@ -372,7 +405,7 @@ export function UsagePage() { claudeConfigs={config?.claudeApiKeys || []} codexConfigs={config?.codexApiKeys || []} vertexConfigs={config?.vertexApiKeys || []} - openaiProviders={config?.openaiCompatibility || []} + openaiProviders={openaiProvidersForUsage} /> {/* Credential Stats */} @@ -383,7 +416,7 @@ export function UsagePage() { claudeConfigs={config?.claudeApiKeys || []} codexConfigs={config?.codexApiKeys || []} vertexConfigs={config?.vertexApiKeys || []} - openaiProviders={config?.openaiCompatibility || []} + openaiProviders={openaiProvidersForUsage} /> {/* Price Settings */}