更新
This commit is contained in:
+295
-36
@@ -1,57 +1,316 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import Layout from '@/layouts/index.vue'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import systemRouter from './systemRouter'
|
||||
import userRoutes from '@/config/routes'
|
||||
import NProgress from 'nprogress'
|
||||
import tool from '@/utils/tool'
|
||||
import i18n from '@/i18n'
|
||||
import { beforeEach, afterEach } from './scrollBehavior'
|
||||
import '@/assets/css/nprogress.css'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: () => import('@/pages/login/index.vue'),
|
||||
meta: { requiresAuth: false },
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'Dashboard',
|
||||
component: Layout,
|
||||
redirect: '/home',
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
component: () => import('@/pages/home/Dashboard.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
// 配置 NProgress
|
||||
NProgress.configure({
|
||||
easing: 'ease',
|
||||
speed: 500,
|
||||
showSpinner: false,
|
||||
trickleSpeed: 200,
|
||||
minimum: 0.3
|
||||
})
|
||||
|
||||
// 匹配pages里面所有的.vue文件
|
||||
const modules = import.meta.glob('@/pages/**/*.vue')
|
||||
|
||||
// 特殊路由模块
|
||||
const otherModules = {
|
||||
'404': () => import('@/layouts/other/404.vue'),
|
||||
empty: () => import('@/layouts/other/empty.vue')
|
||||
}
|
||||
|
||||
// 系统路由
|
||||
const routes = systemRouter
|
||||
|
||||
// 是否已加载过动态/静态路由
|
||||
let isGetRouter = false
|
||||
|
||||
// 404路由移除函数
|
||||
let routes_404_r = null
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: routes,
|
||||
routes: routes
|
||||
})
|
||||
|
||||
/**
|
||||
* 设置页面标题
|
||||
* @param {Object} meta - 路由元信息
|
||||
*/
|
||||
const setPageTitle = (meta) => {
|
||||
const title = 'VueAdmin'
|
||||
if (meta?.title) {
|
||||
try {
|
||||
const translatedTitle = i18n.global.te(meta.title) ? i18n.global.t(meta.title) : meta.title
|
||||
document.title = `${translatedTitle} - ${title}`
|
||||
} catch (error) {
|
||||
document.title = `${meta.title} - ${title}`
|
||||
}
|
||||
} else {
|
||||
document.title = title
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查路由是否需要认证
|
||||
* @param {Object} to - 目标路由
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const checkAuthRequired = (to) => {
|
||||
return to.matched.some((record) => record.meta.requiresAuth !== false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除404路由
|
||||
*/
|
||||
const remove404Route = () => {
|
||||
if (routes_404_r) {
|
||||
router.removeRoute('404')
|
||||
routes_404_r = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加404路由
|
||||
*/
|
||||
const add404Route = () => {
|
||||
if (!routes_404_r) {
|
||||
routes_404_r = router.addRoute({
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: '404',
|
||||
hidden: true,
|
||||
component: otherModules['404']
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载动态路由
|
||||
* @param {Object} to - 目标路由对象
|
||||
* @returns {boolean} 是否加载成功
|
||||
*/
|
||||
const loadDynamicRoutes = async (to) => {
|
||||
try {
|
||||
// 从 store 获取菜单和用户信息
|
||||
const userStore = useUserStore()
|
||||
|
||||
const apiMenu = userStore.getMenu() || []
|
||||
const userInfo = userStore.userInfo
|
||||
|
||||
// 如果没有用户信息,不做处理
|
||||
if (!userInfo) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 根据用户角色过滤静态路由
|
||||
const userMenu = treeFilter(userRoutes, (node) => {
|
||||
return node.meta.role
|
||||
? node.meta.role.some((item) => userInfo.role?.includes(item))
|
||||
: true
|
||||
})
|
||||
|
||||
// 合并静态路由和API菜单
|
||||
const menu = [...userMenu, ...apiMenu]
|
||||
|
||||
// 转换异步路由
|
||||
const menuRouter = filterAsyncRouter(menu)
|
||||
|
||||
// 将树形路由转平铺
|
||||
const flatMenuRouter = tool.tree_to_list(menuRouter)
|
||||
|
||||
// 添加所有路由
|
||||
flatMenuRouter.forEach((item) => {
|
||||
router.addRoute('layout', item)
|
||||
})
|
||||
|
||||
// 添加404路由(必须在所有路由之后)
|
||||
add404Route()
|
||||
|
||||
isGetRouter = true
|
||||
|
||||
// 检查目标路由是否存在
|
||||
const hasRoute = router.hasRoute(to.name) || to.matched.length > 0
|
||||
|
||||
if (!hasRoute && to.name !== '404') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('加载动态路由失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 路由拦截器 - 全局前置守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 开始进度条
|
||||
NProgress.start()
|
||||
|
||||
// 设置页面标题
|
||||
setPageTitle(to.meta)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const isLoggedIn = userStore.isLoggedIn()
|
||||
const requiresAuth = checkAuthRequired(to)
|
||||
|
||||
// 检查路由是否需要认证
|
||||
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth !== false)
|
||||
// 处理404页面
|
||||
if (to.name === '404') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// 处理登录页
|
||||
if (to.path === '/login') {
|
||||
isGetRouter = false
|
||||
remove404Route()
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是系统路由,直接放行
|
||||
if (routes.some((r) => r.path === to.path)) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// 需要认证但未登录,重定向到登录页
|
||||
if (requiresAuth && !isLoggedIn) {
|
||||
// 需要认证但未登录,重定向到登录页
|
||||
NProgress.done()
|
||||
isGetRouter = false
|
||||
userStore.clearMenu()
|
||||
next({
|
||||
path: '/login',
|
||||
query: { redirect: to.fullPath }, // 保存目标路径,登录后可以跳转回去
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
} else if (to.path === '/login' && isLoggedIn) {
|
||||
// 已登录但访问登录页,重定向到首页
|
||||
next({ path: '/' })
|
||||
} else {
|
||||
// 其他情况正常跳转
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// 整页路由处理
|
||||
if (to.meta.fullpage) {
|
||||
to.matched = [to.matched[to.matched.length - 1]]
|
||||
}
|
||||
|
||||
// 调用前置守卫
|
||||
beforeEach(to, from)
|
||||
|
||||
// 加载动态/静态路由
|
||||
if (!isGetRouter) {
|
||||
const loadSuccess = await loadDynamicRoutes(to)
|
||||
|
||||
// 如果路由加载失败,跳转到404
|
||||
if (!loadSuccess) {
|
||||
next({ name: '404', replace: true })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 检查路由是否存在(动态路由已加载后)
|
||||
if (isGetRouter) {
|
||||
const hasRoute = router.hasRoute(to.name)
|
||||
// 如果路由不存在且不是404页面,跳转到404
|
||||
if (!hasRoute && to.name !== '404' && to.matched.length === 0) {
|
||||
next({ name: '404', replace: true })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
// 全局后置钩子
|
||||
router.afterEach((to, from) => {
|
||||
// 调用后置钩子
|
||||
afterEach(to, from)
|
||||
|
||||
// 结束进度条
|
||||
NProgress.done()
|
||||
})
|
||||
|
||||
// 路由错误处理
|
||||
router.onError((error) => {
|
||||
NProgress.done()
|
||||
ElNotification.error({
|
||||
title: '路由错误',
|
||||
message: error.message
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 动态加载组件
|
||||
* @param {string} component - 组件路径
|
||||
* @returns {Function}
|
||||
*/
|
||||
function loadComponent(component) {
|
||||
if (!component) {
|
||||
return otherModules.empty
|
||||
}
|
||||
|
||||
for (const path in modules) {
|
||||
const dir = path.split('pages/')[1]?.split('.vue')[0]
|
||||
if (dir === component || dir === `${component}/index`) {
|
||||
return () => modules[path]()
|
||||
}
|
||||
}
|
||||
|
||||
return otherModules.empty
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换异步路由
|
||||
* @param {Array} routerMap - 路由映射
|
||||
* @returns {Array} 转换后的路由数组
|
||||
*/
|
||||
function filterAsyncRouter(routerMap) {
|
||||
if (!routerMap || !Array.isArray(routerMap)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return routerMap.map((item) => {
|
||||
const meta = item.meta || {}
|
||||
|
||||
// 处理外部链接特殊路由
|
||||
if (meta.type === 'iframe') {
|
||||
meta.url = item.path
|
||||
item.path = `/i/${item.name}`
|
||||
}
|
||||
|
||||
return {
|
||||
path: item.path,
|
||||
name: item.name,
|
||||
meta: meta,
|
||||
redirect: item.redirect,
|
||||
children: item.children ? filterAsyncRouter(item.children) : undefined,
|
||||
component: loadComponent(item.component)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤树结构
|
||||
* @param {Array} tree - 树结构数据
|
||||
* @param {Function} func - 过滤函数
|
||||
* @returns {Array} 过滤后的树结构
|
||||
*/
|
||||
function treeFilter(tree, func) {
|
||||
if (!tree || !Array.isArray(tree)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return tree
|
||||
.map((node) => ({ ...node }))
|
||||
.filter((node) => {
|
||||
node.children = node.children ? treeFilter(node.children, func) : undefined
|
||||
return func(node) || (node.children && node.children.length > 0)
|
||||
})
|
||||
}
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user