fix(connection): hide reconnecting notification

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