diff --git a/src/App.tsx b/src/App.tsx index 713fc8e1e..febd4fa34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -425,10 +425,19 @@ function App() { const { provider, action } = confirmAction; if (action === "remove") { + // Remove from live config only (for additive mode apps like OpenCode/OpenClaw) + // Does NOT delete from database - provider remains in the list await providersApi.removeFromLiveConfig(provider.id, activeApp); - await queryClient.invalidateQueries({ - queryKey: ["opencodeLiveProviderIds"], - }); + // Invalidate queries to refresh the isInConfig state + if (activeApp === "opencode") { + await queryClient.invalidateQueries({ + queryKey: ["opencodeLiveProviderIds"], + }); + } else if (activeApp === "openclaw") { + await queryClient.invalidateQueries({ + queryKey: ["openclawLiveProviderIds"], + }); + } toast.success( t("notifications.removeFromConfigSuccess", { defaultValue: "已从配置移除", @@ -642,7 +651,7 @@ function App() { setConfirmAction({ provider, action: "delete" }) } onRemoveFromConfig={ - activeApp === "opencode" + activeApp === "opencode" || activeApp === "openclaw" ? (provider) => setConfirmAction({ provider, action: "remove" }) : undefined diff --git a/src/components/providers/ProviderActions.tsx b/src/components/providers/ProviderActions.tsx index e1184fefc..b54c04a4a 100644 --- a/src/components/providers/ProviderActions.tsx +++ b/src/components/providers/ProviderActions.tsx @@ -62,10 +62,13 @@ export function ProviderActions({ const { t } = useTranslation(); const iconButtonClass = "h-8 w-8 p-1"; - const isOpenCodeMode = appId === "opencode" && !isOmo; + // 累加模式应用(OpenCode 非 OMO 和 OpenClaw) + const isAdditiveMode = + (appId === "opencode" && !isOmo) || appId === "openclaw"; + // 故障转移模式下的按钮逻辑(累加模式和 OMO 应用不支持故障转移) const isFailoverMode = - !isOpenCodeMode && !isOmo && isAutoFailoverEnabled && onToggleFailover; + !isAdditiveMode && !isOmo && isAutoFailoverEnabled && onToggleFailover; const handleMainButtonClick = () => { if (isOmo) { @@ -74,7 +77,8 @@ export function ProviderActions({ } else { onSwitch(); } - } else if (isOpenCodeMode) { + } else if (isAdditiveMode) { + // 累加模式:切换配置状态(添加/移除) if (isInConfig) { if (onRemoveFromConfig) { onRemoveFromConfig(); @@ -112,7 +116,8 @@ export function ProviderActions({ }; } - if (isOpenCodeMode) { + // 累加模式(OpenCode 非 OMO / OpenClaw) + if (isAdditiveMode) { if (isInConfig) { return { disabled: false, @@ -180,7 +185,7 @@ export function ProviderActions({ const canDelete = isOmo ? !(isLastOmo && isCurrent) - : isOpenCodeMode + : isAdditiveMode ? true : !isCurrent; diff --git a/src/components/providers/ProviderCard.tsx b/src/components/providers/ProviderCard.tsx index 67310b45b..2a3a2946b 100644 --- a/src/components/providers/ProviderCard.tsx +++ b/src/components/providers/ProviderCard.tsx @@ -133,7 +133,10 @@ export function ProviderCard({ const usageEnabled = provider.meta?.usage_script?.enabled ?? false; - const shouldAutoQuery = appId === "opencode" ? isInConfig : isCurrent; + // 获取用量数据以判断是否有多套餐 + // 累加模式应用(OpenCode/OpenClaw):使用 isInConfig 代替 isCurrent + const shouldAutoQuery = + appId === "opencode" || appId === "openclaw" ? isInConfig : isCurrent; const autoQueryInterval = shouldAutoQuery ? provider.meta?.usage_script?.autoQueryInterval || 0 : 0; @@ -176,9 +179,14 @@ export function ProviderCard({ onOpenWebsite(displayUrl); }; + // 判断是否是"当前使用中"的供应商 + // - OMO 供应商:使用 isCurrent + // - 累加模式应用(OpenCode 非 OMO / OpenClaw):不存在"当前"概念,始终返回 false + // - 故障转移模式:代理实际使用的供应商(activeProviderId) + // - 普通模式:isCurrent const isActiveProvider = isOmo ? isCurrent - : appId === "opencode" + : appId === "opencode" || appId === "openclaw" ? false : isAutoFailoverEnabled ? activeProviderId === provider.id diff --git a/src/components/providers/ProviderList.tsx b/src/components/providers/ProviderList.tsx index 7b1ab9eae..ebc4b7cf2 100644 --- a/src/components/providers/ProviderList.tsx +++ b/src/components/providers/ProviderList.tsx @@ -84,12 +84,25 @@ export function ProviderList({ enabled: appId === "opencode", }); + // OpenClaw: 查询 live 配置中的供应商 ID 列表,用于判断 isInConfig + const { data: openclawLiveIds } = useQuery({ + queryKey: ["openclawLiveProviderIds"], + queryFn: () => providersApi.getOpenClawLiveProviderIds(), + enabled: appId === "openclaw", + }); + + // 判断供应商是否已添加到配置(累加模式应用:OpenCode/OpenClaw) const isProviderInConfig = useCallback( (providerId: string): boolean => { - if (appId !== "opencode") return true; // 非 OpenCode 应用始终返回 true - return opencodeLiveIds?.includes(providerId) ?? false; + if (appId === "opencode") { + return opencodeLiveIds?.includes(providerId) ?? false; + } + if (appId === "openclaw") { + return openclawLiveIds?.includes(providerId) ?? false; + } + return true; // 其他应用始终返回 true }, - [appId, opencodeLiveIds], + [appId, opencodeLiveIds, openclawLiveIds], ); const { data: isAutoFailoverEnabled } = useAutoFailoverEnabled(appId); diff --git a/src/lib/api/providers.ts b/src/lib/api/providers.ts index dd154b4e5..d78ed0224 100644 --- a/src/lib/api/providers.ts +++ b/src/lib/api/providers.ts @@ -98,6 +98,14 @@ export const providersApi = { async getOpenCodeLiveProviderIds(): Promise { return await invoke("get_opencode_live_provider_ids"); }, + + /** + * 获取 OpenClaw live 配置中的供应商 ID 列表 + * 用于前端判断供应商是否已添加到 openclaw.json + */ + async getOpenClawLiveProviderIds(): Promise { + return await invoke("get_openclaw_live_provider_ids"); + }, }; // ============================================================================