This commit is contained in:
2026-01-14 14:49:08 +08:00
parent 2ce76820da
commit 7065e5329a
23 changed files with 2236 additions and 413 deletions
+295 -36
View File
@@ -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