From a4afa58fe98a54f346841d2f51859f2789f9490e Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 5 May 2026 02:05:02 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E6=A8=A1=E5=9E=8B=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=85=A2=EF=BC=8C=E9=94=99=E8=AF=AF=E7=83=AD=E9=87=8D?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/routes/config/model.tsx | 7 +++- .../config/model/hooks/useModelAutoSave.ts | 37 ++++++++++++++++++- .../src/routes/config/modelProvider/index.tsx | 16 +++++++- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/dashboard/src/routes/config/model.tsx b/dashboard/src/routes/config/model.tsx index 278dc7bc..a4d8c59c 100644 --- a/dashboard/src/routes/config/model.tsx +++ b/dashboard/src/routes/config/model.tsx @@ -138,7 +138,7 @@ function ModelConfigPageContent() { const { triggerRestart, isRestarting } = useRestart() // 自动保存 (使用 hook 封装的逻辑) - const { clearTimers: clearAutoSaveTimers, initialLoadRef } = useModelAutoSave({ + const { clearTimers: clearAutoSaveTimers, initialLoadRef, resetSnapshots } = useModelAutoSave({ models, taskConfig, onSavingChange: setAutoSaving, @@ -200,6 +200,7 @@ function ModelConfigPageContent() { const taskConf = (config.model_task_config as ModelTaskConfig) || null setTaskConfig(taskConf) + resetSnapshots(modelList, taskConf) // 解析 model_task_config 的 schema if (schemaResult.success && schemaResult.data) { @@ -220,7 +221,7 @@ function ModelConfigPageContent() { } finally { setLoading(false) } - }, [initialLoadRef, checkTaskConfigIssues]) + }, [initialLoadRef, checkTaskConfigIssues, resetSnapshots]) // 初始加载 useEffect(() => { @@ -343,6 +344,7 @@ function ModelConfigPageContent() { setSaving(false) return } + resetSnapshots(config.models as ModelInfo[], taskConfig) setHasUnsavedChanges(false) toast({ title: '保存成功', @@ -392,6 +394,7 @@ function ModelConfigPageContent() { setSaving(false) return } + resetSnapshots(config.models as ModelInfo[], taskConfig) setHasUnsavedChanges(false) toast({ title: '保存成功', diff --git a/dashboard/src/routes/config/model/hooks/useModelAutoSave.ts b/dashboard/src/routes/config/model/hooks/useModelAutoSave.ts index 8bb55d6d..b563d2e4 100644 --- a/dashboard/src/routes/config/model/hooks/useModelAutoSave.ts +++ b/dashboard/src/routes/config/model/hooks/useModelAutoSave.ts @@ -25,6 +25,7 @@ interface UseModelAutoSaveReturn { clearTimers: () => void /** 初始加载状态标记引用 (用于设置初始加载完成) */ initialLoadRef: RefObject + resetSnapshots: (nextModels: ModelInfo[], nextTaskConfig: ModelTaskConfig | null) => void } /** @@ -45,6 +46,8 @@ export function useModelAutoSave( const modelsTimerRef = useRef | null>(null) const taskConfigTimerRef = useRef | null>(null) const initialLoadRef = useRef(true) + const modelsSnapshotRef = useRef(null) + const taskConfigSnapshotRef = useRef(null) // 清除定时器 const clearTimers = useCallback(() => { @@ -83,6 +86,19 @@ export function useModelAutoSave( }, []) // 自动保存模型列表 + const snapshotModels = useCallback((nextModels: ModelInfo[]): string => { + return JSON.stringify(nextModels.map(cleanModelForSave)) + }, [cleanModelForSave]) + + const snapshotTaskConfig = useCallback((nextTaskConfig: ModelTaskConfig | null): string | null => { + return nextTaskConfig ? JSON.stringify(nextTaskConfig) : null + }, []) + + const resetSnapshots = useCallback((nextModels: ModelInfo[], nextTaskConfig: ModelTaskConfig | null) => { + modelsSnapshotRef.current = snapshotModels(nextModels) + taskConfigSnapshotRef.current = snapshotTaskConfig(nextTaskConfig) + }, [snapshotModels, snapshotTaskConfig]) + const autoSaveModels = useCallback(async (newModels: ModelInfo[]) => { try { onSavingChange?.(true) @@ -92,6 +108,7 @@ export function useModelAutoSave( if (!result.success) { throw new Error(result.error) } + modelsSnapshotRef.current = JSON.stringify(cleanedModels) onUnsavedChange?.(false) } catch (error) { console.error('自动保存模型列表失败:', error) @@ -109,6 +126,7 @@ export function useModelAutoSave( if (!result.success) { throw new Error(result.error) } + taskConfigSnapshotRef.current = JSON.stringify(newTaskConfig) onUnsavedChange?.(false) } catch (error) { console.error('自动保存任务配置失败:', error) @@ -122,6 +140,13 @@ export function useModelAutoSave( useEffect(() => { if (initialLoadRef.current) return + const snapshot = snapshotModels(models) + if (modelsSnapshotRef.current === null) { + modelsSnapshotRef.current = snapshot + return + } + if (snapshot === modelsSnapshotRef.current) return + onUnsavedChange?.(true) if (modelsTimerRef.current) { @@ -137,12 +162,19 @@ export function useModelAutoSave( clearTimeout(modelsTimerRef.current) } } - }, [models, autoSaveModels, debounceMs, onUnsavedChange]) + }, [models, autoSaveModels, debounceMs, onUnsavedChange, snapshotModels]) // 监听 taskConfig 变化 useEffect(() => { if (initialLoadRef.current || !taskConfig) return + const snapshot = snapshotTaskConfig(taskConfig) + if (taskConfigSnapshotRef.current === null) { + taskConfigSnapshotRef.current = snapshot + return + } + if (snapshot === taskConfigSnapshotRef.current) return + onUnsavedChange?.(true) if (taskConfigTimerRef.current) { @@ -158,7 +190,7 @@ export function useModelAutoSave( clearTimeout(taskConfigTimerRef.current) } } - }, [taskConfig, autoSaveTaskConfig, debounceMs, onUnsavedChange]) + }, [taskConfig, autoSaveTaskConfig, debounceMs, onUnsavedChange, snapshotTaskConfig]) // 组件卸载时清除定时器 useEffect(() => { @@ -170,5 +202,6 @@ export function useModelAutoSave( return { clearTimers, initialLoadRef, + resetSnapshots, } } diff --git a/dashboard/src/routes/config/modelProvider/index.tsx b/dashboard/src/routes/config/modelProvider/index.tsx index 41550ecb..a14b7595 100644 --- a/dashboard/src/routes/config/modelProvider/index.tsx +++ b/dashboard/src/routes/config/modelProvider/index.tsx @@ -78,6 +78,7 @@ function ModelProviderConfigPageContent() { const autoSaveTimerRef = useRef | null>(null) const initialLoadRef = useRef(true) + const providersSnapshotRef = useRef(null) const prevTourStepRef = useRef(tourState.stepIndex) // 注册 Tour @@ -161,7 +162,9 @@ function ModelProviderConfigPageContent() { return } const config = unwrapModelConfig(result.data) - setProviders(Array.isArray(config.api_providers) ? config.api_providers as APIProvider[] : []) + const providerList = Array.isArray(config.api_providers) ? config.api_providers as APIProvider[] : [] + setProviders(providerList) + providersSnapshotRef.current = JSON.stringify(providerList.map(cleanProviderData)) setHasUnsavedChanges(false) initialLoadRef.current = false } catch (error) { @@ -231,6 +234,7 @@ function ModelProviderConfigPageContent() { setSaving(false) return } + providersSnapshotRef.current = JSON.stringify(cleanedProviders) setHasUnsavedChanges(false) toast({ title: '保存成功', @@ -356,6 +360,7 @@ function ModelProviderConfigPageContent() { } setProviders(deleteConfirmState.pendingProviders) + providersSnapshotRef.current = JSON.stringify(cleanedProviders) setHasUnsavedChanges(false) toast({ @@ -431,6 +436,7 @@ function ModelProviderConfigPageContent() { setHasUnsavedChanges(true) return } + providersSnapshotRef.current = JSON.stringify(cleanedProviders) setHasUnsavedChanges(false) } catch (error) { console.error('自动保存失败:', error) @@ -448,6 +454,13 @@ function ModelProviderConfigPageContent() { useEffect(() => { if (initialLoadRef.current) return + const snapshot = JSON.stringify(providers.map(cleanProviderData)) + if (providersSnapshotRef.current === null) { + providersSnapshotRef.current = snapshot + return + } + if (snapshot === providersSnapshotRef.current) return + setHasUnsavedChanges(true) if (autoSaveTimerRef.current) { @@ -529,6 +542,7 @@ function ModelProviderConfigPageContent() { setSaving(false) return } + providersSnapshotRef.current = JSON.stringify(cleanedProviders) setHasUnsavedChanges(false) toast({ title: '保存成功',