diff --git a/src/components/ProviderForm/ApiKeyInput.tsx b/src/components/providers/forms/ApiKeyInput.tsx
similarity index 100%
rename from src/components/ProviderForm/ApiKeyInput.tsx
rename to src/components/providers/forms/ApiKeyInput.tsx
diff --git a/src/components/ProviderForm/ClaudeConfigEditor.tsx b/src/components/providers/forms/ClaudeConfigEditor.tsx
similarity index 98%
rename from src/components/ProviderForm/ClaudeConfigEditor.tsx
rename to src/components/providers/forms/ClaudeConfigEditor.tsx
index 1def4e6..a340f90 100644
--- a/src/components/ProviderForm/ClaudeConfigEditor.tsx
+++ b/src/components/providers/forms/ClaudeConfigEditor.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react";
-import JsonEditor from "../JsonEditor";
+import JsonEditor from "@/components/JsonEditor";
import { X, Save } from "lucide-react";
-import { isLinux } from "../../lib/platform";
+import { isLinux } from "@/lib/platform";
import { useTranslation } from "react-i18next";
interface ClaudeConfigEditorProps {
diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx
index e02c36b..42f9ee7 100644
--- a/src/components/providers/forms/ClaudeFormFields.tsx
+++ b/src/components/providers/forms/ClaudeFormFields.tsx
@@ -1,10 +1,9 @@
import { useTranslation } from "react-i18next";
import { FormLabel } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
-import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput";
-import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest";
-import KimiModelSelector from "@/components/ProviderForm/KimiModelSelector";
-import { Zap } from "lucide-react";
+import EndpointSpeedTest from "./EndpointSpeedTest";
+import KimiModelSelector from "./KimiModelSelector";
+import { ApiKeySection, EndpointField } from "./shared";
import type { ProviderCategory } from "@/types";
import type { TemplateValueConfig } from "@/config/providerPresets";
@@ -90,38 +89,13 @@ export function ClaudeFormFields({
<>
{/* API Key 输入框 */}
{shouldShowApiKey && (
-
-
- {/* API Key 获取链接 */}
- {shouldShowApiKeyLink && websiteUrl && (
-
- )}
-
+
)}
{/* 模板变量输入 */}
@@ -161,40 +135,19 @@ export function ClaudeFormFields({
{/* Base URL 输入框 */}
{shouldShowSpeedTest && (
-
-
-
- {t("providerForm.apiEndpoint", { defaultValue: "API 端点" })}
-
-
-
-
onBaseUrlChange(e.target.value)}
- placeholder={t("providerForm.apiEndpointPlaceholder", {
- defaultValue: "https://api.example.com",
- })}
- autoComplete="off"
- />
-
-
- {t("providerForm.apiHint", {
- defaultValue: "API 端点地址用于连接服务器",
- })}
-
-
-
+ onEndpointModalToggle(true)}
+ />
)}
{/* 端点测速弹窗 */}
diff --git a/src/components/ProviderForm/CodexConfigEditor.tsx b/src/components/providers/forms/CodexConfigEditor.tsx
similarity index 99%
rename from src/components/ProviderForm/CodexConfigEditor.tsx
rename to src/components/providers/forms/CodexConfigEditor.tsx
index eae23be..a0c9fb4 100644
--- a/src/components/ProviderForm/CodexConfigEditor.tsx
+++ b/src/components/providers/forms/CodexConfigEditor.tsx
@@ -2,13 +2,13 @@ import React, { useState, useEffect, useRef } from "react";
import { X, Save } from "lucide-react";
-import { isLinux } from "../../lib/platform";
+import { isLinux } from "@/lib/platform";
import { useTranslation } from "react-i18next";
import {
generateThirdPartyAuth,
generateThirdPartyConfig,
-} from "../../config/codexProviderPresets";
+} from "@/config/codexProviderPresets";
interface CodexConfigEditorProps {
authValue: string;
diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx
index 6672779..21f2852 100644
--- a/src/components/providers/forms/CodexFormFields.tsx
+++ b/src/components/providers/forms/CodexFormFields.tsx
@@ -1,9 +1,6 @@
import { useTranslation } from "react-i18next";
-import { FormLabel } from "@/components/ui/form";
-import { Input } from "@/components/ui/input";
-import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput";
-import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest";
-import { Zap } from "lucide-react";
+import EndpointSpeedTest from "./EndpointSpeedTest";
+import { ApiKeySection, EndpointField } from "./shared";
import type { ProviderCategory } from "@/types";
interface EndpointCandidate {
@@ -49,77 +46,39 @@ export function CodexFormFields({
return (
<>
{/* Codex API Key 输入框 */}
-
-
- {/* Codex API Key 获取链接 */}
- {shouldShowApiKeyLink && websiteUrl && (
-
- )}
-
+
{/* Codex Base URL 输入框 */}
{shouldShowSpeedTest && (
-
-
-
- {t("codexConfig.apiUrlLabel", { defaultValue: "API 端点" })}
-
-
-
-
onBaseUrlChange(e.target.value)}
- placeholder={t("providerForm.codexApiEndpointPlaceholder", {
- defaultValue: "https://api.example.com/v1",
- })}
- autoComplete="off"
- />
-
-
- {t("providerForm.codexApiHint", {
- defaultValue: "Codex API 端点地址",
- })}
-
-
-
+ onEndpointModalToggle(true)}
+ />
)}
{/* 端点测速弹窗 - Codex */}
diff --git a/src/components/ProviderForm/EndpointSpeedTest.tsx b/src/components/providers/forms/EndpointSpeedTest.tsx
similarity index 100%
rename from src/components/ProviderForm/EndpointSpeedTest.tsx
rename to src/components/providers/forms/EndpointSpeedTest.tsx
diff --git a/src/components/ProviderForm/KimiModelSelector.tsx b/src/components/providers/forms/KimiModelSelector.tsx
similarity index 100%
rename from src/components/ProviderForm/KimiModelSelector.tsx
rename to src/components/providers/forms/KimiModelSelector.tsx
diff --git a/src/components/ProviderForm/PresetSelector.tsx b/src/components/providers/forms/PresetSelector.tsx
similarity index 100%
rename from src/components/ProviderForm/PresetSelector.tsx
rename to src/components/providers/forms/PresetSelector.tsx
diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx
index c02d7a2..3d4c300 100644
--- a/src/components/providers/forms/ProviderForm.tsx
+++ b/src/components/providers/forms/ProviderForm.tsx
@@ -13,7 +13,7 @@ import {
type CodexProviderPreset,
} from "@/config/codexProviderPresets";
import { applyTemplateValues } from "@/utils/providerConfigUtils";
-import CodexConfigEditor from "@/components/ProviderForm/CodexConfigEditor";
+import CodexConfigEditor from "./CodexConfigEditor";
import { CommonConfigEditor } from "./CommonConfigEditor";
import { ProviderPresetSelector } from "./ProviderPresetSelector";
import { BasicFormFields } from "./BasicFormFields";
diff --git a/src/components/providers/forms/shared/ApiKeySection.tsx b/src/components/providers/forms/shared/ApiKeySection.tsx
new file mode 100644
index 0000000..4b41a69
--- /dev/null
+++ b/src/components/providers/forms/shared/ApiKeySection.tsx
@@ -0,0 +1,76 @@
+import { useTranslation } from "react-i18next";
+import ApiKeyInput from "../ApiKeyInput";
+import type { ProviderCategory } from "@/types";
+
+interface ApiKeySectionProps {
+ id?: string;
+ label?: string;
+ value: string;
+ onChange: (value: string) => void;
+ category?: ProviderCategory;
+ shouldShowLink: boolean;
+ websiteUrl: string;
+ placeholder?: {
+ official: string;
+ thirdParty: string;
+ };
+ disabled?: boolean;
+}
+
+export function ApiKeySection({
+ id,
+ label,
+ value,
+ onChange,
+ category,
+ shouldShowLink,
+ websiteUrl,
+ placeholder,
+ disabled,
+}: ApiKeySectionProps) {
+ const { t } = useTranslation();
+
+ const defaultPlaceholder = {
+ official: t("providerForm.officialNoApiKey", {
+ defaultValue: "官方供应商无需 API Key",
+ }),
+ thirdParty: t("providerForm.apiKeyAutoFill", {
+ defaultValue: "输入 API Key,将自动填充到配置",
+ }),
+ };
+
+ const finalPlaceholder = placeholder || defaultPlaceholder;
+
+ return (
+
+
+ {/* API Key 获取链接 */}
+ {shouldShowLink && websiteUrl && (
+
+ )}
+
+ );
+}
diff --git a/src/components/providers/forms/shared/EndpointField.tsx b/src/components/providers/forms/shared/EndpointField.tsx
new file mode 100644
index 0000000..ab5eee1
--- /dev/null
+++ b/src/components/providers/forms/shared/EndpointField.tsx
@@ -0,0 +1,63 @@
+import { useTranslation } from "react-i18next";
+import { FormLabel } from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
+import { Zap } from "lucide-react";
+
+interface EndpointFieldProps {
+ id: string;
+ label: string;
+ value: string;
+ onChange: (value: string) => void;
+ placeholder: string;
+ hint: string;
+ showManageButton?: boolean;
+ onManageClick?: () => void;
+ manageButtonLabel?: string;
+}
+
+export function EndpointField({
+ id,
+ label,
+ value,
+ onChange,
+ placeholder,
+ hint,
+ showManageButton = true,
+ onManageClick,
+ manageButtonLabel,
+}: EndpointFieldProps) {
+ const { t } = useTranslation();
+
+ const defaultManageLabel = t("providerForm.manageAndTest", {
+ defaultValue: "管理和测速",
+ });
+
+ return (
+
+
+ {label}
+ {showManageButton && onManageClick && (
+
+ )}
+
+
onChange(e.target.value)}
+ placeholder={placeholder}
+ autoComplete="off"
+ />
+
+
+ );
+}
diff --git a/src/components/providers/forms/shared/index.ts b/src/components/providers/forms/shared/index.ts
new file mode 100644
index 0000000..f9b85fb
--- /dev/null
+++ b/src/components/providers/forms/shared/index.ts
@@ -0,0 +1,2 @@
+export { ApiKeySection } from "./ApiKeySection";
+export { EndpointField } from "./EndpointField";