用户角色不同 看到菜单不同 目前用户密码默认都是6个1


4-1 修改src/permission.ts
路由导航里权限验证
先判断是否 有token 如果有 判断目前有没有角色,没有就根据token去获取用户信息包含角色信息 然后根据用户角色获取权限菜单 然后在根据权限菜单 筛选asyncRoutes 注册相应的权限路由 并渲染返回的菜单
src/permission.ts
import router from '@/router'import nProgress from 'nprogress'import 'nprogress/nprogress.css' // progress bar styleimport { getToken } from './utils/auth'import store from '@/store'import { ElMessage } from 'element-plus'nProgress.configure({ showSpinner: false })const whiteList = ['/login'] // 白名单router.beforeEach(async (to) => {nProgress.start()const hasToken = getToken()if (hasToken) { // 有token代表已登录if (to.path === '/login') {nProgress.done()return {path: '/',replace: true}} else {try {const hasRoles = store.getters.roles && store.getters.roles.length > 0if (hasRoles) {nProgress.done()return true}// 无用户信息和角色信息 就请求获取const roles = await store.dispatch('user/getUserInfo')// 该用户未分配角色 进行异常提示if (!roles || roles.length === 0) {throw new Error('该用户未分配角色')}// 获取权限路由const accessRoutes = await store.dispatch('permission/generateRoutes')// 动态注册路由accessRoutes.forEach(router.addRoute)// 触发重定向return to.fullPath} catch (error) { // 登录失败处理// 移除token重新登录await store.dispatch('user/resetToken')ElMessage.error('登录失败:' + (error.message || 'Has Error'))nProgress.done()return `/login?redirect=${to.path}`}}} else {if (whiteList.includes(to.path)) {nProgress.done()return true}nProgress.done()return {path: '/login',query: {redirect: to.path,...to.query}}}})router.afterEach(() => {nProgress.done()})
4-2 permissions store
src/store/modules/permission.ts
import { Module, MutationTree, ActionTree } from 'vuex'import { RouteRecordRaw } from 'vue-router'import store, { IRootState } from '../index'import { asyncRoutes } from '../../router/index'import { MenuData } from './menu'import path from 'path'// 生成路由路径数组const generateRoutePaths = (menus: Array<MenuData>): string[] => {return menus.map(menu => menu.path)}// 白名单const whiteList = ['/:pathMatch(.*)*']// 生成可访问路由表const generateRoutes = (routes: Array<RouteRecordRaw>, routePaths: string[], basePath = '/') => {const routerData: Array<RouteRecordRaw> = []routes.forEach(route => {const routePath = path.resolve(basePath, route.path)if (route.children) { // 先看子路由 是否有匹配上的路由route.children = generateRoutes(route.children, routePaths, routePath)}// 如果当前路由子路由 数量大于0有匹配上 或 paths中包含当面路由path 就需要把当前父路由添加上if (routePaths.includes(routePath) || (route.children && route.children.length >= 1) || whiteList.includes(routePath)) {routerData.push(route)}})return routerData}const filterAsyncRoutes = (menus: Array<MenuData>, routes: Array<RouteRecordRaw>) => {// 生成要匹配的路由path数组const routePaths = generateRoutePaths(menus)// 生成匹配path的路由表const routerList = generateRoutes(routes, routePaths)return routerList}// 定义state类型export interface IPermissionState {routes: Array<RouteRecordRaw>;accessRoutes: Array<RouteRecordRaw>;}// mutations类型type IMutations = MutationTree<IPermissionState>// actions类型type IActions = ActionTree<IPermissionState, IRootState>// 定义stateconst state: IPermissionState = {routes: [],accessRoutes: []}// 定义mutationconst mutations: IMutations = {SET_ROUTES(state, data: Array<RouteRecordRaw>) {state.routes = data},SET_ACCESS_ROUTES(state, data: Array<RouteRecordRaw>) {state.accessRoutes = data}}// 定义actionsconst actions: IActions = {generateRoutes({ dispatch }, type?: number) { // 1 针对菜单排序更新return new Promise((resolve, reject) => {let accessedRoutes: Array<RouteRecordRaw> = []if (store.getters.roleNames.includes('super_admin')) { // 超级管理员角色accessedRoutes = asyncRoutesdispatch('menu/getAllMenuListByAdmin', null, { root: true })resolve(accessedRoutes)} else { // 根据角色过滤菜单const roles = store.getters.roleIdsdispatch('menu/getAccessByRoles', roles, { root: true }).then(menus => {if (type !== 1) { // 菜单重新排序 不需要再过次滤路由accessedRoutes = filterAsyncRoutes(menus, asyncRoutes)}resolve(accessedRoutes)}).catch(reject)}})}}// 定义user moduleconst permission: Module<IPermissionState, IRootState> = {namespaced: true,state,mutations,actions}export default permission
4-5 修改路由表
路由表之前 数组 合并导出的路由 现在不需要了

