194 lines
5.9 KiB
TypeScript
194 lines
5.9 KiB
TypeScript
import { createRouter, createWebHistory } from 'vue-router'
|
||
import { useUserStore } from '@/stores/modules/user'
|
||
import Layout from '@/components/layout/Layout.vue'
|
||
import { setRouter, handleRouter } from './handleRouter'
|
||
import { getStorage, setStorage } from '@/utils/storage'
|
||
|
||
const routes = [
|
||
// user路由
|
||
{
|
||
path: '/user',
|
||
name: 'User',
|
||
component: () => import('@/views/user/index.vue'),
|
||
redirect: '/user/login',
|
||
children: [
|
||
{
|
||
path: 'login',
|
||
name: 'Login',
|
||
component: () => import('@/views/user/Login.vue'),
|
||
meta: { isAuthPage: true, title: '登录', keepAlive: false },
|
||
},
|
||
{
|
||
path: 'register',
|
||
name: 'Register',
|
||
component: () => import('@/views/user/Register.vue'),
|
||
meta: { isAuthPage: true, title: '注册', keepAlive: false },
|
||
},
|
||
{
|
||
path: 'forgot-password',
|
||
name: 'ForgotPassword',
|
||
component: () => import('@/views/user/ForgotPassword.vue'),
|
||
meta: { isAuthPage: true, title: '忘记密码', keepAlive: false },
|
||
},
|
||
{
|
||
path: 'change-password',
|
||
name: 'ChangePassword',
|
||
component: () => import('@/views/user/ChangePassword.vue'),
|
||
meta: { requiresAuth: true, title: '修改密码', keepAlive: false },
|
||
},
|
||
{
|
||
path: 'bind-email',
|
||
name: 'BindEmail',
|
||
component: () => import('@/views/user/bindEmail.vue'),
|
||
meta: { requiresAuth: true, title: '绑定邮箱', keepAlive: false },
|
||
},
|
||
{
|
||
path: 'bind-phone',
|
||
name: 'BindPhone',
|
||
component: () => import('@/views/user/bindPhone.vue'),
|
||
meta: { requiresAuth: true, title: '绑定手机号', keepAlive: false },
|
||
},
|
||
],
|
||
},
|
||
// 带侧边栏布局的核心业务页面
|
||
{
|
||
path: '/',
|
||
name: 'Layout',
|
||
component: Layout, // 全局布局(Sidebar + Header + MainContent)
|
||
redirect: '/dashboard',
|
||
meta: { requiresAuth: true }, // 需要登录
|
||
children: [
|
||
// 首页
|
||
{
|
||
path: '/dashboard',
|
||
name: 'Dashboard',
|
||
component: () => import('@/views/dashboard/index.vue'),
|
||
meta: {
|
||
title: '首页', // 左侧菜单显示的标题
|
||
keepAlive: false,
|
||
},
|
||
},
|
||
],
|
||
},
|
||
{
|
||
path: '/realName',
|
||
name: '实名认证',
|
||
component: () => import('@/views/realName/index.vue'),
|
||
meta: { requiresAuth: true, title: '实名认证', keepAlive: false },
|
||
},
|
||
{
|
||
path: '/livenessVerification',
|
||
name: '活体认证',
|
||
component: () => import('@/views/livenessVerification/index.vue'),
|
||
meta: { requiresAuth: true, title: '活体认证', keepAlive: false },
|
||
},
|
||
{
|
||
path: '/:pathMatch(.*)*',
|
||
redirect: () => {
|
||
const token = getStorage('token')
|
||
return token ? '/dashboard' : '/user/login'
|
||
}, // 404 重定向到登录页,避免循环
|
||
},
|
||
]
|
||
|
||
const router = createRouter({
|
||
history: createWebHistory(),
|
||
routes,
|
||
})
|
||
|
||
// 标记:动态路由是否已初始化完成
|
||
let dynamicRoutesReady = false
|
||
|
||
router.beforeEach(async (to, _from) => {
|
||
const userStore = useUserStore()
|
||
const isLoggedIn = !!userStore.token
|
||
const isRealNameVerified = userStore.userInfo?.real_check !== 1
|
||
|
||
console.log('dynamicRoutesReady', userStore.userInfo)
|
||
console.log('isLoggedIn', isLoggedIn)
|
||
console.log('isRealNameVerified', isRealNameVerified)
|
||
console.log('to.meta.requiresAuth', to.meta.requiresAuth)
|
||
console.log('to.meta.isAuthPage', to.meta.isAuthPage)
|
||
|
||
// 1. 未登录访问需要认证的页 → 登录页
|
||
if (!isLoggedIn && to.meta.requiresAuth) {
|
||
return '/user/login'
|
||
}
|
||
|
||
// 2. ✅ 动态路由恢复(只执行一次)
|
||
if (isLoggedIn && !dynamicRoutesReady) {
|
||
const menuList = (getStorage('menusInfo') as any[]) || []
|
||
|
||
// 🌟 核心:无论任何角色,始终确保首页路由存在
|
||
const hasDashboardRoute = router.getRoutes().some(
|
||
(route) => route.path === '/dashboard',
|
||
)
|
||
if (!hasDashboardRoute) {
|
||
router.addRoute('Layout', {
|
||
path: '/dashboard',
|
||
name: 'Dashboard',
|
||
component: () => import('@/views/dashboard/index.vue'),
|
||
meta: {
|
||
title: '首页',
|
||
keepAlive: false,
|
||
},
|
||
})
|
||
}
|
||
|
||
if (menuList.length > 0) {
|
||
const routeList = handleRouter(menuList).filter(
|
||
(item) => item.path && item.path !== '/dashboard',
|
||
)
|
||
routeList.forEach((route) => router.addRoute('Layout', route))
|
||
}
|
||
dynamicRoutesReady = true
|
||
// 返回目标路由以触发重新匹配
|
||
return to.redirectedFrom?.fullPath || true
|
||
}
|
||
|
||
// 3. 白名单页面(实名认证、活体认证、登录/注册相关页面)
|
||
// 这些页面即使未实名认证也可以访问
|
||
const whiteList = [
|
||
'/realName',
|
||
'/livenessVerification',
|
||
'/user/login',
|
||
'/user/register',
|
||
'/user/forgot-password',
|
||
]
|
||
const isWhiteListPage = whiteList.some((path) => to.path === path)
|
||
|
||
// 4. 已登录但未完成实名认证 → 只能访问白名单页面
|
||
if (isLoggedIn && !isRealNameVerified && !isWhiteListPage) {
|
||
// 未实名认证访问非白名单页面 → 跳转到登录页
|
||
return '/user/login'
|
||
}
|
||
|
||
// 5. 已登录访问登录页(从认证页面退出)→ 允许
|
||
if (isLoggedIn && !isRealNameVerified && to.path === '/user/login') {
|
||
// 未认证用户主动跳转到登录页(用于退出登录),允许放行
|
||
return true
|
||
}
|
||
|
||
// 6. 已登录访问登录/注册页 → 首页(如果已认证)或实名认证页(如果未认证)
|
||
if (isLoggedIn && to.meta.isAuthPage) {
|
||
return isRealNameVerified ? '/' : '/realName'
|
||
}
|
||
|
||
// 7. 已完成认证的用户访问实名认证或活体认证页 → 首页
|
||
if (isLoggedIn && isRealNameVerified && isWhiteListPage && (to.path === '/realName' || to.path === '/livenessVerification')) {
|
||
return '/'
|
||
}
|
||
|
||
// 8. 其他情况正常放行
|
||
return true
|
||
})
|
||
|
||
// 可选:设置页面标题
|
||
router.afterEach((to) => {
|
||
if (to.meta.title) {
|
||
document.title = to.meta.title as string
|
||
}
|
||
})
|
||
|
||
export default router
|