• 权限应用 之 动态生成左侧菜单


    目录

    权限应用-动态生成左侧菜单-整体分析

    分析

    示例

     权限应用-动态生成左侧菜单-addRoutes方法

    addRoutes基本使用

    示例

    在代码中书写

    权限应用-动态生成左侧菜单-改写路由信息保存位置

    分析

    定义vuex管理菜单数据

    1.补充模块,在src/store/modules下补充menu.js

    2.提交setMenuList生成完整的菜单数据

    3.菜单生成部分改写使用vuex中的数据

    权限应用-使用权限数据做过滤处理

    过滤的思路

    从actions中返回菜单项

     在permission.js中获取action的返回值并过滤

    小结

    刷新页面时的bug修复

    问题 

    原因

    解决

    实现代码

     解决刷新出现的白屏bug

    退出登录时重置路由

    问题

    原因

    解决


    上文说到,角色分配权限
    ,以及提到权限两个应用

    动态生成左侧菜单 以及按钮级控制

    权限应用-动态生成左侧菜单-整体分析

    分析

    登录成功(页面跳转),进入导航守卫:

    • 获取个人权限信息
    • 生成可以访问的动态路由

    示例

     权限应用-动态生成左侧菜单-addRoutes方法

    vue-router对象中的addRoutes,用它来动态添加路由配置

    因为用户访问到的页面(路由配置)必须是动态的,所有要先掌握下可以动态添加路由地址的api

    addRoutes基本使用

    router.addRoutes([路由配置对象])
    或者:
    this.$router.addRoutes([路由配置对象])

    示例

    1.在views下创建页面

    2.通过代码添加路由配置

    // 按钮

    // 回调
    hAddRoute() {
        this.$router.addRoutes([{
            path: '/abc',
            component: () => import('@/views/abc'),
            }])
    },

    点击了按钮之后,就可以在地址中访问/abc了

    在代码中书写

    1.在router/index.js中的路由配置中删除动态路由的部分

    1. const createRouter = () => new Router({
    2. // mode: 'history', // require service support
    3. scrollBehavior: () => ({ y: 0 }),
    4. // routes: constantRoutes
    5. // 合并动态和静态的路由 , ...asyncRoutes
    6. - routes: [...constantRoutes, ...asyncRoutes]
    7. + routes: [...constantRoutes]
    8. })

    2.在permission.js中引入,并使用addRoutes动态添加

    把之前在router中直接静态写死的动态路由表改造成通过addRoutes 方法调用添加的形式

    1. // 引入所有的动态路由表(未经过筛选)
    2. + import router, { asyncRoutes } from '@/router'
    3. const whiteList = ['/login', '/404']
    4. router.beforeEach(async(to, from, next) => {
    5. // 开启进度条
    6. NProgress.start()
    7. // 获取本地token 全局getter
    8. const token = store.getters.token
    9. if (token) {
    10. // 有token
    11. if (to.path === '/login') {
    12. next('/')
    13. } else {
    14. if (!store.getters.userId) {
    15. await store.dispatch('user/getUserInfo')
    16. // 改写成动态添加的方式
    17. + router.addRoutes(asyncRoutes)
    18. }
    19. next()
    20. }
    21. } else {
    22. // 没有token
    23. if (whiteList.includes(to.path)) {
    24. next()
    25. } else {
    26. next('/login')
    27. }
    28. }
    29. // 结束进度条
    30. NProgress.done()
    31. })

    效果

    1.左侧的菜单只剩下静态的首页了

    2.浏览器手动输入某一个动态路由地址,依旧是可用的,这证明了已经把动态路由添加到我们的路由系统了

    权限应用-动态生成左侧菜单-改写路由信息保存位置

    分析

    当前的菜单渲染(src\layout\components\Sidebar\index.vue)使用的数据:this.$router.options.routes 这个数据是固定,我们通过addRoutes添加的路由表只存在内存中,并不会改变this.$router.options.routes

    如果我们希望在调用addRoutes方法之后,要路由数据立刻反映到菜单中,我们需要想一个额外的方法,vue开发中,哪个技术可以保证响应式特性还可以动态修改? vuex

    因此在vuex中保存菜单数据

    定义vuex管理菜单数据

    1.补充模块,在src/store/modules下补充menu.js

    • 定义数据menuList
    • 修改数据的方法setMenuList        
    1. // 导入静态路由
    2. import { constantRoutes } from '@/router'
    3. export default {
    4. namespaced: true,
    5. state: {
    6. // 先以静态路由作为菜单数据的初始值
    7. menuList: [...constantRoutes]
    8. },
    9. mutations: {
    10. setMenuList(state, asyncRoutes) {
    11. // 将动态路由和静态路由组合起来
    12. state.menuList = [...constantRoutes, ...asyncRoutes]
    13. }
    14. }
    15. }

    当然,要在src/store/index.js中注册这个模块

    1. + import menu from './modules/menu'
    2. Vue.use(Vuex)
    3. const store = new Vuex.Store({
    4. modules: {
    5. app,
    6. settings,
    7. user,
    8. + menu
    9. },
    10. getters
    11. })

    2.提交setMenuList生成完整的菜单数据

    修改src/permission.js中的代码

    1. if (!store.getters.userId) {
    2. await store.dispatch('user/getUserInfo')
    3. // 动态添加可以访问的路由设置
    4. router.addRoutes(asyncRoutes)
    5. // 根据用户实际能访问几个页面来决定从整体8个路由设置
    6. // 中,过滤中出来几个,然后保存到vuex中
    7. + store.commit('menu/setMenuList', asyncRoutes)
    8. }

    3.菜单生成部分改写使用vuex中的数据

    在src\layout\components\Sidebar\index.vue文件中,修改

    routes() {
      // 拿到的是一个完整的包含了静态路由和动态路由的数据结构
      // return this.$router.options.routes
      return this.$store.state.menu.menuList
    }

    权限应用-使用权限数据做过滤处理

    上一步我们实现了

    • 把动态路由通过addRoutes动态添加到了路由系统里
    • 把动态路由保存到vuex的menu中

    但是我们没有和权限数据做搭配,接下来我们通过接口返回的权限数据对动态菜单(8个页面)做过滤处理,以确定完成菜单与用户权限相关。

    过滤的思路

    过滤使用name作为标识,对照下标检查路由name是否一致

    后端的接口约定如下:

    • 页面名字: 员工   标识: employees
    • 页面名字: 权限  标识: permissions
    • 页面名字: 组织架构  标识: departments
    • 页面名字: 设置 标识: settings
    • 页面名字: 工资  标识: salarys
    • 页面名字: 审核 标识: approvals
    • 页面名字: 考勤  标识: attendances
    • 页面名字: 社保 标识: social_securitys

    从actions中返回菜单项

    用户能访问哪些页面是通过actions获取到的,只需要从action中返回即可。

    修改 store/modules/user.js ,补充return语句。

    1. // 用来获取用户信息的action
    2. async getUserInfo(context) {
    3. // 1. ajax获取基本信息,包含用户id
    4. const rs = await getUserInfoApi()
    5. console.log('用来获取用户信息的,', rs)
    6. // 2. 根据用户id(rs.data.userId)再发请求,获取详情(包含头像)
    7. const info = await getUserDetailById(rs.data.userId)
    8. console.log('获取详情', info.data)
    9. // 把上边获取的两份合并在一起,保存到vuex中
    10. context.commit('setUserInfo', { ...info.data, ...rs.data })
    11. // 当前用户可以看到的菜单 res.data.roles.menus
    12. + return rs.data.roles.menus
    13. },

     在permission.js中获取action的返回值并过滤

    src/permission.js

    1. if (!store.getters.userId) {
    2. // 有token,要去的不是login,就直接放行
    3. // 进一步获取用户信息
    4. // 发ajax---派发action来做
    5. + const menus = await store.dispatch('user/getUserInfo')
    6. console.log('当前用户能访问的页面', menus) // ['salarys', 'settings']
    7. console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
    8. // 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
    9. + const filterRoutes = asyncRoutes.filter(route => {
    10. + const routeName = route.children[0].name
    11. + return menus.includes(routeName)
    12. + })
    13. // 一定要在进入主页之前去获取用户信息
    14. // addRoutes用来动态添加路由配置
    15. // 只有在这里设置了补充了路由配置,才可能去访问页面
    16. // 它们不会出现左侧
    17. + router.addRoutes(filterRoutes)
    18. // 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
    19. // 生成左侧菜单时,也应该去vuex中拿
    20. + store.commit('menu/setMenuList', filterRoutes)
    21. }

    小结

    • 语法: 从actions中获取返回值
    • asyncRoutes.filter

    刷新页面时的bug修复

    问题 

    (1)如果我们刷新浏览器,会发现跳到了404页面

    (2)对于addRoute添加的路由,在刷新时会白屏

    原因

    刷新浏览器,会发现跳到了404页面

    现在我们的路由设置中的404页处在中间位置而不是所有路由的末尾了。

    解决

    把404页改到路由配置的最末尾就可以了

    实现代码

    1.从route/index.js中的静态路由中删除path:'*'这一项

    1. // 不需要特殊的权限控制就可以访问的页面
    2. export const constantRoutes = [
    3. {
    4. path: '/login',
    5. component: () => import('@/views/login/index'),
    6. hidden: true
    7. },
    8. // 404 page must be placed at the end !!!
    9. - { path: '*', redirect: '/404', hidden: true }
    10. ]

    2.在permission.js中补充在最后

    1. // if(没有userInfo) {
    2. if (!store.getters.userId) {
    3. // 有token,要去的不是login,就直接放行
    4. // 进一步获取用户信息
    5. // 发ajax---派发action来做
    6. const menus = await store.dispatch('user/getUserInfo')
    7. console.log('当前用户能访问的页面', menus)
    8. console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
    9. // 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
    10. const filterRoutes = asyncRoutes.filter(route => {
    11. const routeName = route.children[0].name
    12. return menus.includes(routeName)
    13. })
    14. // 一定要在进入主页之前去获取用户信息
    15. //404加到最后一条
    16. + filterRoutes.push( // 404 page must be placed at the end !!!
    17. { path: '*', redirect: '/404', hidden: true })
    18. // addRoutes用来动态添加路由配置
    19. // 只有在这里设置了补充了路由配置,才可能去访问页面
    20. // 它们不会出现左侧
    21. router.addRoutes(filterRoutes)
    22. // 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
    23. // 生成左侧菜单时,也应该去vuex中拿
    24. store.commit('menu/setMenuList', filterRoutes)
    25. }

     解决刷新出现的白屏bug

    if (!store.getters.userId) {
      // 省略其他...
      // 解决刷新出现的白屏bug
      next({
        ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
        replace: true // 重进一次, 不保留重复历史
      })
    } else {
      next()
    }

    退出登录时重置路由

    问题

    退出后,再次登陆,发现菜单异常 (控制台有输出说路由重复);

    原因

    路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。

    需要将路由权限重置 (恢复默认)   将来登录后再次追加才可以,不然的话,就会重复添加

    解决

    我们的router/index.js文件,发现一个重置路由方法

    // 重置路由
    export function resetRouter() {
      const newRouter = createRouter()
      router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
    }
     

    这个方法就是将路由重新实例化,相当于换了一个新的路由,之前加的路由就不存在了,需要在登出的时候, 调用一下即可

    store/modules/user.js

    1. import { resetRouter } from '@/router'
    2. // 退出的action操作
    3. logout(context) {
    4. // 1. 移除vuex个人信息
    5. context.commit('removeUserInfo')
    6. // 2. 移除token信息
    7. context.commit('removeToken')
    8. // 3. 重置路由
    9. resetRouter()
    10. }

  • 相关阅读:
    我的崽崽跑着跑就长大了
    爱上开源之golang入门至实战第四章-切片(Slice)
    【ICCV 2023】FocalFormer3D : Focusing on Hard Instance for 3D Object Detection
    【webrtc】老版本的OnReceivedPayloadData 及VCMPacket
    哪些地方会出现css阻塞和js阻塞_前端培训技术
    第2周学习:卷积神经网络基础
    搭建 Vite + Vue3 + TypeScript + Electron 项目
    netdata 监控软件安装与学习
    洛谷P2196 [NOIP1996 提高组] 挖地雷【动态规划思路分析】看完直接举一反三!
    AWS设备接入-MQTT方式
  • 原文地址:https://blog.csdn.net/LJM51200/article/details/126464331