/src/router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'import Layout from '@/layout/index.vue'// 看作是异步获取路由export const asyncRoutes: Array<RouteRecordRaw> = [{path: '/documentation',component: Layout, // 布局组件作为一级路由redirect: '/documentation/index',name: 'DocumentationLayout',children: [{path: 'index',name: 'Documentation',component: () => import(/* webpackChunkName: "documentation" */ '@/views/documentation/index.vue'),meta: {title: 'Documentation',icon: 'documentation',hidden: false, // 菜单栏不显示// 路由是否缓存 没有这个属性或false都会缓存 true不缓存noCache: true}}]},{path: '/async',component: Layout,redirect: '/async/index',name: 'AsyncLayout',children: [{path: 'index',name: 'Async',component: () => import(/* webpackChunkName: "async" */ '@/views/async.vue'),meta: {title: '动态路由',icon: 'guide'// 当guide路由激活时高亮选中的是 documentation/index菜单// activeMenu: '/documentation/index'}}]},{path: '/guide',component: Layout,redirect: '/guide/index',name: 'GuideLayout',meta: {title: 'GuideLay',icon: 'guide'},children: [{path: 'index',name: 'Guide',component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),meta: {title: 'Guide',icon: 'guide'// 当guide路由激活时高亮选中的是 documentation/index菜单// activeMenu: '/documentation/index'}},{path: 'guide2',name: 'Guide2',component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),meta: {title: 'Guide2',icon: 'guide'// 当guide路由激活时高亮选中的是 documentation/index菜单// activeMenu: '/documentation/index'}},{path: 'guide3',name: 'Guide3',component: () => import(/* webpackChunkName: "guide" */ '@/views/guide/index.vue'),meta: {title: 'Guide3',icon: 'guide'// 当guide路由激活时高亮选中的是 documentation/index菜单// activeMenu: '/documentation/index'}}]},{path: '/system',component: Layout,redirect: '/system/user',name: 'SystemLayout',meta: {title: 'System',icon: 'lock',alwaysShow: true // 根路由始终显示 哪怕只有一个子路由},children: [{path: 'menu',name: 'Menu Management',component: () => import(/* webpackChunkName: "menu" */ '@/views/system/menu/index.vue'),meta: {title: 'Menu Management',hidden: false,breadcrumb: false}},{path: 'role',name: 'Role Management',component: () => import(/* webpackChunkName: "role" */ '@/views/system/role/index.vue'),meta: {title: 'Role Management',hidden: false}},{path: 'user',name: 'User Management',component: () => import(/* webpackChunkName: "user" */ '@/views/system/user/index.vue'),meta: {title: 'User Management'}}]},{ // 外链路由path: '/external-link',component: Layout,children: [{path: 'https://www.baidu.com/',redirect: '/',meta: {title: 'External Link',icon: 'link'}}]},{ // 404一定放在要在最后面path: '/:pathMatch(.*)*',redirect: '/404',meta: {hidden: true}}]export const constantRoutes: Array<RouteRecordRaw> = [{path: '/',component: Layout,redirect: '/dashboard',name: 'DashboardLayout',children: [{path: 'dashboard',name: 'Dashboard',component: () => import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/index.vue'),meta: {title: 'Dashboard',// icon: 'dashboard'icon: 'el-icon-platform-eleme',affix: true // 固定显示在tagsView中}}]},{path: '/redirect',component: Layout,meta: {hidden: true},name: 'Redirect',children: [{ // 带参数的动态路由正则匹配// https://next.router.vuejs.org/zh/guide/essentials/route-matching-syntax.html#%E5%8F%AF%E9%87%8D%E5%A4%8D%E7%9A%84%E5%8F%82%E6%95%B0path: '/redirect/:path(.*)', // 要匹配多级路由 应该加*号component: () => import('@/views/redirect/index.vue')}]},{path: '/login',name: 'Login',component: () => import('@/views/login/index.vue')},{path: '/profile',component: Layout,redirect: '/profile/index',name: 'ProfileLayout',children: [{path: 'index',name: 'Profile',component: () => import('@/views/profile/index.vue'),meta: {hidden: true,title: '个人中心'}}]},{path: '/401',component: Layout,name: '401Layout',children: [{path: '',component: () => import('@/views/error-page/401.vue'),meta: {title: '401',icon: '404',hidden: true}}]},{path: '/404',component: () => import('@/views/error-page/404.vue'),meta: {hidden: true // 404 hidden掉}}]// export const routes = [// ...constantRoutes,// ...asyncRoutes// ]export const routes = constantRoutesconst router = createRouter({history: createWebHashHistory(),routes})export default router
4-4 修改侧边栏
从权限store里获取菜单


