1.思路
每个用户都有自己的角色,不同的角色拥有不同的异步路由(每个角色都拥有常量路由),这时需要根据用户登录成功之后,后端返回的用户信息中的路由信息,在所有的异步路由中过滤出用户所拥有的路由,再动态添加到路由器当中,最后配合路由导航菜单的递归创建,把用户对应角色的路由展示给用户。
2.router中相关的代码
//通过vue-router插件实现模板路由配置
import { createRouter, createWebHashHistory } from 'vue-router'
import { constantRoute } from './routes'
//创建路由器
const router = createRouter({
//路由模式hash
history: createWebHashHistory(),
routes: constantRoute,
//滚动行为
scrollBehavior() {
return {
left: 0,
top: 0,
}
},
})
export default router
//对外暴露配置路由(常量路由):全部用户都可以访问到的路由
export const constantRoute = [
{
//登录
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'login',
meta: {
title: '登录', //菜单标题
hidden: true, //代表路由标题在菜单中是否隐藏 true:隐藏 false:不隐藏
icon: 'Promotion', //菜单文字左侧的图标,支持element-plus全部图标
},
},
{
//登录成功以后展示数据的路由
path: '/',
component: () => import('@/layout/index.vue'),
name: 'layout',
meta: {
title: '',
hidden: false,
icon: '',
},
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled',
},
},
],
},
{
//404
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404',
meta: {
title: '404',
hidden: true,
icon: 'DocumentDelete',
},
},
{
path: '/screen',
component: () => import('@/views/screen/index.vue'),
name: 'Screen',
meta: {
hidden: false,
title: '数据大屏',
icon: 'Platform',
},
},
]
//异步路由
export const asnycRoute = [
{
path: '/acl',
component: () => import('@/layout/index.vue'),
name: 'Acl',
meta: {
title: '权限管理',
icon: 'Lock',
},
redirect: '/acl/user',
children: [
{
path: '/acl/user',
component: () => import('@/views/acl/user/index.vue'),
name: 'User',
meta: {
title: '用户管理',
icon: 'User',
},
},
{
path: '/acl/role',
component: () => import('@/views/acl/role/index.vue'),
name: 'Role',
meta: {
title: '角色管理',
icon: 'UserFilled',
},
},
{
path: '/acl/permission',
component: () => import('@/views/acl/permission/index.vue'),
name: 'Permission',
meta: {
title: '菜单管理',
icon: 'Monitor',
},
},
],
},
{
path: '/product',
component: () => import('@/layout/index.vue'),
name: 'Product',
meta: {
title: '商品管理',
icon: 'Goods',
},
redirect: '/product/trademark',
children: [
{
path: '/product/trademark',
component: () => import('@/views/product/trademark/index.vue'),
name: 'Trademark',
meta: {
title: '品牌管理',
icon: 'ShoppingCartFull',
},
},
{
path: '/product/attr',
component: () => import('@/views/product/attr/index.vue'),
name: 'Attr',
meta: {
title: '属性管理',
icon: 'ChromeFilled',
},
},
{
path: '/product/spu',
component: () => import('@/views/product/spu/index.vue'),
name: 'Spu',
meta: {
title: 'SPU管理',
icon: 'Calendar',
},
},
{
path: '/product/sku',
component: () => import('@/views/product/sku/index.vue'),
name: 'Sku',
meta: {
title: 'SKU管理',
icon: 'Orange',
},
},
],
},
]
3.store中相关的代码
//引入所有路由
import { constantRoute, asnycRoute} from '@/router/routes'
//引入深拷贝方法
//@ts-expect-error
import cloneDeep from 'lodash/cloneDeep'
import router from '@/router'
//用于过滤当前用户需要展示的异步路由
function filterAsyncRoute(asnycRoute: any, routes: any) {
return asnycRoute.filter((item: any) => {
if (routes.includes(item.name)) {
if (item.children && item.children.length > 0) {
//硅谷333账号:product\trademark\attr\sku
item.children = filterAsyncRoute(item.children, routes)
}
return true
}
})
}
//计算当前用户需要展示的异步路由
const userAsyncRoute = filterAsyncRoute(
cloneDeep(asnycRoute),
result.data.routes,
)
//菜单需要的数据整理完毕
this.menuRoutes = [...constantRoute, ...userAsyncRoute];
//目前路由器管理的只有常量路由:用户计算完毕异步路由动态追加
[...userAsyncRoute].forEach((route: any) => {
router.addRoute(route)
})
4.布局容器组件中的菜单组件
<template>
<template v-for="(item, index) in menuList" :key="item.path">
<!--没有子路由-->
<template v-if="!item.children">
<el-menu-item :index="item.path" v-if="!item.meta.hidden" @click="goRoute">
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有子路由但是只有一个子路由 -->
<template v-if="item.children && item.children.length == 1">
<el-menu-item :index="item.children[0].path" v-if="!item.children[0].meta.hidden" @click="goRoute">
<el-icon>
<component :is="item.children[0].meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.children[0].meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有子路由且个数大于一个1 -->
<el-sub-menu :index="item.path" v-if="item.children && item.children.length > 1">
<template #title>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
<Menu :menuList="item.children"></Menu>
</el-sub-menu>
</template>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
//获取父组件传递过来的全部路由数组
defineProps(['menuList']);
//获取路由器对象
let $router = useRouter();
//点击菜单的回调
const goRoute = (vc: any) => {
//路由跳转
$router.push(vc.index);
}
</script>
<style scoped></style>