import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { Provider, UsageScript } from "../types"; import { AppType } from "../lib/tauri-api"; import { Play, Edit3, Trash2, CheckCircle2, Users, Check, BarChart3 } from "lucide-react"; import { buttonStyles, cardStyles, badgeStyles, cn } from "../lib/styles"; import UsageFooter from "./UsageFooter"; import UsageScriptModal from "./UsageScriptModal"; // 不再在列表中显示分类徽章,避免造成困惑 interface ProviderListProps { providers: Record; currentProviderId: string; onSwitch: (id: string) => void; onDelete: (id: string) => void; onEdit: (id: string) => void; appType: AppType; onNotify?: ( message: string, type: "success" | "error", duration?: number, ) => void; onProvidersUpdated?: () => Promise; } const ProviderList: React.FC = ({ providers, currentProviderId, onSwitch, onDelete, onEdit, appType, onNotify, onProvidersUpdated, }) => { const { t, i18n } = useTranslation(); const [usageModalProviderId, setUsageModalProviderId] = useState(null); // 提取API地址(兼容不同供应商配置:Claude env / Codex TOML) const getApiUrl = (provider: Provider): string => { try { const cfg = provider.settingsConfig; // Claude/Anthropic: 从 env 中读取 if (cfg?.env?.ANTHROPIC_BASE_URL) { return cfg.env.ANTHROPIC_BASE_URL; } // Codex: 从 TOML 配置中解析 base_url if (typeof cfg?.config === "string" && cfg.config.includes("base_url")) { // 支持单/双引号 const match = cfg.config.match(/base_url\s*=\s*(['"])([^'\"]+)\1/); if (match && match[2]) return match[2]; } return t("provider.notConfigured"); } catch { return t("provider.configError"); } }; const handleUrlClick = async (url: string) => { try { await window.api.openExternal(url); } catch (error) { console.error(t("console.openLinkFailed"), error); onNotify?.( `${t("console.openLinkFailed")}: ${String(error)}`, "error", 4000, ); } }; // 列表页不再提供 Claude 插件按钮,统一在“设置”中控制 // 处理用量配置保存 const handleSaveUsageScript = async (providerId: string, script: UsageScript) => { try { const provider = providers[providerId]; const updatedProvider = { ...provider, meta: { ...provider.meta, usage_script: script, }, }; await window.api.updateProvider(updatedProvider, appType); onNotify?.("用量查询配置已保存", "success", 2000); // 重新加载供应商列表,触发 UsageFooter 的 useEffect if (onProvidersUpdated) { await onProvidersUpdated(); } } catch (error) { console.error("保存用量配置失败:", error); onNotify?.("保存失败", "error"); } }; // 对供应商列表进行排序 const sortedProviders = Object.values(providers).sort((a, b) => { // 按添加时间排序 // 没有时间戳的视为最早添加的(排在最前面) // 有时间戳的按时间升序排列 const timeA = a.createdAt || 0; const timeB = b.createdAt || 0; // 如果都没有时间戳,按名称排序 if (timeA === 0 && timeB === 0) { const locale = i18n.language === "zh" ? "zh-CN" : "en-US"; return a.name.localeCompare(b.name, locale); } // 如果只有一个没有时间戳,没有时间戳的排在前面 if (timeA === 0) return -1; if (timeB === 0) return 1; // 都有时间戳,按时间升序 return timeA - timeB; }); return (
{sortedProviders.length === 0 ? (

{t("provider.noProviders")}

{t("provider.noProvidersDescription")}

) : (
{sortedProviders.map((provider) => { const isCurrent = provider.id === currentProviderId; const apiUrl = getApiUrl(provider); return (

{provider.name}

{/* 分类徽章已移除 */}
{t("provider.currentlyUsing")}
{provider.websiteUrl ? ( ) : ( {apiUrl} )}
{/* 新增:用量配置按钮 */}
{/* 用量信息 Footer */}
); })}
)} {/* 用量配置模态框 */} {usageModalProviderId && providers[usageModalProviderId] && ( setUsageModalProviderId(null)} onSave={(script) => handleSaveUsageScript(usageModalProviderId, script) } onNotify={onNotify} /> )}
); }; export default ProviderList;