From 4c0f0a8fdd2671d1c1b70c53822255aacbde25b2 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 24 Jul 2026 22:31:33 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20fix(ui):=20=E6=B8=85=E7=90=86?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=8F=B0=20Form=20=E4=B8=8E=20Modal=20?= =?UTF-8?q?=E5=BC=83=E7=94=A8=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 关闭消息发送弹窗时不再对已卸载 Form 调用 resetFields - 缩放修复路径中 ResetWebViewZoom 不可用时最多告警一次 - 将 Drawer/Modal 的 destroyOnClose 统一迁移为 destroyOnHidden --- frontend/src/App.tsx | 41 +++++++++++-------- .../MessagePublishModal.i18n.test.ts | 3 ++ .../src/components/MessagePublishModal.tsx | 5 ++- .../components/audit/SqlAuditDetailDrawer.tsx | 2 +- .../audit/SqlAuditSettingsDrawer.tsx | 2 +- .../components/explain/ExplainWorkbench.tsx | 2 +- .../src/components/explain/SlowQueryPanel.tsx | 4 +- .../components/resultDiff/ResultDiffPanel.tsx | 2 +- .../resultDiff/ResultDiffWizard.tsx | 2 +- .../resultDiff/ViewDataVerifyWizard.tsx | 2 +- 10 files changed, 39 insertions(+), 26 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ec5b4795..7b65bcf4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1797,6 +1797,29 @@ function App() { let hiddenSeen = document.visibilityState === 'hidden'; const wait = (ms: number) => new Promise((resolve) => window.setTimeout(resolve, ms)); + // Automatic scale-fix may call ResetWebViewZoom multiple times on startup. + // The backend path depends on Wails unexported fields and can fail harmlessly; + // log at most once so the console is not flooded with expected unavailability. + let resetWebViewZoomUnavailableLogged = false; + const tryResetWebViewZoomQuietly = async () => { + try { + const res = await (window as any).go?.app?.App?.ResetWebViewZoom?.(); + if (res?.success) { + return true; + } + if (!resetWebViewZoomUnavailableLogged) { + resetWebViewZoomUnavailableLogged = true; + console.warn('ResetWebViewZoom unavailable in fixWindowScaleIfNeeded:', res?.message); + } + return false; + } catch (e) { + if (!resetWebViewZoomUnavailableLogged) { + resetWebViewZoomUnavailableLogged = true; + console.warn('ResetWebViewZoom call failed in fixWindowScaleIfNeeded', e); + } + return false; + } + }; const fixWindowScaleIfNeeded = async (reason: WindowScaleFixReason) => { if (cancelled || inFlight) return; @@ -1828,14 +1851,7 @@ function App() { const shouldResetWebViewZoom = shouldResetWebViewZoomForScaleFix(reason, hasViewportScaleDrift); if (shouldResetWebViewZoom && !isMaximised) { - try { - const res = await (window as any).go?.app?.App?.ResetWebViewZoom?.(); - if (!res?.success) { - console.warn('ResetWebViewZoom unavailable in fixWindowScaleIfNeeded:', res?.message); - } - } catch (e) { - console.warn('ResetWebViewZoom call failed in fixWindowScaleIfNeeded', e); - } + await tryResetWebViewZoomQuietly(); } if (isMaximised) { @@ -1847,14 +1863,7 @@ function App() { // backend 失败(wails 升级破坏反射 / 非 Windows)时回退到 dispatch resize 兜底; // 用户仍可按 Ctrl+Shift+0 手动 toggle 修复。 if (shouldResetWebViewZoom) { - try { - const res = await (window as any).go?.app?.App?.ResetWebViewZoom?.(); - if (!res?.success) { - console.warn('ResetWebViewZoom unavailable in fixWindowScaleIfNeeded:', res?.message); - } - } catch (e) { - console.warn('ResetWebViewZoom call failed in fixWindowScaleIfNeeded', e); - } + await tryResetWebViewZoomQuietly(); } window.dispatchEvent(new Event('resize')); lastFixAt = Date.now(); diff --git a/frontend/src/components/MessagePublishModal.i18n.test.ts b/frontend/src/components/MessagePublishModal.i18n.test.ts index ef03aae2..8fd1099a 100644 --- a/frontend/src/components/MessagePublishModal.i18n.test.ts +++ b/frontend/src/components/MessagePublishModal.i18n.test.ts @@ -19,6 +19,9 @@ describe('MessagePublishModal i18n shell guards', () => { expect(modalSource).toContain('connectionName: connection.name'); expect(modalSource).toContain('detail: res?.message'); expect(modalSource).toContain('detail: error?.message || String(error)'); + expect(modalSource).toContain('destroyOnHidden'); + // Closed modals unmount Form; never call form APIs while open === false. + expect(modalSource).not.toContain('form.resetFields()'); expect(modalSource).not.toContain('测试发送消息'); expect(modalSource).not.toContain('okText="发送"'); expect(modalSource).not.toContain('发送失败:'); diff --git a/frontend/src/components/MessagePublishModal.tsx b/frontend/src/components/MessagePublishModal.tsx index b4bea4b9..7b12e865 100644 --- a/frontend/src/components/MessagePublishModal.tsx +++ b/frontend/src/components/MessagePublishModal.tsx @@ -63,6 +63,8 @@ const MessagePublishModal: React.FC = ({ ); useEffect(() => { + // destroyOnHidden unmounts the Form while closed. Only touch the form + // instance when the modal is open so useForm stays connected. if (!open || !connection) return; form.setFieldsValue( createDefaultMessagePublishDraft( @@ -74,9 +76,8 @@ const MessagePublishModal: React.FC = ({ useEffect(() => { if (open) return; - form.resetFields(); setSubmitting(false); - }, [form, open]); + }, [open]); const handleSubmit = async () => { if (!connection) return; diff --git a/frontend/src/components/audit/SqlAuditDetailDrawer.tsx b/frontend/src/components/audit/SqlAuditDetailDrawer.tsx index 41c7be7f..9fa27705 100644 --- a/frontend/src/components/audit/SqlAuditDetailDrawer.tsx +++ b/frontend/src/components/audit/SqlAuditDetailDrawer.tsx @@ -146,7 +146,7 @@ export default function SqlAuditDetailDrawer({ onClose={onClose} width="min(760px, calc(100vw - 24px))" title={t('sql_audit.detail.title')} - destroyOnClose + destroyOnHidden styles={{ body: { padding: 20, diff --git a/frontend/src/components/audit/SqlAuditSettingsDrawer.tsx b/frontend/src/components/audit/SqlAuditSettingsDrawer.tsx index 602ee1f6..333fe69a 100644 --- a/frontend/src/components/audit/SqlAuditSettingsDrawer.tsx +++ b/frontend/src/components/audit/SqlAuditSettingsDrawer.tsx @@ -82,7 +82,7 @@ export default function SqlAuditSettingsDrawer({ onClose={onClose} width={440} title={t('sql_audit.settings.title')} - destroyOnClose + destroyOnHidden extra={( diff --git a/frontend/src/components/explain/ExplainWorkbench.tsx b/frontend/src/components/explain/ExplainWorkbench.tsx index 5cc1f747..eadbe369 100644 --- a/frontend/src/components/explain/ExplainWorkbench.tsx +++ b/frontend/src/components/explain/ExplainWorkbench.tsx @@ -262,7 +262,7 @@ export default function ExplainWorkbench({ open, onClose, config, dbName, sql }: width="90%" style={{ top: 20 }} title={{t('sql_analysis.workbench.title')}} - destroyOnClose + destroyOnHidden >
{dbName || t('sql_analysis.slow_query.current_connection')}
} - destroyOnClose + destroyOnHidden >
, ]} - destroyOnClose + destroyOnHidden >
 = ({
         onClose={handleClose}
         maskClosable={false}
         width="min(1100px, 96vw)"
-        destroyOnClose={false}
+        destroyOnHidden={false}
         extra={actionButtons}
       >
         {body}
diff --git a/frontend/src/components/resultDiff/ResultDiffWizard.tsx b/frontend/src/components/resultDiff/ResultDiffWizard.tsx
index fb14c884..d02d2c49 100644
--- a/frontend/src/components/resultDiff/ResultDiffWizard.tsx
+++ b/frontend/src/components/resultDiff/ResultDiffWizard.tsx
@@ -184,7 +184,7 @@ const ResultDiffWizard: React.FC = ({
       onOk={handleStart}
       okText={t('result_diff.wizard.start')}
       confirmLoading={loading}
-      destroyOnClose
+      destroyOnHidden
       width={640}
       maskClosable={!loading}
     >
diff --git a/frontend/src/components/resultDiff/ViewDataVerifyWizard.tsx b/frontend/src/components/resultDiff/ViewDataVerifyWizard.tsx
index 56df3c6f..42420fbe 100644
--- a/frontend/src/components/resultDiff/ViewDataVerifyWizard.tsx
+++ b/frontend/src/components/resultDiff/ViewDataVerifyWizard.tsx
@@ -510,7 +510,7 @@ const ViewDataVerifyWizard: React.FC = ({
       onOk={handleStart}
       okText={t('result_diff.view_verify.start')}
       confirmLoading={running || probing}
-      destroyOnClose
+      destroyOnHidden
       width={680}
       maskClosable={!running}
     >