addRoute API控制页面权限

453 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看活动详情

前言

在前文中使用自定义指令完成了对功能按钮权限的控制,今天记录一下如何利用addRoute来控制页面权限

addRoute

addRoute: 添加一条新的路由记录到路由。如果路由有一个 name,并且已经有一个与之名字相同的路由,它会先删除之前的路由。

addRoute(route: RouteRecordRaw): () => void

addRoute详情

页面权限

登录的用户不同,用户的权限不同,有些页面则无需显示,利用addRoute API,可以将用户拥有的权限页面增加进路由,无权限的不需要添加。登录用户不同,也能动态的显示用户已有的权限页面

router/index.js,创建两个变量privateRoutespublicRoutes,私有路由表保存需要权限的路由,公共路由表保存不需要权限的路由

// 私有路由表
export const privateRoutes = []
// 公有路由表
export var publicRoutes = []

const router = createRouter({
  history: createWebHashHistory(),
  routes: publicRoutes
})

接着需要一个模块单独处理路由,在vuex中新建一个控制路由权限的模块,其中routes是初始路由表,初始只有不需要权限的页面的路由,setRoutes设置新的路由表,filterRoutes的作用是根据权限筛选路由

store/modules/permission.js

// 处理权限路由的模块
import { publicRoutes, privateRoutes } from '@/router'
export default {
  namespaced: true,
  state: {
    // 路由表:初始拥有静态路由权限
    routes: publicRoutes
  },
  mutations: {
    setRoutes(state, newRoutes) {
      // 永远在静态路由的基础上增加新路由
      state.routes = [...publicRoutes, ...newRoutes]
    }
  },
  actions: {
    /**
     * 根据权限筛选路由
     */
    filterRoutes(context, menus) {
    }
  }
}

接下来补充privateRoutes,我们将所有权限路由加入到私有路由表中

举个栗子,以下是一个权限路由,这里注意路由要加上name属性,我们依靠name属性值去判断用户是否含有该页面权限,在用户登录后,获取的个人信息中保存有menus数组,其中包含用户拥有的页面权限,menus数组中的内容和路由中的name值是相对的

1.png

import layout from '@/layout'

export default {
  path: '/user',
  component: layout,
  redirect: '/user/manage',
  name: 'userManage',
  meta: {
    title: 'user',
    icon: 'personnel'
  },
  children: [
    {
      path: '/user/manage',
      component: () => import('@/views/user-manage/index'),
      meta: {
        title: 'userManage',
        icon: 'personnel-manage'
      }
    },
    {
      path: '/user/info/:id',
      name: 'userInfo',
      component: () => import('@/views/user-info/index'),
      props: true,
      meta: {
        title: 'userInfo'
      }
    },
    {
      path: '/user/import',
      name: 'import',
      component: () => import('@/views/import/index'),
      meta: {
        title: 'excelImport'
      }
    }
  ]
}

将其加入私有路由表

···
import UserManageRouter from './modules/UserManage'
···

export const asyncRoutes = [
  ···   
  UserManageRouter,
  ···
]

将权限路由全都加入私有路由表完成之后,接下来去完善vuex permission.js模块中的filterRoutes方法,该方法需要传入用户的menus数组,即用户所拥有的权限页面的name值,根据name值,从私有路由表中筛选出含有每个name的路由,返回一个数组,这个数组保存的就是用户拥有的权限路由

import { publicRoutes, privateRoutes } from '@/router'

filterRoutes(context, menus) {
      // 筛选后需要通过addRoute进行添加的路由表数组
      const routes = []
      menus.forEach((key) => {
        routes.push(...privateRoutes.filter((item) => item.name === key))
      })

      // 所有不匹配的路由都进入404
      // 必须写在最后
      routes.push({
        path: '/:catchAll(.*)',
        redirect: '/404'
      })

      context.commit('setRoutes', routes)

      return routes
    }

接着在路由导航守卫中调用该函数获取用户拥有的权限路由,通过addRoute方法加入router

router.beforeEach(async (to, from, next) => {
  // 用户已登录 不进login
  // 用户未登录 只进login
  if (store.getters.token) {
    if (to.path === '/login') {
      next('/')
    } else {
      if (!store.getters.hashUserInfo) {
        // 这里是获取用户的权限数组
        const { permission } = await store.dispatch('user/getUserInfo')
        // 获取用户权限路由
        const filterRoutes = await store.dispatch(
          'permission/filterRoutes',
          permission.menus
        )
        // 利用 addRoute 循环添加
        filterRoutes.forEach((item) => {
          router.addRoute(item)
        })
        // store.dispatch('user/getUserInfo')
        // 添加完动态路由之后,需要在进行一次主动跳转
        return next(to.path)
      }
      next()
    }
  }else {
      ···
  }

至此页面权限控制已经完成,但是当我们退出登陆时,添加的路由表没有删除,当另一个用户登录时,菜单是没有变的,只有手动刷新,才能展示最新的路由表,所以当用户退出时,需要将动态添加的路由删除

这里需要用的removeRoute API: 通过名称删除现有路由。

router/index.js导出方法,它的作用是将动态添加的路由删除

export function resetRouter() {
  if (
    store.getters.userInfo &&
    store.getters.userInfo.permission &&
    store.getters.userInfo.permission.menus
  ) {
    // 获取用户权限数组
    const menus = store.getters.userInfo.permission.menus
    menus.forEach((menu) => {
      router.removeRoute(menu)
    })
  }

在退出时调用该方法即可

import { resetRouter } from '@/router'

logout() {
      resetRouter()
      ...
}