feat(error): 实现智能网络错误检测和自动跳转机制
- 新增后端服务可用性检测功能,2秒超时检测 - 实现智能状态检测:用户有认证信息但访问404页面时自动检测 - 添加受保护路径识别逻辑(/system/、/management/、/tools/) - 后端服务不可用时自动清除认证信息并跳转登录页面 - 完善用户界面提示,显示检测状态和自动跳转信息 - 支持手动重试连接和返回登录页面操作
This commit is contained in:
parent
b6dfde844b
commit
65af9b3a10
@ -18,19 +18,229 @@
|
||||
alt=""
|
||||
class="w-1/3"
|
||||
>
|
||||
<n-button
|
||||
type="primary"
|
||||
@click="router.push('/')"
|
||||
>
|
||||
{{ $t('app.backHome') }}
|
||||
</n-button>
|
||||
|
||||
<!-- 网络状态检测提示 -->
|
||||
<div v-if="!isOnline || !isBackendAvailable" class="mt-4 text-center">
|
||||
<n-alert type="warning" :show-icon="false" class="mb-4">
|
||||
<template v-if="!isOnline">
|
||||
网络连接失败,请检查网络状态
|
||||
</template>
|
||||
<template v-else-if="!isBackendAvailable">
|
||||
服务暂时不可用,请稍后重试
|
||||
</template>
|
||||
</n-alert>
|
||||
<p class="text-sm text-gray-500 mb-4">
|
||||
<template v-if="!isOnline">
|
||||
正在检测网络连接...
|
||||
</template>
|
||||
<template v-else-if="!isBackendAvailable">
|
||||
正在检测服务状态...
|
||||
</template>
|
||||
</p>
|
||||
<div class="flex justify-center space-x-1 mb-4">
|
||||
<div class="loading-dot" style="animation-delay: 0s;" />
|
||||
<div class="loading-dot" style="animation-delay: 0.1s;" />
|
||||
<div class="loading-dot" style="animation-delay: 0.2s;" />
|
||||
</div>
|
||||
|
||||
<!-- 服务检测提示 -->
|
||||
<div v-if="(!isBackendAvailable || shouldAutoRedirect) && type === '404'" class="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<p class="text-sm text-blue-600 dark:text-blue-400">
|
||||
<template v-if="!isBackendAvailable">
|
||||
检测到后端服务问题,正在重定向到登录页面...
|
||||
</template>
|
||||
<template v-else-if="shouldAutoRedirect">
|
||||
检测到可能的服务问题,即将自动跳转到登录页面...
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex flex-col items-center space-y-3">
|
||||
<n-button
|
||||
v-if="!isOnline || !isBackendAvailable"
|
||||
type="primary"
|
||||
:loading="isRetrying"
|
||||
@click="handleRetry"
|
||||
>
|
||||
{{ isRetrying ? '重试中...' : '重试连接' }}
|
||||
</n-button>
|
||||
|
||||
<n-button
|
||||
v-if="isOnline && isBackendAvailable"
|
||||
type="primary"
|
||||
@click="handleBackHome"
|
||||
>
|
||||
{{ $t('app.backHome') }}
|
||||
</n-button>
|
||||
|
||||
<n-button
|
||||
v-if="!isOnline || !isBackendAvailable"
|
||||
text
|
||||
@click="handleBackToLogin"
|
||||
>
|
||||
返回登录页面
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { coiMsgError, coiMsgSuccess } from '@/utils/coi'
|
||||
import { local } from '@/utils'
|
||||
|
||||
defineProps<{
|
||||
/** 异常类型 403 404 500 */
|
||||
type: '403' | '404' | '500'
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const isOnline = ref(navigator.onLine)
|
||||
const isRetrying = ref(false)
|
||||
const isBackendAvailable = ref(true)
|
||||
const shouldAutoRedirect = ref(false)
|
||||
|
||||
// 监听网络状态变化
|
||||
function updateOnlineStatus() {
|
||||
isOnline.value = navigator.onLine
|
||||
}
|
||||
|
||||
// 检测后端服务可用性
|
||||
async function checkBackendAvailability() {
|
||||
try {
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 2000)
|
||||
|
||||
await fetch('/api/login/captchaPng', {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
isBackendAvailable.value = true
|
||||
}
|
||||
catch {
|
||||
isBackendAvailable.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 智能状态检测
|
||||
async function checkServiceStatus() {
|
||||
const currentRoute = router.currentRoute.value
|
||||
|
||||
// 如果是404页面,很可能是后端服务问题
|
||||
if (currentRoute.name === '404' || currentRoute.name === 'notFound') {
|
||||
// 检查是否有认证信息(说明用户之前是登录状态)
|
||||
const hasAuthToken = Boolean(local.get('accessToken'))
|
||||
|
||||
if (hasAuthToken) {
|
||||
// 先尝试检查后端服务状态
|
||||
try {
|
||||
await checkBackendAvailability()
|
||||
|
||||
// 如果后端服务不可用,立即跳转到登录页面
|
||||
if (!isBackendAvailable.value) {
|
||||
handleBackToLogin()
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// 检测异常,忽略错误继续执行后续逻辑
|
||||
}
|
||||
|
||||
// 简化方案:如果用户在一个受保护的页面但显示404,直接跳转
|
||||
const isProtectedPath = currentRoute.fullPath.includes('/system/')
|
||||
|| currentRoute.fullPath.includes('/management/')
|
||||
|| currentRoute.fullPath.includes('/tools/')
|
||||
|
||||
if (isProtectedPath) {
|
||||
shouldAutoRedirect.value = true
|
||||
setTimeout(() => {
|
||||
handleBackToLogin()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重试连接
|
||||
async function handleRetry() {
|
||||
isRetrying.value = true
|
||||
|
||||
try {
|
||||
// 检查网络状态
|
||||
updateOnlineStatus()
|
||||
|
||||
// 检查后端服务可用性
|
||||
await checkBackendAvailability()
|
||||
|
||||
if (isOnline.value && isBackendAvailable.value) {
|
||||
coiMsgSuccess('服务连接已恢复')
|
||||
// 尝试重新加载当前页面
|
||||
window.location.reload()
|
||||
}
|
||||
else if (!isOnline.value) {
|
||||
coiMsgError('网络连接失败,请检查网络设置')
|
||||
}
|
||||
else if (!isBackendAvailable.value) {
|
||||
coiMsgError('后端服务暂时不可用,请稍后重试')
|
||||
}
|
||||
}
|
||||
catch {
|
||||
coiMsgError('重试失败,请稍后再试')
|
||||
}
|
||||
finally {
|
||||
isRetrying.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 返回首页
|
||||
function handleBackHome() {
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
// 返回登录页面
|
||||
function handleBackToLogin() {
|
||||
// 清除认证信息
|
||||
local.remove('accessToken')
|
||||
local.remove('refreshToken')
|
||||
|
||||
// 跳转到登录页面
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
// 网络状态监听
|
||||
onMounted(async () => {
|
||||
window.addEventListener('online', updateOnlineStatus)
|
||||
window.addEventListener('offline', updateOnlineStatus)
|
||||
|
||||
// 初始检查服务状态
|
||||
await checkServiceStatus()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('online', updateOnlineStatus)
|
||||
window.removeEventListener('offline', updateOnlineStatus)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Loading点动画 */
|
||||
.loading-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
animation: bounce 1.4s ease-in-out infinite both;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user