diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index 8d23b2889..f2906a704 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -8,6 +8,7 @@ import { Form, FormField, FormItem, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider"; import type { AppId } from "@/lib/api"; +import { configApi } from "@/lib/api"; import type { ProviderCategory, ProviderMeta, @@ -478,6 +479,8 @@ export function ProviderForm({ handleExtract: handleClaudeExtract, isLoading: isClaudeCommonConfigLoading, finalConfig: claudeFinalConfig, + getPendingCommonConfigSnippet: getPendingClaudeCommonConfigSnippet, + markCommonConfigSaved: markClaudeCommonConfigSaved, } = useCommonConfigSnippet({ settingsConfig: form.getValues("settingsConfig"), onConfigChange: (config) => form.setValue("settingsConfig", config), @@ -498,6 +501,8 @@ export function ProviderForm({ handleExtract: handleCodexExtract, isLoading: isCodexCommonConfigLoading, finalConfig: codexFinalConfig, + getPendingCommonConfigSnippet: getPendingCodexCommonConfigSnippet, + markCommonConfigSaved: markCodexCommonConfigSaved, } = useCodexCommonConfig({ codexConfig, onConfigChange: handleCodexConfigChange, @@ -587,6 +592,8 @@ export function ProviderForm({ handleExtract: handleGeminiExtract, isLoading: isGeminiCommonConfigLoading, finalEnv: geminiFinalEnv, + getPendingCommonConfigSnippet: getPendingGeminiCommonConfigSnippet, + markCommonConfigSaved: markGeminiCommonConfigSaved, } = useGeminiCommonConfig({ envValue: geminiEnv, onEnvChange: handleGeminiEnvChange, @@ -817,7 +824,7 @@ export function ProviderForm({ const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false); - const handleSubmit = (values: ProviderFormData) => { + const handleSubmit = async (values: ProviderFormData) => { // 检查通用配置是否仍在加载中 const isCommonConfigLoading = (appId === "claude" && isClaudeCommonConfigLoading) || @@ -970,6 +977,37 @@ export function ProviderForm({ } } + // 保存待保存的通用配置(延迟保存模式:在表单提交时统一保存) + try { + if (appId === "claude") { + const pendingSnippet = getPendingClaudeCommonConfigSnippet(); + if (pendingSnippet !== null) { + await configApi.setCommonConfigSnippet("claude", pendingSnippet); + markClaudeCommonConfigSaved(); + } + } else if (appId === "codex") { + const pendingSnippet = getPendingCodexCommonConfigSnippet(); + if (pendingSnippet !== null) { + await configApi.setCommonConfigSnippet("codex", pendingSnippet); + markCodexCommonConfigSaved(); + } + } else if (appId === "gemini") { + const pendingSnippet = getPendingGeminiCommonConfigSnippet(); + if (pendingSnippet !== null) { + await configApi.setCommonConfigSnippet("gemini", pendingSnippet); + markGeminiCommonConfigSaved(); + } + } + } catch (error) { + console.error("保存通用配置失败:", error); + toast.error( + t("providerForm.saveCommonConfigFailed", { + defaultValue: "保存通用配置失败", + }), + ); + return; + } + let settingsConfig: string; // Codex: 组合 auth 和 config diff --git a/src/components/providers/forms/hooks/useCodexCommonConfig.ts b/src/components/providers/forms/hooks/useCodexCommonConfig.ts index 63f043a03..af262e9ba 100644 --- a/src/components/providers/forms/hooks/useCodexCommonConfig.ts +++ b/src/components/providers/forms/hooks/useCodexCommonConfig.ts @@ -88,6 +88,12 @@ export interface UseCodexCommonConfigReturn { handleExtract: () => Promise; /** 最终配置(运行时合并结果,只读) */ finalConfig: string; + /** 是否有待保存的通用配置变更 */ + hasUnsavedCommonConfig: boolean; + /** 获取待保存的通用配置片段(用于 handleSubmit) */ + getPendingCommonConfigSnippet: () => string | null; + /** 标记通用配置已保存 */ + markCommonConfigSaved: () => void; } /** @@ -118,18 +124,8 @@ export function useCodexCommonConfig({ const [commonConfigError, setCommonConfigError] = useState(""); const [isLoading, setIsLoading] = useState(true); const [isExtracting, setIsExtracting] = useState(false); - - // 用于避免异步保存乱序 - const saveSequenceRef = useRef(0); - const saveQueueRef = useRef>(Promise.resolve()); - const enqueueSave = useCallback((saveFn: () => Promise) => { - const next = saveQueueRef.current.then(saveFn); - // Log errors to help with debugging, but don't block subsequent saves - saveQueueRef.current = next.catch((e) => { - console.error("[SaveQueue] Codex common config save failed:", e); - }); - return next; - }, []); + // 是否有待保存的通用配置变更 + const [hasUnsavedCommonConfig, setHasUnsavedCommonConfig] = useState(false); // 用于跟踪编辑模式是否已初始化 const hasInitializedEditMode = useRef(false); @@ -340,24 +336,15 @@ export function useCodexCommonConfig({ ); // ============================================================================ - // 处理通用配置片段变化 + // 处理通用配置片段变化(延迟保存模式:只更新本地状态,实际保存在表单提交时) // ============================================================================ const handleCommonConfigSnippetChange = useCallback( (value: string) => { setCommonConfigSnippetState(value); if (!value.trim()) { - const saveId = ++saveSequenceRef.current; setCommonConfigError(""); - enqueueSave(() => configApi.setCommonConfigSnippet("codex", "")).catch( - (error) => { - if (saveSequenceRef.current !== saveId) return; - console.error("保存 Codex 通用配置失败:", error); - setCommonConfigError( - t("codexConfig.saveFailed", { error: String(error) }), - ); - }, - ); + setHasUnsavedCommonConfig(true); return; } @@ -372,26 +359,17 @@ export function useCodexCommonConfig({ return; } - const saveId = ++saveSequenceRef.current; setCommonConfigError(""); - enqueueSave(() => configApi.setCommonConfigSnippet("codex", value)).catch( - (error) => { - if (saveSequenceRef.current !== saveId) return; - console.error("保存 Codex 通用配置失败:", error); - setCommonConfigError( - t("codexConfig.saveFailed", { error: String(error) }), - ); - }, - ); + setHasUnsavedCommonConfig(true); // 注意:新架构下不再需要同步到其他供应商的 settingsConfig // 因为 finalConfig 是运行时计算的 }, - [enqueueSave, t], + [t], ); // ============================================================================ - // 从当前最终配置提取通用配置片段 + // 从当前最终配置提取通用配置片段(延迟保存模式:只更新本地状态,实际保存在表单提交时) // ============================================================================ const handleExtract = useCallback(async () => { setIsExtracting(true); @@ -420,11 +398,9 @@ export function useCodexCommonConfig({ return; } - // 更新片段状态 + // 更新片段状态(延迟保存:不立即调用后端 API) setCommonConfigSnippetState(extracted); - - // 保存到后端 - await configApi.setCommonConfigSnippet("codex", extracted); + setHasUnsavedCommonConfig(true); // 提取成功后,从 config 中移除与 extracted 相同的部分 const customToml = extractConfigToml(codexConfig); @@ -435,10 +411,10 @@ export function useCodexCommonConfig({ const parsed = JSON.parse(codexConfig); parsed.config = diffResult.customToml; onConfigChange(JSON.stringify(parsed, null, 2)); - // Notify user that config was modified + // Notify user that config was modified (提示用户需要保存) toast.success( - t("codexConfig.extractSuccess", { - defaultValue: "已提取通用配置,自定义配置已自动更新", + t("codexConfig.extractSuccessNeedSave", { + defaultValue: "已提取通用配置,点击保存按钮完成保存", }), ); } catch (parseError) { @@ -458,6 +434,20 @@ export function useCodexCommonConfig({ } }, [finalConfig, codexConfig, onConfigChange, extractConfigToml, t]); + // ============================================================================ + // 获取待保存的通用配置片段(用于 handleSubmit 中统一保存) + // ============================================================================ + const getPendingCommonConfigSnippet = useCallback(() => { + return hasUnsavedCommonConfig ? commonConfigSnippet : null; + }, [hasUnsavedCommonConfig, commonConfigSnippet]); + + // ============================================================================ + // 标记通用配置已保存 + // ============================================================================ + const markCommonConfigSaved = useCallback(() => { + setHasUnsavedCommonConfig(false); + }, []); + return { useCommonConfig, commonConfigSnippet, @@ -468,5 +458,8 @@ export function useCodexCommonConfig({ handleCommonConfigSnippetChange, handleExtract, finalConfig, + hasUnsavedCommonConfig, + getPendingCommonConfigSnippet, + markCommonConfigSaved, }; } diff --git a/src/components/providers/forms/hooks/useCommonConfigSnippet.ts b/src/components/providers/forms/hooks/useCommonConfigSnippet.ts index 72b25ce39..afb571231 100644 --- a/src/components/providers/forms/hooks/useCommonConfigSnippet.ts +++ b/src/components/providers/forms/hooks/useCommonConfigSnippet.ts @@ -65,6 +65,12 @@ export interface UseCommonConfigSnippetReturn { handleExtract: () => Promise; /** 最终配置(运行时合并结果,只读) */ finalConfig: string; + /** 是否有待保存的通用配置变更 */ + hasUnsavedCommonConfig: boolean; + /** 获取待保存的通用配置片段(用于 handleSubmit) */ + getPendingCommonConfigSnippet: () => string | null; + /** 标记通用配置已保存 */ + markCommonConfigSaved: () => void; } /** @@ -96,18 +102,8 @@ export function useCommonConfigSnippet({ const [commonConfigError, setCommonConfigError] = useState(""); const [isLoading, setIsLoading] = useState(true); const [isExtracting, setIsExtracting] = useState(false); - - // 用于避免异步保存乱序 - const saveSequenceRef = useRef(0); - const saveQueueRef = useRef>(Promise.resolve()); - const enqueueSave = useCallback((saveFn: () => Promise) => { - const next = saveQueueRef.current.then(saveFn); - // Log errors to help with debugging, but don't block subsequent saves - saveQueueRef.current = next.catch((e) => { - console.error("[SaveQueue] Claude common config save failed:", e); - }); - return next; - }, []); + // 是否有待保存的通用配置变更 + const [hasUnsavedCommonConfig, setHasUnsavedCommonConfig] = useState(false); // 用于跟踪编辑模式是否已初始化 const hasInitializedEditMode = useRef(false); @@ -344,54 +340,33 @@ export function useCommonConfigSnippet({ ); // ============================================================================ - // 处理通用配置片段变化 + // 处理通用配置片段变化(延迟保存模式:只更新本地状态,实际保存在表单提交时) // ============================================================================ - const handleCommonConfigSnippetChange = useCallback( - (value: string) => { - setCommonConfigSnippetState(value); + const handleCommonConfigSnippetChange = useCallback((value: string) => { + setCommonConfigSnippetState(value); - if (!value.trim()) { - const saveId = ++saveSequenceRef.current; - setCommonConfigError(""); - enqueueSave(() => configApi.setCommonConfigSnippet("claude", "")).catch( - (error) => { - if (saveSequenceRef.current !== saveId) return; - console.error("保存通用配置失败:", error); - setCommonConfigError( - t("claudeConfig.saveFailed", { error: String(error) }), - ); - }, - ); - return; - } - - // JSON 格式校验 - const validationError = validateJsonConfig(value, "通用配置片段"); - if (validationError) { - setCommonConfigError(validationError); - return; - } - - const saveId = ++saveSequenceRef.current; + if (!value.trim()) { setCommonConfigError(""); - enqueueSave(() => - configApi.setCommonConfigSnippet("claude", value), - ).catch((error) => { - if (saveSequenceRef.current !== saveId) return; - console.error("保存通用配置失败:", error); - setCommonConfigError( - t("claudeConfig.saveFailed", { error: String(error) }), - ); - }); + setHasUnsavedCommonConfig(true); + return; + } - // 注意:新架构下不再需要同步到其他供应商的 settingsConfig - // 因为 finalConfig 是运行时计算的 - }, - [enqueueSave, t], - ); + // JSON 格式校验 + const validationError = validateJsonConfig(value, "通用配置片段"); + if (validationError) { + setCommonConfigError(validationError); + return; + } + + setCommonConfigError(""); + setHasUnsavedCommonConfig(true); + + // 注意:新架构下不再需要同步到其他供应商的 settingsConfig + // 因为 finalConfig 是运行时计算的 + }, []); // ============================================================================ - // 从当前最终配置提取通用配置片段 + // 从当前最终配置提取通用配置片段(延迟保存模式:只更新本地状态,实际保存在表单提交时) // ============================================================================ const handleExtract = useCallback(async () => { setIsExtracting(true); @@ -414,11 +389,9 @@ export function useCommonConfigSnippet({ return; } - // 更新片段状态 + // 更新片段状态(延迟保存:不立即调用后端 API) setCommonConfigSnippetState(extracted); - - // 保存到后端 - await configApi.setCommonConfigSnippet("claude", extracted); + setHasUnsavedCommonConfig(true); // 提取成功后,从 settingsConfig 中移除与 extracted 相同的部分 try { @@ -428,10 +401,10 @@ export function useCommonConfigSnippet({ if (isPlainObject(customParsed) && isPlainObject(extractedParsed)) { const diffResult = extractDifference(customParsed, extractedParsed); onConfigChange(JSON.stringify(diffResult.customConfig, null, 2)); - // Notify user that config was modified + // Notify user that config was modified (提示用户需要保存) toast.success( - t("claudeConfig.extractSuccess", { - defaultValue: "已提取通用配置,自定义配置已自动更新", + t("claudeConfig.extractSuccessNeedSave", { + defaultValue: "已提取通用配置,点击保存按钮完成保存", }), ); } @@ -451,6 +424,20 @@ export function useCommonConfigSnippet({ } }, [finalConfig, settingsConfig, onConfigChange, t]); + // ============================================================================ + // 获取待保存的通用配置片段(用于 handleSubmit 中统一保存) + // ============================================================================ + const getPendingCommonConfigSnippet = useCallback(() => { + return hasUnsavedCommonConfig ? commonConfigSnippet : null; + }, [hasUnsavedCommonConfig, commonConfigSnippet]); + + // ============================================================================ + // 标记通用配置已保存 + // ============================================================================ + const markCommonConfigSaved = useCallback(() => { + setHasUnsavedCommonConfig(false); + }, []); + return { useCommonConfig, commonConfigSnippet, @@ -461,5 +448,8 @@ export function useCommonConfigSnippet({ handleCommonConfigSnippetChange, handleExtract, finalConfig, + hasUnsavedCommonConfig, + getPendingCommonConfigSnippet, + markCommonConfigSaved, }; } diff --git a/src/components/providers/forms/hooks/useGeminiCommonConfig.ts b/src/components/providers/forms/hooks/useGeminiCommonConfig.ts index 8b2e1ffde..0680a0c5d 100644 --- a/src/components/providers/forms/hooks/useGeminiCommonConfig.ts +++ b/src/components/providers/forms/hooks/useGeminiCommonConfig.ts @@ -65,6 +65,12 @@ export interface UseGeminiCommonConfigReturn { handleExtract: () => Promise; /** 最终 env 对象(运行时合并结果,只读) */ finalEnv: Record; + /** 是否有待保存的通用配置变更 */ + hasUnsavedCommonConfig: boolean; + /** 获取待保存的通用配置片段(用于 handleSubmit) */ + getPendingCommonConfigSnippet: () => string | null; + /** 标记通用配置已保存 */ + markCommonConfigSaved: () => void; } /** @@ -97,18 +103,8 @@ export function useGeminiCommonConfig({ const [commonConfigError, setCommonConfigError] = useState(""); const [isLoading, setIsLoading] = useState(true); const [isExtracting, setIsExtracting] = useState(false); - - // 用于避免异步保存乱序 - const saveSequenceRef = useRef(0); - const saveQueueRef = useRef>(Promise.resolve()); - const enqueueSave = useCallback((saveFn: () => Promise) => { - const next = saveQueueRef.current.then(saveFn); - // Log errors to help with debugging, but don't block subsequent saves - saveQueueRef.current = next.catch((e) => { - console.error("[SaveQueue] Gemini common config save failed:", e); - }); - return next; - }, []); + // 是否有待保存的通用配置变更 + const [hasUnsavedCommonConfig, setHasUnsavedCommonConfig] = useState(false); // 用于跟踪编辑模式是否已初始化 const hasInitializedEditMode = useRef(false); @@ -330,24 +326,15 @@ export function useGeminiCommonConfig({ ); // ============================================================================ - // 处理通用配置片段变化 + // 处理通用配置片段变化(延迟保存模式:只更新本地状态,实际保存在表单提交时) // ============================================================================ const handleCommonConfigSnippetChange = useCallback( (value: string) => { setCommonConfigSnippetState(value); if (!value.trim()) { - const saveId = ++saveSequenceRef.current; setCommonConfigError(""); - enqueueSave(() => configApi.setCommonConfigSnippet("gemini", "")).catch( - (error) => { - if (saveSequenceRef.current !== saveId) return; - console.error("保存 Gemini 通用配置失败:", error); - setCommonConfigError( - t("geminiConfig.saveFailed", { error: String(error) }), - ); - }, - ); + setHasUnsavedCommonConfig(true); return; } @@ -358,26 +345,17 @@ export function useGeminiCommonConfig({ return; } - const saveId = ++saveSequenceRef.current; setCommonConfigError(""); - enqueueSave(() => - configApi.setCommonConfigSnippet("gemini", value), - ).catch((error) => { - if (saveSequenceRef.current !== saveId) return; - console.error("保存 Gemini 通用配置失败:", error); - setCommonConfigError( - t("geminiConfig.saveFailed", { error: String(error) }), - ); - }); + setHasUnsavedCommonConfig(true); // 注意:新架构下不再需要同步到其他供应商的 settingsConfig // 因为 finalEnv 是运行时计算的 }, - [enqueueSave, parseSnippetEnv, t], + [parseSnippetEnv], ); // ============================================================================ - // 从当前最终 env 提取通用配置片段 + // 从当前最终 env 提取通用配置片段(延迟保存模式:只更新本地状态,实际保存在表单提交时) // ============================================================================ const handleExtract = useCallback(async () => { setIsExtracting(true); @@ -402,11 +380,9 @@ export function useGeminiCommonConfig({ return; } - // 更新片段状态 + // 更新片段状态(延迟保存:不立即调用后端 API) setCommonConfigSnippetState(extracted); - - // 保存到后端 - await configApi.setCommonConfigSnippet("gemini", extracted); + setHasUnsavedCommonConfig(true); // 提取成功后,从 customEnv 中移除与 extracted 相同的部分 const diffResult = extractDifference( @@ -420,10 +396,10 @@ export function useGeminiCommonConfig({ } } onEnvChange(envObjToString(newCustomEnv)); - // Notify user that config was modified + // Notify user that config was modified (提示用户需要保存) toast.success( - t("geminiConfig.extractSuccess", { - defaultValue: "已提取通用配置,自定义配置已自动更新", + t("geminiConfig.extractSuccessNeedSave", { + defaultValue: "已提取通用配置,点击保存按钮完成保存", }), ); } catch (error) { @@ -436,6 +412,20 @@ export function useGeminiCommonConfig({ } }, [finalEnv, customEnv, onEnvChange, envObjToString, parseSnippetEnv, t]); + // ============================================================================ + // 获取待保存的通用配置片段(用于 handleSubmit 中统一保存) + // ============================================================================ + const getPendingCommonConfigSnippet = useCallback(() => { + return hasUnsavedCommonConfig ? commonConfigSnippet : null; + }, [hasUnsavedCommonConfig, commonConfigSnippet]); + + // ============================================================================ + // 标记通用配置已保存 + // ============================================================================ + const markCommonConfigSaved = useCallback(() => { + setHasUnsavedCommonConfig(false); + }, []); + return { useCommonConfig, commonConfigSnippet, @@ -446,5 +436,8 @@ export function useGeminiCommonConfig({ handleCommonConfigSnippetChange, handleExtract, finalEnv, + hasUnsavedCommonConfig, + getPendingCommonConfigSnippet, + markCommonConfigSaved, }; } diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 6548cbd15..38131e168 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -59,6 +59,7 @@ "extractFromCurrent": "Extract from Editor", "extractNoCommonConfig": "No common config available to extract from editor", "extractFailed": "Extract failed: {{error}}", + "extractSuccessNeedSave": "Common config extracted, click Save to complete", "saveFailed": "Save failed: {{error}}", "hidePreview": "Hide merged preview", "showPreview": "Show merged preview", @@ -493,7 +494,8 @@ "categoryAggregation": "Aggregation", "categoryThirdParty": "Third Party", "commonConfigLoading": "Common config is still loading, please wait before saving", - "commonConfigSyncFailed": "Common config sync failed for some providers. Please retry." + "commonConfigSyncFailed": "Common config sync failed for some providers. Please retry.", + "saveCommonConfigFailed": "Failed to save common config" }, "endpointTest": { "title": "API Endpoint Management", @@ -563,6 +565,7 @@ "extractFromCurrent": "Extract from Editor", "extractNoCommonConfig": "No common config available to extract from editor", "extractFailed": "Extract failed: {{error}}", + "extractSuccessNeedSave": "Common config extracted, click Save to complete", "saveFailed": "Save failed: {{error}}" }, "geminiConfig": { @@ -577,6 +580,7 @@ "extractFromCurrent": "Extract from Editor", "extractNoCommonConfig": "No common config available to extract from editor", "extractFailed": "Extract failed: {{error}}", + "extractSuccessNeedSave": "Common config extracted, click Save to complete", "saveFailed": "Save failed: {{error}}", "extractedConfigInvalid": "Extracted config format is invalid", "invalidJsonFormat": "Common config snippet format error (must be valid JSON)", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 487becdf9..d44f3171b 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -59,6 +59,7 @@ "extractFromCurrent": "編集内容から抽出", "extractNoCommonConfig": "編集内容から抽出できる共通設定がありません", "extractFailed": "抽出に失敗しました: {{error}}", + "extractSuccessNeedSave": "共通設定を抽出しました。保存ボタンをクリックして完了してください", "saveFailed": "保存に失敗しました: {{error}}", "hidePreview": "マージプレビューを非表示", "showPreview": "マージプレビューを表示", @@ -493,7 +494,8 @@ "categoryAggregation": "アグリゲーター", "categoryThirdParty": "サードパーティ", "commonConfigLoading": "共通設定を読み込み中です。保存する前にお待ちください", - "commonConfigSyncFailed": "共通設定の同期に失敗しました。再試行してください" + "commonConfigSyncFailed": "共通設定の同期に失敗しました。再試行してください", + "saveCommonConfigFailed": "共通設定の保存に失敗しました" }, "endpointTest": { "title": "API エンドポイント管理", @@ -563,6 +565,7 @@ "extractFromCurrent": "編集内容から抽出", "extractNoCommonConfig": "編集内容から抽出できる共通設定がありません", "extractFailed": "抽出に失敗しました: {{error}}", + "extractSuccessNeedSave": "共通設定を抽出しました。保存ボタンをクリックして完了してください", "saveFailed": "保存に失敗しました: {{error}}" }, "geminiConfig": { @@ -577,6 +580,7 @@ "extractFromCurrent": "編集内容から抽出", "extractNoCommonConfig": "編集内容から抽出できる共通設定がありません", "extractFailed": "抽出に失敗しました: {{error}}", + "extractSuccessNeedSave": "共通設定を抽出しました。保存ボタンをクリックして完了してください", "saveFailed": "保存に失敗しました: {{error}}", "extractedConfigInvalid": "抽出した設定のフォーマットが不正です", "invalidJsonFormat": "共通設定スニペットの形式が不正です(有効な JSON でなければなりません)", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 721bf28e1..805309420 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -59,6 +59,7 @@ "extractFromCurrent": "从编辑内容提取", "extractNoCommonConfig": "当前编辑内容没有可提取的通用配置", "extractFailed": "提取失败: {{error}}", + "extractSuccessNeedSave": "已提取通用配置,点击保存按钮完成保存", "saveFailed": "保存失败: {{error}}", "hidePreview": "隐藏合并预览", "showPreview": "显示合并预览", @@ -493,7 +494,8 @@ "categoryAggregation": "聚合服务", "categoryThirdParty": "第三方", "commonConfigLoading": "通用配置正在加载中,请稍候再保存", - "commonConfigSyncFailed": "通用配置同步部分失败,请重试" + "commonConfigSyncFailed": "通用配置同步部分失败,请重试", + "saveCommonConfigFailed": "保存通用配置失败" }, "endpointTest": { "title": "请求地址管理", @@ -577,6 +579,7 @@ "extractFromCurrent": "从编辑内容提取", "extractNoCommonConfig": "当前编辑内容没有可提取的通用配置", "extractFailed": "提取失败: {{error}}", + "extractSuccessNeedSave": "已提取通用配置,点击保存按钮完成保存", "saveFailed": "保存失败: {{error}}", "extractedConfigInvalid": "提取的配置格式错误", "invalidJsonFormat": "通用配置片段格式错误(必须是有效的 JSON)",