目录
1. 拆分路由
静态(常量)路由:大家都可以拥有的路由 login、首页、数据大屏、404
异步路由:不同的身份有的有这个路由、有的没有 权限管理(三个子路由) 商品管理模块(四个子路由)
任意路由:任意路由
//对外暴露配置路由(常量路由):全部用户都可以访问到的路由
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',
}
},
]
}
]
//任意路由
export const anyRoute = {
//任意路由
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any',
meta: {
title: '任意路由',
hidden: true,
icon: 'DataLine'
}
}
//通过vue-router插件实现模板路由配置
import { createRouter, createWebHashHistory } from 'vue-router';
import { constantRoute } from './routes';
//创建路由器
let router = createRouter({
//路由模式hash
history: createWebHashHistory(),
routes: constantRoute,
});
export default router;
2. 生成路由
后台获取的用户信息中有仓库存储生成菜单需要数组(路由)
然后根据获取到的权限信息,过滤出该用户有权限访问的路由,通过Vue Router提供的addRoutes方法进行动态添加
//创建用户相关的小仓库
import { defineStore } from 'pinia';
//引入接口
import { reqLogin, reqUserInfo, reqLogout } from '@/api/user';
//引入路由(常量路由)
import { constantRoute, asnycRoute, anyRoute } from '@/router/routes';
//引入深拷贝方法
//@ts-ignore
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) {
item.children = filterAsyncRoute(item.children, routes);
}
return true;
}
})
}
//创建用户小仓库
let useUserStore = defineStore('User', {
//小仓库存储数据地方
state: (): UserState => {
return {
token: GET_TOKEN(),//用户唯一标识token
menuRoutes: constantRoute,//仓库存储生成菜单需要数组(路由)
//存储当前用户是否包含某一个按钮
buttons:[],
}
},
//异步|逻辑的地方
actions: {
//获取用户信息方法
async userInfo() {
//获取用户信息进行存储仓库当中[用户头像、名字]
let result: userInfoReponseData = await reqUserInfo();
//如果获取用户信息成功,存储一下用户信息
if (result.code == 200) {
//计算当前用户需要展示的异步路由
let userAsyncRoute = filterAsyncRoute(cloneDeep(asnycRoute), result.data.routes);
//菜单需要的数据整理完毕
this.menuRoutes = [...constantRoute, ...userAsyncRoute, anyRoute];
//目前路由器管理的只有常量路由:用户计算完毕异步路由、任意路由动态追加
[...userAsyncRoute, anyRoute].forEach((route: any) => {
router.addRoute(route);
});
return 'ok';
} else {
return Promise.reject(new Error(result.message));
}
}
},
getters: {
}
})
//对外暴露获取小仓库方法
export default useUserStore;
3. 生成菜单
根据生成的路由信息,通过递归生成嵌套菜单。可以通过Vue组件的递归调用实现。
<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>
<script lang="ts">
export default {
name: 'Menu'
}
</script>
<style scoped></style>