【V3 Admin Vite】教程五:前端权限(涉及角色、动态路由、权限函数、权限指令)

前言

本系列文章是为了帮助没有直接上手(或上手比较困难)做项目能力的初级前端开发工程师采用 V3 Admin Vite 开源模板来编写业务代码。

如果你是一个有经验的朋友,那建议你直接阅读文档即可:V3 Admin Vite 中文文档,因为本系列教程节奏偏慢。

本系列文章的同步视频教程版本地址:B 站(群友好心录制)

文章目的

本文将带你学习该项目是如何通过用户的 “角色” 字段来进行页面级别的权限控制(动态路由的挂载);以及通过角色字段进行内容级别的权限控制(权限函数、权限指令)

Begin

页面权限

动态路由

@/router/index.ts 就是用来存放常驻路由和动态路由的文件,如图所示:

router/index.ts

不管什么情况下,都需要挂载的路由,我们就存放在 constantRoutes 数组下,比如登录页、首页;需要用户登录并根据角色字段来判断是否有权限的路由,我们就放在 asyncRoutes 数组下,并且要为该路由配置好 rolesname 属性

下面源码就是项目中写好的一个动态路由示例,注意看它是有 rolesname 属性的:

{
  path: "/permission",
  component: Layout,
  redirect: "/permission/page",
  name: "Permission", // 不要忘了写
  meta: {
    title: "权限管理",
    svgIcon: "lock",
    roles: ["admin", "editor"], // 可以在根路由中设置角色
    alwaysShow: true // 将始终显示根菜单
  },
  children: [
    {
      path: "page",
      component: () => import("@/views/permission/page.vue"),
      name: "PagePermission", // 不要忘了写
      meta: {
        title: "页面权限",
        roles: ["admin"] // 或者在子导航中设置角色
      }
    },
    {
      path: "directive",
      component: () => import("@/views/permission/directive.vue"),
      name: "DirectivePermission", // 不要忘了写
      meta: {
        title: "指令权限" // 如果未设置角色,则表示:该页面不需要权限,但会继承根路由的角色
      }
    }
  ]
}

演示

根据上文的动态路由示例代码,反映到页面上就是这样的:

登录 admin 账号时,可以看见这两个页面

admin

但是在登录 editor 账号时,只能看见一个了

editor

开启动态路由功能

项目默认是开启状态

写好了动态路由后,我们在 @/config/async-route.ts 文件中可以找到是否开启动态路由的开关,源码如下,只需要将下面代码中的 asyncRouteSettings.open 设置为 true 就可以开启动态路由功能:

/** 动态路由配置 */
interface IAsyncRouteSettings {
  /**
   * 是否开启动态路由功能?
   * 1. 开启后需要后端配合,在查询用户详情接口返回当前用户可以用来判断并加载动态路由的字段(该项目用的是角色 roles 字段)
   * 2. 假如项目不需要根据不同的用户来显示不同的页面,则应该将 open: false
   */
  open: boolean
  /** 当动态路由功能关闭时:
   * 1. 应该将所有路由都写到常驻路由里面(表明所有登陆的用户能访问的页面都是一样的)
   * 2. 系统自动给当前登录用户赋值一个没有任何作用的默认角色
   */
  defaultRoles: Array<string>
}

const asyncRouteSettings: IAsyncRouteSettings = {
  open: true,
  defaultRoles: ["DEFAULT_ROLE"]
}

export default asyncRouteSettings

开启以后,主要是作用于路由守卫 @/router/permission.ts 中的这样一段代码:

if (asyncRouteSettings.open) {
  // 注意:角色必须是一个数组! 例如: ['admin'] 或 ['developer', 'editor']
  await userStore.getInfo()
  const roles = userStore.roles
  // 根据角色生成可访问的 Routes(可访问路由 = 常驻路由 + 有访问权限的动态路由)
  permissionStore.setRoutes(roles)
} else {
  // 没有开启动态路由功能,则启用默认角色
  userStore.setRoles(asyncRouteSettings.defaultRoles)
  permissionStore.setRoutes(asyncRouteSettings.defaultRoles)
}

简单来说(其实注释已经写的很清楚了)就是:如果开启该功能,那么通过用户详情接口拿到用户角色数组后,根据角色去过滤动态路由,然后再通过 router.addRoute() 挂载过滤之后的动态路由

关闭动态路由功能

假如,你选择关闭动态路由功能,那么你需要记得将所有路由都写在常驻路由数组里面(虽然写在动态路由数组里也行,因为程序兼容了这种偷懒)

这样的话,所有登陆的用户能访问的页面都是一模一样的了

内容权限

权限函数

@/utils/permission.ts 文件里,有一个 checkPermission 权限判断函数:

import { useUserStoreHook } from "@/store/modules/user"

/** 权限判断函数 */
export const checkPermission = (value: string[]): boolean => {
  if (value && value instanceof Array && value.length > 0) {
    const roles = useUserStoreHook().roles
    const permissionRoles = value
    return roles.some((role) => {
      return permissionRoles.includes(role)
    })
  } else {
    console.error("need roles! Like checkPermission(['admin','editor'])")
    return false
  }
}

向该函数传递一个权限数组,然后它会去对比当前登录用户的角色数组,如果能匹配上,就返回 true

使用方法非常简单,checkPermission 函数配合 v-if 即可:

// 引入
import { checkPermission } from "@/utils/permission"

// 使用
<el-button v-if="checkPermission(['admin'])">按钮</el-button>

更多详细的使用案例,可见 @/views/permission/directive.vue 页面

权限指令

@/directives/permission/index.ts 文件里,写好了权限判断指令 v-permission

import { type Directive } from "vue"
import { useUserStoreHook } from "@/store/modules/user"

/** 权限指令 */
export const permission: Directive = {
  mounted(el, binding) {
    const { value } = binding
    const roles = useUserStoreHook().roles
    if (value && value instanceof Array && value.length > 0) {
      const permissionRoles = value
      const hasPermission = roles.some((role) => {
        return permissionRoles.includes(role)
      })
      if (!hasPermission) {
        el.style.display = "none"
      }
    } else {
      throw new Error(`need roles! Like v-permission="['admin','editor']"`)
    }
  }
}

向该指令传递一个权限数组,然后它会去对比当前登录用户的角色数组,如果不能匹配上,就通过 CSS style.display = "none" 将其隐藏

v-permission 已经通过 app.directive() 挂载完成,可以直接在 template 中直接使用:

<el-button v-permission="['admin']">按钮</el-button>

更多详细的使用案例,可见 @/views/permission/directive.vue 页面

后端返回动态路由?

虽然前端控制动态路由是我个人比较喜欢的一种方式,但是也同样有人偏爱后端返回动态路由,这项功能的话是已经在该项目的后续维护计划中,到时候会同步放上配套的用户管理、角色管理、权限管理三个页面

敬请期待~

End

本系列所有手摸手教程

V3 Admin Vite 相关链接

猜你喜欢

转载自juejin.im/post/7226261250576597050
今日推荐