首先,不同的用户登录成功所显示的页面不同是因为做看到的菜单是不一样的。而我们的菜单是由静态路由所遍历生成的。因此,不同的用户登录上去实质上是我们所添加的静态路由不同。
路由:静态路由;异步路由;任意路由
①静态路由(常量路由):这里面放的是所有用户无条件都能看到的菜单路由;
export const staticRoutes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/login/index.vue'),
meta: {
hidden: true
}
},
{
path: '/404',
name: '404',
component: () => import('@/views/error/404.vue'),
meta: {
hidden: true
}
},
{
path: '/',
component: () => import('@/layout/index.vue'),
redirect: '/home',
children: [{
path: 'home',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页', //为了在侧边栏每一项显示
icon: 'ele-HomeFilled',
//为了侧边栏每一项的图标,这个图标按道理来讲每个一级路由才会出现图标
// 但是如果这个一级路由的子路由只有一个,那么当前这个一级路由不应该出现菜单
// 二级路由直接升级成为一级路由的菜单,图标也应该在二级路由身上
}
}]
},
];
②异步路由(动态路由):这里面放的是所有的需要根据用户菜单权限信息数据过滤的动态路由
export const allAsyncRoutes: RouteRecordRaw[] = [
// 商品管理 也是一个一级路由,包含了4个二级路由
{
path: '/product',
name: 'Product', //这个名字代表命名路由,后期是要在权限控制当中去使用的,必须配而且名字必须固定,不能变
component: () => import('@/layout/index.vue'),
// redirect:'', //不需要重定向,因为它跳转不了,变为箭头,进行展开和合并二级路由菜单了
// 如果一级路由里面有多个二级路由,那么一级路由的meta需要配置,并且要带标题和图标
// 如果一级路由里面只有一个二级路由,那么一级路由后面就会被唯一的二级路由替代,直接
// 让二级路由称为一级路由的菜单,带图标和标题
meta: {
title: '商品管理',
icon: 'ele-Goods'
},
children: [
{
path: 'trademark/list',
component: () => import('@/views/product/trademark/index.vue'),
// 如果一级路由有菜单并且有图标和标题,那么二级路由只需要配置标题就可以了
// 不需要配置图标
meta: {
title: '品牌管理'
},
name: 'Trademark'
},
{
path: 'attr/list',
component: () => import('@/views/product/attr/index.vue'),
meta: {
title: '平台属性管理'
},
name: 'Attr'
},
{
path: 'sku/list',
component: () => import('@/views/product/sku/index.vue'),
meta: {
title: 'Sku管理'
},
name: 'Sku'
},
{
path: 'spu/list',
component: () => import('@/views/product/spu/index.vue'),
meta: {
title: 'Spu管理'
},
name: 'Spu'
},
{
path: 'scoped/list',
component: () => import('@/views/product/scoped/index.vue'),
meta: {
title: 'Scoped测试'
},
name: 'Scoped'
},
]
}
]
③任意路由:一定要注册在路由器的最后一位,重定向到404
export const anyRoute: RouteRecordRaw = /* 匹配任意的路由 必须最后注册 */
{
path: '/:pathMatch(.*)',
name: 'Any',
redirect: '/404',
meta: {
hidden: true
}
}
定义函数
// 这个函数就是专门让用户登录后从配置的所有异步路由数组中过滤自己的异步路由
function filterAsyncRoutes(allAsyncRoutes:RouteRecordRaw[],routeNames:string[]){
return allAsyncRoutes.filter(item => {
// 只能过滤出一级路由,二级路由我们是没有过滤过,直接全部拿走,这样不对
// 所以我们还要对二级路由再次进行过滤,要用递归
if(routeNames.indexOf(item.name as string) !== -1){
// 过滤出二级路由
if(item.children && item.children.length){
// 下面就过滤出用户想要的二级路由数组,把原本的覆盖掉
// 这行代码会出现一个bug,它会把allAsyncRoutes路由数组里面的二级路由数组给换
// 成过滤出来的新数组地址,导致整个二级路由发生变化
// 所以后期我们在过滤的时候,要深拷贝一份,再去过滤
item.children = filterAsyncRoutes(item.children,routeNames)
}
return true
}
})
}
// 这个函数是专门往路由器内部去动态添加注册路由用的
function addRoutes(routes:RouteRecordRaw[]){
// 下面这个方法是vue-router给我们的api,但是缺陷是每次只能添加一个路由对象
// 所以我们封装方法,去一次添加一个数组所有路由
routes.forEach(item => {
router.addRoute(item) //每次只能添一个
})
}
// 这个函数是专门用于重置路由的
function resetRoutes(staticRoutes:RouteRecordRaw[]){
// 1、先删除路由器当中注册的所有路由
let routes = router.getRoutes() //获取路由器当中的所有路由组成的数组
routes.forEach(item => {
if(item.name){
router.removeRoute(item.name)
}
})
// 2、再把路由器当中动态添加静态路由
addRoutes(staticRoutes)
}
获取到用户信息后进行过滤和注册路由
this.userInfo = result //存储用户信息
// 1》用户获取信息成功,之后,我们需要根据用户的routes(它是字符串)
// 从配置的所有的异步路由数组当中过滤出当前这个用户所需要的异步路由
let userAsyncRoutes = filterAsyncRoutes(cloneDeep(allAsyncRoutes),result.routes)
// 2》拿到了用户自己的异步路由之后,路由器内部目前只注册了常量路由,
// 还要把过滤出来的动态路由,要动态添加到路由器当中,同时把任意路由添加注册到最后一位
addRoutes(userAsyncRoutes.concat(anyRoute))
// 路由器当中的路由注册完成,是为了后期点击菜单可以跳转,但是菜单也是需要遍历
// 路由当中注册的路由来动态生成的,因此遍历生成菜单的路由数组也要发生变化
// 3》修改我们的menuRoutes数据,让他是最新的路由器当中注册的路由数组,然后菜单就会变成我们最新的
this.menuRoutes = staticRoutes.concat(userAsyncRoutes,anyRoute)
按钮权限
按钮权限也是根据获取到用户信息来进行判断的
// directives/hasbtn.js
// 函数是作为插件使用的,函数内部就是给app自定义一个全局的指令
import type {App} from 'vue'
import {useUserInfoStore} from '@/stores/userInfo'
import pinia from '@/stores/index'
// 作用就是对所有的按钮,进行鉴权处理
// 按钮级别权限的控制
function hasBtnPermission(app:App){
// 上面写的简写,但是它会在挂载完成及更新阶段都会执行
// app.directive('has',(el,bindings) => {
// // console.log(el,bindings);
// if(useUserInfoStore(pinia).userInfo.buttons.indexOf(bindings.value) === -1){
// el.parentNode.removeChild(el)
// }
// })
// 只会在挂载完成后去执行
app.directive('has',{
mounted(el,bindings){
if(useUserInfoStore(pinia).userInfo.buttons.indexOf(bindings.value) === -1){
el.parentNode.removeChild(el)
}
}
})
}
export default hasBtnPermission
//在main.js文件里面引入并使用自定义插件
import HasBtn from './directives/hasbtn'
app.use(HasBtn)