From 99c910e58e8d02138ce619f2cf5c94fd17c8a061 Mon Sep 17 00:00:00 2001 From: Dex Miller Date: Mon, 12 Jan 2026 16:26:17 +0800 Subject: [PATCH] fix(provider): persist endpoint auto-select state (#611) - Add endpointAutoSelect field to ProviderMeta for persistence - Lift autoSelect state from EndpointSpeedTest to ProviderForm - Save auto-select preference when provider is saved - Restore preference when editing existing provider Fixes https://github.com/farion1231/cc-switch/issues/589 --- src-tauri/src/provider.rs | 3 +++ .../providers/forms/ClaudeFormFields.tsx | 6 ++++++ .../providers/forms/CodexFormFields.tsx | 6 ++++++ .../providers/forms/EndpointSpeedTest.tsx | 9 +++++++-- .../providers/forms/GeminiFormFields.tsx | 6 ++++++ src/components/providers/forms/ProviderForm.tsx | 17 +++++++++++++++++ src/types.ts | 2 ++ 7 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index 13eebce51..30ff410b4 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -147,6 +147,9 @@ pub struct ProviderMeta { /// 用量查询脚本配置 #[serde(skip_serializing_if = "Option::is_none")] pub usage_script: Option, + /// 请求地址管理:测速后自动选择最佳端点 + #[serde(rename = "endpointAutoSelect", skip_serializing_if = "Option::is_none")] + pub endpoint_auto_select: Option, /// 合作伙伴标记(前端使用 isPartner,保持字段名一致) #[serde(rename = "isPartner", skip_serializing_if = "Option::is_none")] pub is_partner: Option, diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index e3e1d5311..d16dd9669 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -36,6 +36,8 @@ interface ClaudeFormFieldsProps { isEndpointModalOpen: boolean; onEndpointModalToggle: (open: boolean) => void; onCustomEndpointsChange?: (endpoints: string[]) => void; + autoSelect: boolean; + onAutoSelectChange: (checked: boolean) => void; // Model Selector shouldShowModelSelector: boolean; @@ -83,6 +85,8 @@ export function ClaudeFormFields({ isEndpointModalOpen, onEndpointModalToggle, onCustomEndpointsChange, + autoSelect, + onAutoSelectChange, shouldShowModelSelector, claudeModel, reasoningModel, @@ -170,6 +174,8 @@ export function ClaudeFormFields({ initialEndpoints={speedTestEndpoints} visible={isEndpointModalOpen} onClose={() => onEndpointModalToggle(false)} + autoSelect={autoSelect} + onAutoSelectChange={onAutoSelectChange} onCustomEndpointsChange={onCustomEndpointsChange} /> )} diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index f8cada485..c527b991f 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -25,6 +25,8 @@ interface CodexFormFieldsProps { isEndpointModalOpen: boolean; onEndpointModalToggle: (open: boolean) => void; onCustomEndpointsChange?: (endpoints: string[]) => void; + autoSelect: boolean; + onAutoSelectChange: (checked: boolean) => void; // Model Name shouldShowModelField?: boolean; @@ -50,6 +52,8 @@ export function CodexFormFields({ isEndpointModalOpen, onEndpointModalToggle, onCustomEndpointsChange, + autoSelect, + onAutoSelectChange, shouldShowModelField = true, modelName = "", onModelNameChange, @@ -130,6 +134,8 @@ export function CodexFormFields({ initialEndpoints={speedTestEndpoints} visible={isEndpointModalOpen} onClose={() => onEndpointModalToggle(false)} + autoSelect={autoSelect} + onAutoSelectChange={onAutoSelectChange} onCustomEndpointsChange={onCustomEndpointsChange} /> )} diff --git a/src/components/providers/forms/EndpointSpeedTest.tsx b/src/components/providers/forms/EndpointSpeedTest.tsx index ebeb77129..011116dd4 100644 --- a/src/components/providers/forms/EndpointSpeedTest.tsx +++ b/src/components/providers/forms/EndpointSpeedTest.tsx @@ -30,6 +30,8 @@ interface EndpointSpeedTestProps { initialEndpoints: EndpointCandidate[]; visible?: boolean; onClose: () => void; + autoSelect: boolean; + onAutoSelectChange: (checked: boolean) => void; // 新建模式:当自定义端点列表变化时回传(仅包含 isCustom 的条目) // 编辑模式:不使用此回调,端点直接保存到后端 onCustomEndpointsChange?: (urls: string[]) => void; @@ -85,6 +87,8 @@ const EndpointSpeedTest: React.FC = ({ initialEndpoints, visible = true, onClose, + autoSelect, + onAutoSelectChange, onCustomEndpointsChange, }) => { const { t } = useTranslation(); @@ -93,7 +97,6 @@ const EndpointSpeedTest: React.FC = ({ ); const [customUrl, setCustomUrl] = useState(""); const [addError, setAddError] = useState(null); - const [autoSelect, setAutoSelect] = useState(true); const [isTesting, setIsTesting] = useState(false); const [lastError, setLastError] = useState(null); const [isSaving, setIsSaving] = useState(false); @@ -488,7 +491,9 @@ const EndpointSpeedTest: React.FC = ({ setAutoSelect(event.target.checked)} + onChange={(event) => { + onAutoSelectChange(event.target.checked); + }} className="h-3.5 w-3.5 rounded border-border-default bg-background text-primary focus:ring-2 focus:ring-primary/20" /> {t("endpointTest.autoSelect")} diff --git a/src/components/providers/forms/GeminiFormFields.tsx b/src/components/providers/forms/GeminiFormFields.tsx index f66bdfcf1..b0400ec66 100644 --- a/src/components/providers/forms/GeminiFormFields.tsx +++ b/src/components/providers/forms/GeminiFormFields.tsx @@ -29,6 +29,8 @@ interface GeminiFormFieldsProps { isEndpointModalOpen: boolean; onEndpointModalToggle: (open: boolean) => void; onCustomEndpointsChange: (endpoints: string[]) => void; + autoSelect: boolean; + onAutoSelectChange: (checked: boolean) => void; // Model shouldShowModelField: boolean; @@ -55,6 +57,8 @@ export function GeminiFormFields({ isEndpointModalOpen, onEndpointModalToggle, onCustomEndpointsChange, + autoSelect, + onAutoSelectChange, shouldShowModelField, model, onModelChange, @@ -142,6 +146,8 @@ export function GeminiFormFields({ initialEndpoints={speedTestEndpoints} visible={isEndpointModalOpen} onClose={() => onEndpointModalToggle(false)} + autoSelect={autoSelect} + onAutoSelectChange={onAutoSelectChange} onCustomEndpointsChange={onCustomEndpointsChange} /> )} diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index cb97473d5..40cd688a0 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -124,6 +124,9 @@ export function ProviderForm({ return []; }, ); + const [endpointAutoSelect, setEndpointAutoSelect] = useState( + () => initialData?.meta?.endpointAutoSelect ?? true, + ); // 使用 category hook const { category } = useProviderCategory({ @@ -141,6 +144,7 @@ export function ProviderForm({ if (!initialData) { setDraftCustomEndpoints([]); } + setEndpointAutoSelect(initialData?.meta?.endpointAutoSelect ?? true); }, [appId, initialData]); const defaultValues: ProviderFormData = useMemo( @@ -647,6 +651,13 @@ export function ProviderForm({ } } + const baseMeta: ProviderMeta | undefined = + payload.meta ?? (initialData?.meta ? { ...initialData.meta } : undefined); + payload.meta = { + ...(baseMeta ?? {}), + endpointAutoSelect, + }; + onSubmit(payload); }; @@ -856,6 +867,8 @@ export function ProviderForm({ onCustomEndpointsChange={ isEditMode ? undefined : setDraftCustomEndpoints } + autoSelect={endpointAutoSelect} + onAutoSelectChange={setEndpointAutoSelect} shouldShowModelSelector={category !== "official"} claudeModel={claudeModel} reasoningModel={reasoningModel} @@ -889,6 +902,8 @@ export function ProviderForm({ onCustomEndpointsChange={ isEditMode ? undefined : setDraftCustomEndpoints } + autoSelect={endpointAutoSelect} + onAutoSelectChange={setEndpointAutoSelect} shouldShowModelField={category !== "official"} modelName={codexModelName} onModelNameChange={handleCodexModelNameChange} @@ -917,6 +932,8 @@ export function ProviderForm({ isEndpointModalOpen={isEndpointModalOpen} onEndpointModalToggle={setIsEndpointModalOpen} onCustomEndpointsChange={setDraftCustomEndpoints} + autoSelect={endpointAutoSelect} + onAutoSelectChange={setEndpointAutoSelect} shouldShowModelField={true} model={geminiModel} onModelChange={handleGeminiModelChange} diff --git a/src/types.ts b/src/types.ts index 2761319b8..c234963c4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -92,6 +92,8 @@ export interface ProviderMeta { custom_endpoints?: Record; // 用量查询脚本配置 usage_script?: UsageScript; + // 请求地址管理:测速后自动选择最佳端点 + endpointAutoSelect?: boolean; // 是否为官方合作伙伴 isPartner?: boolean; // 合作伙伴促销 key(用于后端识别 PackyCode 等)