做法:
在utils文件夹>>>map-menus.ts文件,使用for循环当前用户菜单数据的方式动态生成需要动态挂载的路由数组。在用户登录成功以后把这些路由数据动态挂载。
1. 创建路由
router>>index.ts
import { login_token } from '@/global/constance.'
import { localCache } from '@/utils/cache'
import { createRouter, createWebHashHistory } from 'vue-router'
import { firstRoute } from '@/utils/map-menus'
const router = createRouter({
history: createWebHashHistory(),
//映射关系:path=>component
routes: [
{
path: '/',
redirect: '/main'
},
{
path: '/login',
component: () => import('../view/login/Login.vue')
},
//----------------------------------------------------------------------------------------
{
path: '/main',
name: 'main',
// children:[],
// 注意这里的name要写,这样调用addRoute('main',item)方法时才会把item添加到children数组中
component: () => import('../view/main/Main.vue')
},
//----------------------------------------------------------------------------------------
{
path: '/:pathMatch(.*)',
component: () => import('../view/not-found/NotFound.vue')
}
]
})
const token = localCache.getCache(login_token)
//导航守卫-------------------------------------------------------------------------------------
router.beforeEach((to) => {
console.log('router.beforeEach((to) >>')
// if (to.path.startsWith('main') && !token)如果登录后里面所有的页面都是main开头页可以这样写
if (to.path !== '/login' && !token) {
return '/login'
// 当用户输入main页面或登录成功后,默认跳转到菜单的第一项
} else if (to.path ==='/main' && token) {
return firstRoute.url
}
})
export default router
2.根据当前用户的菜单数据,通过for循环生成main路由下的所有children数据
文件位置:utils>>>map-menus.ts
import type { RouteRecordRaw } from 'vue-router'
// 获取所有菜单url地址
const localRouter: RouteRecordRaw[] = []
// const files: Record<string, any> = import.meta.glob(
// '../../view/main/**/*.vue',
// { eager: true }
// )
// { eager: true }立即加载函数,将结果给出
//从文件系统导入多个模块 ../../view/main/**/*.vue
const files: Record<string, any> = import.meta.glob('../view/main/**/*.vue', {
eager: true
})
export let firstRoute: any = null
//获取当前用户所有main权限路由,将将要动态挂载的数据localRouter通过调用getRouterItem方法暴露出去
export function getRouterItem(menueInfo: any[]) {
//for循环拿到需要动态挂载在main的children下的路由数组
for (const item of menueInfo) {
for (const item2 of item.children) {
const url: string = `../view${item2.url}/${item2.url.split('/')[3]}.vue`
//拿到component模块
const module = files[url]?.default
// 记录第一个被匹配的菜单
if (!firstRoute && item) firstRoute = item2
// 通过for循环把main下的children中的每个对象oush进localRouter
localRouter.push({
path: item2.url,
component: module
})
}
}
return localRouter
}
3.用户登录成功后,调用store>>>login>>>login.ts里action中的loginAccountAction方法
store>>>login>>>login.ts
import { defineStore } from 'pinia'
import {
accountLoginRequest,
getMenueInfoRequest,
getUserInfoRequest
} from '@/service/login/login'
import { localCache } from '@/utils/cache'
import { login_token } from '@/global/constance.'
import type { IAccount } from '@/type'
import router from '@/router'
// 获取用户可登录的url地址
// import localRouter from '@/router/main'
import { getRouterItem } from '@/utils/map-menus'
//----------------------------------------------------------
//将token定义为变量,避免使用的时候拼写错误
// const login_token = 'token'
interface ILoginState {
token: string
userInfo: any
menueInfo: any
}
const useLoginStore = defineStore('login', {
state: (): ILoginState => ({
token: '',
userInfo: {},
menueInfo: []
}),
actions: {
async loginAccountAction(account: IAccount) {
// 发送登录请求,将获取的结果进行赋值
const loginResult = await accountLoginRequest(account)
//发送用户信息请求
// 将数据保存到pinia中
const id = loginResult.data.id
this.token = loginResult.data.token
const name = loginResult.data.name
// 为避免刷新时数据消失,将请求存储到localhost
localCache.setCache(login_token, this.token)
// 请求用户信息
const userInfo = await getUserInfoRequest(id)
this.userInfo = userInfo.data
console.log('this.userInfo :>> ', this.userInfo)
// 获取菜单信息
const menueInfo = await getMenueInfoRequest(id)
// console.log('menueInfo :>> ', menueInfo)
this.menueInfo = menueInfo?.data ?? []
localCache.setCache('menueInfo', this.menueInfo)
localCache.setCache('userInfo', this.userInfo)
//====================
//动态挂载路由
//调用router里暴露的dynamicAddRoute方法动态挂载路由的方法
// dynamicAddRoute()
if (menueInfo.length > 0) {
const localRoutes = getRouterItem(menueInfo)
localRoutes.forEach((item) => {
// console.log('item :>> ', item)
router.addRoute('main', item)
})
}
//================================
//页面跳转到main
router.push('/main')
},
loadingLocalCacheAction() { //解决用户刷新时,路由信息丢失
const token = localCache.getCache(login_token)
const userInfo = localCache.getCache('userInfo')
const menueInfo = localCache.getCache('menueInfo')
// 当有用户信息时,动态挂载路由
if (token && userInfo && menueInfo) {
const localRoutes = getRouterItem(menueInfo)
localRoutes.forEach((item) => {
router.addRoute('main', item)
})
}
}
}
})
export default useLoginStore
4.创建registerStore方法,在挂载app,注册pinia后再调用piniade的useLoginStore里的loadingLocalCacheAction方法。
store>>>index.ts
import { createPinia } from 'pinia'
import type { App } from 'vue'
import useLoginStore from './login/login'
const pinia = createPinia()
function registerStore(app: App<Element>) {
app.use(pinia)
const loginStore = useLoginStore()
loginStore.loadingLocalCacheAction()
}
export default registerStore
5.在main.ts中引入store>>>index.ts的registerStore,注意要将此方法放在app.use(router)前面
import { createApp } from 'vue'
import App from './App.vue'
import 'normalize.css'
import './assets/css/index.less'
import './assets/main.css'
import router from './router'
import registerIcon from './global/register-icons'
import registerStore from './store'
console.log('main.ts :>> ')
const app = createApp(App)
// app.use(pinia)
//将通过引入registerStore的方式代码看起来更舒适
app.use(registerStore)
// 注意router要放在后面动态添加的路由才能挂载成功
app.use(router)
app.use(registerIcon)
app.mount('#app')
// createApp(App).use(router).mount('#app')
![](https://i-blog.csdnimg.cn/blog_migrate/2bf17e4ef965122b2e295a903b37ea8a.png)