- 新增后端服务可用性检测功能,2秒超时检测 - 实现智能状态检测:用户有认证信息但访问404页面时自动检测 - 添加受保护路径识别逻辑(/system/、/management/、/tools/) - 后端服务不可用时自动清除认证信息并跳转登录页面 - 完善用户界面提示,显示检测状态和自动跳转信息 - 支持手动重试连接和返回登录页面操作
247 lines
6.2 KiB
Vue
247 lines
6.2 KiB
Vue
<template>
|
||
<div class="flex-col-center h-full">
|
||
<img
|
||
v-if="type === '403'"
|
||
src="@/assets/svg/error-403.svg"
|
||
alt=""
|
||
class="w-1/3"
|
||
>
|
||
<img
|
||
v-if="type === '404'"
|
||
src="@/assets/svg/error-404.svg"
|
||
alt=""
|
||
class="w-1/3"
|
||
>
|
||
<img
|
||
v-if="type === '500'"
|
||
src="@/assets/svg/error-500.svg"
|
||
alt=""
|
||
class="w-1/3"
|
||
>
|
||
|
||
<!-- 网络状态检测提示 -->
|
||
<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>
|