From 2475a98f2bc8105ccb5d84abbbfca12e31d62d2d Mon Sep 17 00:00:00 2001 From: jxxghp Date: Fri, 28 Aug 2026 07:06:37 +0800 Subject: [PATCH] fix(connection): hide reconnecting notification --- .../default/components/OfflinePage.vue | 28 +++--------- .../components/__tests__/OfflinePage.spec.ts | 43 +++++++++---------- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/src/layouts/default/components/OfflinePage.vue b/src/layouts/default/components/OfflinePage.vue index 2b623a35..b5c0dbe8 100644 --- a/src/layouts/default/components/OfflinePage.vue +++ b/src/layouts/default/components/OfflinePage.vue @@ -9,49 +9,35 @@ const { connectionStatus, connectionReason } = useGlobalOfflineStatus() const { isRestarting } = useSystemRestartStatus() const shownConnectionPromptKeys = new Set() -const isChecking = computed(() => connectionStatus.value === 'checking') -const statusTitle = computed(() => (isChecking.value ? t('app.connectionChecking') : t('app.serviceUnavailable'))) const statusMessage = computed(() => { if (connectionReason.value === 'browser-offline') return t('app.browserOfflineMessage') if (connectionReason.value === 'timeout') return t('app.serviceTimeoutMessage') - if (isChecking.value) return t('app.connectionCheckingMessage') return t('app.serviceUnavailableMessage') }) /** 拼接离线状态提示文案,供 Toast 或 Agent 助手气泡展示。 */ function buildConnectionPromptMessage() { - return `${statusTitle.value}:${statusMessage.value}` + return `${t('app.serviceUnavailable')}:${statusMessage.value}` } -/** 根据当前连接状态选择 Toast 级别,Agent 助手可用时会由全局 Toast 路由接管。 */ +/** 显示连接失败 Toast,Agent 助手可用时会由全局 Toast 路由接管。 */ function showConnectionPrompt() { - const message = buildConnectionPromptMessage() - const options = { - timeout: isChecking.value ? 5000 : 7000, - } - - if (isChecking.value) { - toast.warning(message, options) - return - } - - toast.error(message, options) + toast.error(buildConnectionPromptMessage(), { timeout: 7000 }) } -/** 在同一轮连接异常内按状态去重提示,并在恢复在线后允许下一轮提示重新出现。 */ +/** 仅在确认连接失败后提示,并在恢复在线后允许下一轮提示重新出现。 */ function handleConnectionStatusChange() { if (connectionStatus.value === 'online') { shownConnectionPromptKeys.clear() return } + if (connectionStatus.value !== 'offline') return + // 重启期间由重启进度弹窗承载反馈,避免离线提示与进度提示叠加。 if (isRestarting.value) return - const promptKey = - connectionStatus.value === 'checking' - ? connectionStatus.value - : `${connectionStatus.value}:${connectionReason.value || 'unknown'}` + const promptKey = `${connectionStatus.value}:${connectionReason.value || 'unknown'}` if (shownConnectionPromptKeys.has(promptKey)) return diff --git a/src/layouts/default/components/__tests__/OfflinePage.spec.ts b/src/layouts/default/components/__tests__/OfflinePage.spec.ts index c93fe455..c4ce90f7 100644 --- a/src/layouts/default/components/__tests__/OfflinePage.spec.ts +++ b/src/layouts/default/components/__tests__/OfflinePage.spec.ts @@ -7,7 +7,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' const mocks = vi.hoisted(() => ({ error: vi.fn(), isRestarting: { value: false }, - warning: vi.fn(), })) vi.mock('@/composables/useSystemRestart', () => ({ @@ -20,7 +19,7 @@ vi.mock('vue-i18n', async importOriginal => ({ })) vi.mock('vue-toastification', () => ({ - useToast: () => ({ error: mocks.error, warning: mocks.warning }), + useToast: () => ({ error: mocks.error }), })) describe('OfflinePage', () => { @@ -29,7 +28,6 @@ describe('OfflinePage', () => { beforeEach(() => { mocks.error.mockReset() - mocks.warning.mockReset() mocks.isRestarting.value = false status.markServerOnline() wrapper = mount(OfflinePage) @@ -40,19 +38,23 @@ describe('OfflinePage', () => { status.markServerOnline() }) - it('同一轮 checking 状态只提示一次,并使用非阻断警告', async () => { + it('连接检查期间保持静默,确认离线后才显示错误', async () => { status.markConnectionChecking('network-error') await nextTick() - expect(mocks.warning).toHaveBeenCalledWith('app.connectionChecking:app.connectionCheckingMessage', { - timeout: 5000, - }) + expect(mocks.error).not.toHaveBeenCalled() status.markConnectionChecking('timeout') await nextTick() - expect(mocks.warning).toHaveBeenCalledTimes(1) expect(mocks.error).not.toHaveBeenCalled() + + status.markServerOffline('timeout') + await nextTick() + + expect(mocks.error).toHaveBeenCalledWith('app.serviceUnavailable:app.serviceTimeoutMessage', { + timeout: 7000, + }) }) it('按离线原因显示错误,并允许不同离线状态分别提示', async () => { @@ -76,42 +78,39 @@ describe('OfflinePage', () => { expect(mocks.error).toHaveBeenNthCalledWith(3, 'app.serviceUnavailable:app.serviceUnavailableMessage', { timeout: 7000, }) - expect(mocks.warning).not.toHaveBeenCalled() }) - it('在线恢复后允许下一轮 checking 再次提示', async () => { - status.markConnectionChecking() + it('在线恢复后允许下一轮离线提示重新出现', async () => { + status.markServerOffline() await nextTick() - expect(mocks.warning).toHaveBeenCalledTimes(1) + expect(mocks.error).toHaveBeenCalledTimes(1) status.markServerOnline() await nextTick() - status.markConnectionChecking() + status.markServerOffline() await nextTick() - expect(mocks.warning).toHaveBeenCalledTimes(2) + expect(mocks.error).toHaveBeenCalledTimes(2) }) it('重启期间不提示,但在线恢复仍清空上一轮去重状态', async () => { - status.markConnectionChecking() + status.markServerOffline() await nextTick() - expect(mocks.warning).toHaveBeenCalledTimes(1) + expect(mocks.error).toHaveBeenCalledTimes(1) mocks.isRestarting.value = true status.markServerOffline('timeout') await nextTick() - expect(mocks.error).not.toHaveBeenCalled() - expect(mocks.warning).toHaveBeenCalledTimes(1) + expect(mocks.error).toHaveBeenCalledTimes(1) status.markServerOnline() await nextTick() - expect(mocks.error).not.toHaveBeenCalled() - expect(mocks.warning).toHaveBeenCalledTimes(1) + expect(mocks.error).toHaveBeenCalledTimes(1) mocks.isRestarting.value = false - status.markConnectionChecking() + status.markServerOffline() await nextTick() - expect(mocks.warning).toHaveBeenCalledTimes(2) + expect(mocks.error).toHaveBeenCalledTimes(2) }) })