From ec1ae7073f78723ed2b4cddc866bc4c03d1b41e3 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Mon, 17 Nov 2025 16:40:28 +0800 Subject: [PATCH] feat(providers): add notes field for provider management - Add notes field to Provider model (backend and frontend) - Display notes with higher priority than URL in provider card - Style notes as non-clickable text to differentiate from URLs - Add notes input field in provider form - Add i18n support (zh/en) for notes field --- src-tauri/src/provider.rs | 4 +++ src/components/providers/ProviderCard.tsx | 31 +++++++++++++++++-- .../providers/forms/BasicFormFields.tsx | 14 +++++++++ .../providers/forms/ProviderForm.tsx | 2 ++ src/i18n/locales/en.json | 2 ++ src/i18n/locales/zh.json | 2 ++ src/lib/schemas/provider.ts | 1 + src/types.ts | 2 ++ 8 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index b6aa159..e3b6298 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -22,6 +22,9 @@ pub struct Provider { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "sortIndex")] pub sort_index: Option, + /// 备注信息 + #[serde(skip_serializing_if = "Option::is_none")] + pub notes: Option, /// 供应商元数据(不写入 live 配置,仅存于 ~/.cc-switch/config.json) #[serde(skip_serializing_if = "Option::is_none")] pub meta: Option, @@ -43,6 +46,7 @@ impl Provider { category: None, created_at: None, sort_index: None, + notes: None, meta: None, } } diff --git a/src/components/providers/ProviderCard.tsx b/src/components/providers/ProviderCard.tsx index bb95b61..9c53333 100644 --- a/src/components/providers/ProviderCard.tsx +++ b/src/components/providers/ProviderCard.tsx @@ -33,10 +33,17 @@ interface ProviderCardProps { } const extractApiUrl = (provider: Provider, fallbackText: string) => { + // 优先级 1: 备注 + if (provider.notes?.trim()) { + return provider.notes.trim(); + } + + // 优先级 2: 官网地址 if (provider.websiteUrl) { return provider.websiteUrl; } + // 优先级 3: 从配置中提取请求地址 const config = provider.settingsConfig; if (config && typeof config === "object") { @@ -83,10 +90,24 @@ export function ProviderCard({ return extractApiUrl(provider, fallbackUrlText); }, [provider, fallbackUrlText]); + // 判断是否为可点击的 URL(备注不可点击) + const isClickableUrl = useMemo(() => { + // 如果有备注,则不可点击 + if (provider.notes?.trim()) { + return false; + } + // 如果显示的是回退文本,也不可点击 + if (displayUrl === fallbackUrlText) { + return false; + } + // 其他情况(官网地址或请求地址)可点击 + return true; + }, [provider.notes, displayUrl, fallbackUrlText]); + const usageEnabled = provider.meta?.usage_script?.enabled ?? false; const handleOpenWebsite = () => { - if (!displayUrl || displayUrl === fallbackUrlText) { + if (!isClickableUrl) { return; } onOpenWebsite(displayUrl); @@ -174,8 +195,14 @@ export function ProviderCard({ diff --git a/src/components/providers/forms/BasicFormFields.tsx b/src/components/providers/forms/BasicFormFields.tsx index daec379..f4e8a21 100644 --- a/src/components/providers/forms/BasicFormFields.tsx +++ b/src/components/providers/forms/BasicFormFields.tsx @@ -46,6 +46,20 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) { )} /> + + ( + + {t("provider.notes")} + + + + + + )} + /> ); } diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index edafc9b..e4d057d 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -74,6 +74,7 @@ interface ProviderFormProps { initialData?: { name?: string; websiteUrl?: string; + notes?: string; settingsConfig?: Record; category?: ProviderCategory; meta?: ProviderMeta; @@ -138,6 +139,7 @@ export function ProviderForm({ () => ({ name: initialData?.name ?? "", websiteUrl: initialData?.websiteUrl ?? "", + notes: initialData?.notes ?? "", settingsConfig: initialData?.settingsConfig ? JSON.stringify(initialData.settingsConfig, null, 2) : appId === "codex" diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b66f491..39cc560 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -83,6 +83,8 @@ "name": "Provider Name", "namePlaceholder": "e.g., Claude Official", "websiteUrl": "Website URL", + "notes": "Notes", + "notesPlaceholder": "e.g., Company dedicated account", "configJson": "Config JSON", "writeCommonConfig": "Write common config", "editCommonConfigButton": "Edit common config", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 6959f59..d998818 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -83,6 +83,8 @@ "name": "供应商名称", "namePlaceholder": "例如:Claude 官方", "websiteUrl": "官网链接", + "notes": "备注", + "notesPlaceholder": "例如:公司专用账号", "configJson": "配置 JSON", "writeCommonConfig": "写入通用配置", "editCommonConfigButton": "编辑通用配置", diff --git a/src/lib/schemas/provider.ts b/src/lib/schemas/provider.ts index 62b203a..d3ce7a7 100644 --- a/src/lib/schemas/provider.ts +++ b/src/lib/schemas/provider.ts @@ -38,6 +38,7 @@ function parseJsonError(error: unknown): string { export const providerSchema = z.object({ name: z.string().min(1, "请填写供应商名称"), websiteUrl: z.string().url("请输入有效的网址").optional().or(z.literal("")), + notes: z.string().optional(), settingsConfig: z .string() .min(1, "请填写配置内容") diff --git a/src/types.ts b/src/types.ts index 40fc5d1..6701676 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,8 @@ export interface Provider { category?: ProviderCategory; createdAt?: number; // 添加时间戳(毫秒) sortIndex?: number; // 排序索引(用于自定义拖拽排序) + // 备注信息 + notes?: string; // 新增:是否为商业合作伙伴 isPartner?: boolean; // 可选:供应商元数据(仅存于 ~/.cc-switch/config.json,不写入 live 配置)