From 235f57c9ae5e110c46c361b4a4ab60905103135d Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Wed, 5 Aug 2026 09:43:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(settings):=20=E6=96=B0=E5=A2=9E=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E9=A1=B5=E9=87=8D=E5=90=AF=E6=8C=89=E9=92=AE=EF=BC=8C?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E4=BF=9D=E5=AD=98=E5=90=8E=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E4=B8=8E=E6=89=8B=E5=8A=A8=E8=A7=A6=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 状态从 showRestartDialog: bool 改为 restartDialogMode: 'saved' | 'manual' | null,三态清晰区分触发来源。 改动:- 保存后自动弹出 'saved' 模式对话框(标题 '配置已保存',按钮 '稍后') - 新增独立 '重启' 按钮,点击弹出 'manual' 模式对话框(标题 '重启服务',按钮 '取消'),dirty 时提示未保存更改 - 重启中禁用保存与重启按钮,handleClose 阻断关闭并提示 - ESC 在对话框打开时仅关闭对话框,不关闭整个页面 - 重启按钮 loading 状态与 restarting 同步 --- web/src/components/Settings/ConfigPage.tsx | 52 +++++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/web/src/components/Settings/ConfigPage.tsx b/web/src/components/Settings/ConfigPage.tsx index a378e62..6f49576 100644 --- a/web/src/components/Settings/ConfigPage.tsx +++ b/web/src/components/Settings/ConfigPage.tsx @@ -49,7 +49,7 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage const [error, setError] = useState(''); const [toast, setToastState] = useState(''); const [dirty, setDirty] = useState(false); - const [showRestartDialog, setShowRestartDialog] = useState(false); + const [restartDialogMode, setRestartDialogMode] = useState<'saved' | 'manual' | null>(null); const [restarting, setRestarting] = useState(false); // toast 自动清除:每次设置非空 toast 时启动 3s 计时器 @@ -77,9 +77,13 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage }, []); const handleClose = useCallback(() => { + if (restarting) { + showToast('服务正在重启,请等待完成'); + return; + } if (dirty && !confirm('有未保存的更改,确定要关闭吗?')) return; onClose(); - }, [dirty, onClose]); + }, [dirty, restarting, showToast, onClose]); // Load config useEffect(() => { @@ -93,11 +97,18 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage // ESC 关闭(子模态框的 ESC 在 capture 阶段已 stopPropagation,不会触发此处) useEffect(() => { const h = (e: KeyboardEvent) => { - if (e.key === 'Escape') handleClose(); + if (e.key === 'Escape') { + // 重启确认对话框打开时,ESC 仅关闭对话框,不关闭整个页面 + if (restartDialogMode !== null) { + setRestartDialogMode(null); + return; + } + handleClose(); + } }; document.addEventListener('keydown', h); return () => document.removeEventListener('keydown', h); - }, [handleClose]); + }, [handleClose, restartDialogMode]); const update = useCallback((key: K, value: AppConfig[K]) => { setConfig((prev) => (prev ? { ...prev, [key]: value } : prev)); @@ -115,13 +126,13 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage // Config is now synced to both disk and in-memory state, // so the local state is already correct. No need to re-fetch. setDirty(false); - setShowRestartDialog(true); + setRestartDialogMode('saved'); } setSaving(false); }; const handleRestart = async () => { - setShowRestartDialog(false); + setRestartDialogMode(null); setRestarting(true); try { const { status, data } = await restartGateway(); @@ -289,12 +300,21 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage + {/* Toast */} @@ -310,7 +330,7 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage )} {/* Restart confirmation dialog */} - {showRestartDialog && ( + {restartDialogMode && (
@@ -318,16 +338,24 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
-

配置已保存

-

是否立即重启服务使配置生效?

+

+ {restartDialogMode === 'saved' ? '配置已保存' : '重启服务'} +

+

+ {restartDialogMode === 'saved' + ? '是否立即重启服务使配置生效?' + : dirty + ? '有未保存的更改,重启后将丢失。确定要重启吗?' + : '确定要重启服务吗?'} +