从零开始vue3+vite+ts+pinia+router4后台管理(3)-动态路由
项目地址
我们在做项目中经常遇到有后端返回的文件路径(比如order/orderList),然后前端根据获取的路由数组动态加载路由,今天我们就看看怎么样实现的吧!本项目vue版本vue3 vue-router4 pinia Vite
完成的
实现思路
在路由钩子里面判断是否首次进入系统(permission.ts)
init为true说明已经获取过路由,就直接放行,init为false则向后台请求用户路由
获取路由
解析路由,存储权限
使用router的
api addRoute
拼接路由存储路由
init改为true,路由初始化完成
放行路由
###1 引入vue-router 设置静态路由 登陆,首页等。。。
router/index.ts
import {createRouter, createWebHistory} from 'vue-router'
import Layout from '@/layout/index.vue'
//constantRoutes 静态路由 登陆,首页等。。。
export const constantRoutes = [
{
path: '/',//默认首页
redirect: '/dashboard',
hidden: true,
},
{
path: "/dashboard",
name: 'Dashboard',
meta: {
title: '首页',
icon: "House",
},
component: Layout
},
{
path: "/login",
name: "Login",
meta: {
title: '登录'
},
component: () => import('@/views/user/login/index.vue'),
hidden: true
},
{
path: '/:pathMatch(.*)*',// 此写法解决动态路由页面刷新的 warning 警告
component: () => import('@/views/user/error-page/404.vue'),
hidden: true
},
]
//动态路由 asyncRoutes
export const asyncRoutes = []
const router = createRouter({
history: createWebHistory(),// HTML5的history模式
routes: constantRoutes
})
export default router
2 获取后端路由数组,动态挂载路由permission.ts
permission.ts
入口权限判断ts 和mian.ts
同一级
import router from './router/index'
import { useUserStore } from '@/store/user'
import { usePermissionStore } from '@/store/permission'
import {getToken} from '@/utils/storage.ts'
import NProgress from 'nprogress'
import'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // 白名单
router.beforeEach(async (to) => {
NProgress.start();
document.title = `${to.meta.title} | vu3dny-admin`
const hasToken = getToken('Access-Token')
const userStore = useUserStore()
const permissionStore = usePermissionStore()
if (hasToken) {//判断token是否存在 存在即为已经登录
if (to.path !== "/login") {
if (userStore.init) { // 获取了动态路由 init一定true,就无需再次请求 直接放行
return true
}else {
// init为false,一定没有获取动态路由,就跳转到获取动态路由的方法
const result = await userStore.getInfo() //获取路由
const accessRoutes = await permissionStore.generateRoutes(result.menus) //解析路由,存储路由
// console.log(accessRoutes);
// 动态挂载路由
accessRoutes.forEach((route) => {
router.addRoute(route)
})
userStore.init = true//init改为true,路由初始化完成
return { ...to, replace: true }// hack方法 确保addRoute已完成
}
}else {
NProgress.done()
return '/'
}
}else {
// 白名单,直接放行
if (whiteList.indexOf(to.path) > -1) return true
// 非白名单,去登录
else return '/login'
NProgress.done()
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
3 递归解析路由 根据后端返回文件路径匹配路由
import.meta.glob的妙用:Vite是一个现代工具,你主要使用ES模块。而Vite用方便的工具扩展了全局
import.meta
对象。例如,它添加了一个import.meta.glob
函数,允许你从路径中解析文件。比如后端返回的是oder/orderList 路由 需要前端匹配 你需要在views文件夹 新建一个order文件夹 然后文件夹新建一个orderList.vue
const modules = import.meta.glob('@/views/**/**.vue')
router的component
component = modules[
/src/views${e.url}]
当然 如果你用的是vue2 vueCli构建的vue项目 你可以用 require引入 import.meta.glob只适用于Vite
store/permission.ts 生成动态路由权限
import {defineStore} from 'pinia'
import {constantRoutes} from '@/router'
import Layout from '@/layout/index.vue'
const modules = import.meta.glob('@/views/**/**.vue')
// console.log(modules);
export const filterAsyncRoutes = (routerList) => {
//进行递归解析
//routerList 后端获取的路由
const res = []
// console.log(testData);
routerList.forEach(e => {
// console.log(e.component);
let e_new = {
path: e.url,
name: e.menuName,
meta: {
title: e.menuName,
icon: e.icon
},
component: null
}
if (e.menuType === 'M') {
e_new.component = Layout
}else {
// console.log("22222",e.url);
e_new.component = modules[`/src/views${e.url}/index.vue`]
}
// console.log(e_new);
if (e.children && e.children!=null) {
const children = filterAsyncRoutes(e.children)
// 保存权限
e_new = { ...e_new, children: children }
}
res.push(e_new)
})
// console.vlog("111",res);
return res
}
export const usePermissionStore = defineStore('permission', {
id: 'permission', // id必填,且需要唯一
state: () => {
return {
routes: [],//全部路由
addRoutes: []//后端增加的路由
}
},
actions: {
generateRoutes(routes) {
// console.log(routes);
let routerList = JSON.parse(JSON.stringify(routes))
// console.log(routerList);
return new Promise((resolve) => {
const accessedRoutes = filterAsyncRoutes(routerList)
// console.log(accessedRoutes);
this.addRoutes = accessedRoutes
// console.log("111",accessedRoutes);
this.routes = constantRoutes.concat(accessedRoutes)
resolve(accessedRoutes)
})
}
}
})