src/layout/components/Sidebar/index.vue
<template><div class="sidebar-wrapper"><logo v-if="showLogo" :collapse="isCollapse" /><scroll-panel><el-menuclass="sidebar-container-menu":class="{'sidebar-show-logo': showLogo}"mode="vertical":default-active="activeMenu":background-color="scssVariables.menuBg":text-color="scssVariables.menuText":active-text-color="themeColor":collapse="isCollapse":collapse-transition="true"><sidebar-itemv-for="route in menuRoutes":key="route.path":item="route":base-path="route.path"/></el-menu></scroll-panel></div></template><script lang="ts">import { defineComponent, computed } from 'vue'import { useRoute } from 'vue-router'import variables from '@/styles/variables.scss'import SidebarItem from './SidebarItem.vue'import { useStore } from '@/store'import Logo from './Logo.vue'import ScrollPanel from '@/components/ScrollPanel.vue'export default defineComponent({name: 'Sidebar',components: {Logo,SidebarItem,ScrollPanel},setup() {const route = useRoute()const store = useStore()// 根据路由路径 对应 当前激活的菜单const activeMenu = computed(() => {const { path, meta } = route// 可根据meta.activeMenu指定 当前路由激活时 让哪个菜单高亮选中if (meta.activeMenu) {return meta.activeMenu}return path})// scss变量const scssVariables = computed(() => variables)// 展开收起状态 稍后放store 当前是展开就让它收起const isCollapse = computed(() => !store.getters.sidebar.opened)// 获取权限菜单const menuList = computed(() => store.state.menu.authMenuTreeData)// 渲染路由const menuRoutes = computed(() => [...menuList.value])// 获取主题色const themeColor = computed(() => store.getters.themeColor)// 是否显示logoconst showLogo = computed(() => store.state.settings.sidebarLogo)return {// ...toRefs(variables), // 不用toRefs原因 缺点variables里面变量属性来源不明确scssVariables,isCollapse,activeMenu,menuRoutes,themeColor,showLogo}}})</script><style lang="scss" scoped>.sidebar-wrapper {.sidebar-container-menu {height: 100vh;&.sidebar-show-logo { // 显示logo时// 100vh-50pxheight: calc(100vh - 50px);}}}</style>



