最近做管理系统的时候,需要实现不同用户登陆所展示的菜单不同,查了不少帖子,总结下实现的步骤:
1.在router/index.js的代码:
import { createRouter, createWebHistory,createWebHashHistory } from 'vue-router'
import NotFound from '@/pages/404/404.vue'
const routes = [{ path: "/", component: () => import('@/pages/manage/manage.vue') }, // 登录页{ path: "/login", component: () => import('@/pages/login/login.vue') }, // 登录页{path: "/manage", name: 'Manage', component: () => import('@/pages/manage/manage.vue')}, { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]
const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes:routes
})export default router
2.在main.js同目录建立permission.js
// 说明:路由守卫文件
import axios from "axios";
// 引入
import router from "./router";
// 判断用户无token 返回登录页提示有用
import { ElMessage } from 'element-plus';
let hasGetUserInfo = false;
// 一、前置守卫
router.beforeEach(async (to, from, next) => {const token = localStorage.getItem('token')if(!token && to.path !== '/login') return next('/login')if(token && to.path =='/login') {return next(from.path)}if(token && !hasGetUserInfo) {const res = await axios.get('https://mock.mengxuegu.com/mock/639d71742e0f396e51a5c945/example/menus')console.log(res)const ret = res.data.data.list;createRouters(ret);hasGetUserInfo =truereturn next(to.path);}next()
})// 动态路由获取:注:之后完善项目直接考虑在登录的时候直接获取
// 直接缓存在 pinia 里
// 这里直接取数据,不请求// const data = localStorage.getItem('routes')
// const allData = JSON.parse(data)
// function addRoutes() {
// // 1、后端数据
// createRouters(allData)
// }
// 拼接路由
function createRouters(result) {result.forEach((item) => {// 1、类型为0的菜单,子路由不为空,将子路由添加到manage里if (item.menuType === '0' && item.children.length > 0) {item.children.forEach((children) => {createRouterTemplate('Manage', children);})}// 2、menuType == 1, 子路由为空if (item.menuType === '1' && item.children.length === 0) {createRouterTemplate('Manage', item);}// 3、递归层级if (item.children.length > 0) {createRouters(item.children);}});
}
// 把router 的动态路由进行封装
function createRouterTemplate(fatherRouter, item) {router.addRoute(fatherRouter, {path: item.path,name: item.name,meta: {title: item.meta.title, // 面包屑用requiresAuth: item.meta.requiresAuth,roles: item.meta.roles,breadcrumb: item.meta.breadcrumb,keepAlive: item.meta.keepAlive},// /* @vite-ignore */ :处理vite动态导入的警告component: () => import(/* @vite-ignore */ `./views${item.component}`)})}
// 二、后置守卫
router.afterEach((to) => {// 标签抬头})// main.js 导入的为这个router
export default router
3.然后在mian.js引入:
import { createApp } from 'vue'
import { createPinia } from 'pinia'import App from './App.vue'
import router from './permission'; // 现router
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'const app = createApp(App)app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.mount('#app')
4.后端返回数据格式:
[{"id": "1","name": "Home","path": "/home","component": "/home/index.vue","menuType": "1","icon": "Discount","sort": 0,"meta": {"title": "系统首页","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []},{"id": "2","name": "System","path": "/system","component": "/system/index.vue","menuType": "0","icon": "Operation","sort": 0,"meta": {"title": "系统管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": [{"id": "211","name": "User","path": "/user","component": "/user/index.vue","menuType": "1","icon": "user","sort": 0,"meta": {"title": "用户管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []},{"id": "222","name": "Menu","path": "/menu","component": "/menu/index.vue","menuType": "1","icon": "Menu","sort": 0,"meta": {"title": "菜单管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []},{"id": "223","name": "Role","path": "/role","component": "/role/index.vue","menuType": "1","icon": "Avatar","sort": 0,"meta": {"title": "角色管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []}]},{"id": "3","name": "Log","path": "/log","component": "/log/index.vue","menuType": "1","icon": "Notebook","sort": 0,"meta": {"title": "日志管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []},{"id": "4","name": "Study","path": "/study","component": "/study/index.vue","menuType": "0","icon": "Notebook","sort": 0,"meta": {"title": "学习管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": [{"id": "441","name": "StudyUser","path": "/studyUser","component": "/study/user/index.vue","menuType": "0","icon": "Notebook","sort": 0,"meta": {"title": "用户管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": [{"id": "4441","name": "Student","path": "/student","component": "/study/user/student/index.vue","menuType": "1","icon": "Notebook","sort": 0,"meta": {"title": "学生管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []},{"id": "4442","name": "Teacher","path": "/teacher","component": "/study/user/teacher/index.vue","menuType": "1","icon": "Notebook","sort": 0,"meta": {"title": "教师管理","requiresAuth": null,"roles": [],"breadcrumb": [{}],"keepAlive": null},"children": []}]}]}
]
5.首页的代码:
<script setup>
import { RouterView ,useRouter} from 'vue-router';
import AsideMenu from '@/components/AsideMenu.vue'
const router = useRouter()
const logout = ()=>{localStorage.removeItem('token')router.replace('/login')
}
</script>
<template><div class="common-layout"><el-container style="height: 100vh;"><el-aside width="200px"><AsideMenu></AsideMenu></el-aside><el-main><div><el-button @click="logout">推出登陆</el-button></div><RouterView /></el-main></el-container></div></template>
6.组件AsideMenu.vue的代码:
<template><el-menu router :default-active="$route.path" :class="'menu-left'" :default-openeds="openedsArr" text-color="#fff"><LeftSubMenu :menuData="treeMenu"></LeftSubMenu></el-menu>
</template><script setup>
import LeftSubMenu from "./LeftSubMenu.vue";
import { computed } from "vue";
import { useRouter } from "vue-router";
import { onMounted,ref } from "vue";const treeMenu = ref([])const openedsArr = treeMenu.value.map((item) => {return item.path;
});onMounted(()=>{const data = localStorage.getItem('menuData')treeMenu.value = JSON.parse(data)
})</script><style scoped>
.menu-left {flex: 1;padding: 0 8px;border-right: none;background: none;
}.menu-left:deep(.el-menu),
.menu-left:deep(.el-sub-menu__title:hover) {background: none;
}.menu-left:deep(.el-menu-item),
.menu-left:deep(.el-sub-menu__title) {height: 36px;margin-bottom: 4px;border-radius: 4px;color: var(--text-main-color) !important;
}.menu-left:deep(.el-menu-item:hover .icon),
.menu-left:deep(.el-menu-item.is-active .icon) {filter: invert(100%);-webkit-filter: invert(100%);
}.menu-left:deep(.el-menu-item:hover),
.menu-left:deep(.el-menu-item.is-active) {color: #ffffff !important;background-color: #eecece;
}
</style>
7.组件LeftSubMenu.vue的代码
<template><template v-for="item in props.menuData"><el-sub-menu :key="item.path" v-if="item.children && item.children.length > 0" :index="item.path"><template #title><el-icon><component :is="item.icon"></component></el-icon><span>{{ item.meta.title }}</span></template><LeftSubMenu :menuData="item.children"></LeftSubMenu></el-sub-menu><el-menu-item :key="item.id" v-else :index="item.path" :disabled="item.disabled"><template #title><!-- <img class="icon pd-r-10" :src="item.icon" /> --><el-icon><component :is="item.icon"></component></el-icon><span>{{ item.meta.title }}</span></template></el-menu-item></template>
</template><script setup>
import LeftSubMenu from "./LeftSubMenu.vue";
import { computed, onMounted } from "vue";
import { useRouter } from "vue-router";const props = defineProps({menuData: {type: Array,default: [],},
});onMounted(() => {console.log(props.menuData, "Item打印数据");
});const curRoute = computed(() => {const router = useRouter();const { path } = router.currentRoute.value;return path;
});
</script>
这样就实现该功能,登陆页面就是登陆保存token的操作。
感谢VueRouter4 - 动态路由刷新变空白或404_vue刷新页面后路由匹配到空白-CSDN博客,Vue3+Vue-Router+Element-Plus根据后端数据实现前端动态路由——权限管理模块_vue3动态路由权限-CSDN博客