Skip to content

权限控制

你需要让「普通用户」看不到「管理员」按钮?本页介绍三种粒度的权限控制方案,从页面级到按钮级,按需选用。

权限体系概览

权限系统由三层组成:

┌─────────────────────────────────────────┐
│  动态路由(页面级)                        │
│  → 用户能看到哪些页面                      │
├─────────────────────────────────────────┤
│  角色 RBAC(数据级)                       │
│  → 用户能看到哪些数据                      │
├─────────────────────────────────────────┤
│  v-hasPerm(按钮级)                      │
│  → 用户能看到哪些按钮                      │
└─────────────────────────────────────────┘

三层独立工作,可组合使用。

路由权限(页面级)

用户登录后,根据角色权限动态生成可访问的路由。

实现原理

路由守卫首次进入时生成动态路由:

typescript
// src/router/guards/permission.ts
if (!permissionStore.isRouteGenerated) {
  const dynamicRoutes = await permissionStore.generateRoutes()
  dynamicRoutes.forEach((route) => router.addRoute(route))
  next({ ...to, replace: true })
}

路由配置

在路由 meta 中声明权限标识:

typescript
{
  path: "/system/user",
  component: () => import("@/views/system/user/index.vue"),
  meta: {
    title: "用户管理",
    icon: "user",
    perms: ["sys:user:list"],  // 权限标识
    roles: ["ADMIN"],          // 角色限制
  },
}

按钮权限

指令方式

使用 v-hasPerm 指令控制按钮显隐:

vue
<template>
  <!-- 单个权限 -->
  <el-button v-hasPerm="['sys:user:add']">新增</el-button>
  
  <!-- 多个权限(满足其一即可) -->
  <el-button v-hasPerm="['sys:user:edit', 'sys:user:update']">编辑</el-button>
</template>

函数方式

在逻辑中判断权限:

typescript
import { hasPerm } from "@/utils/auth"

if (hasPerm(["sys:user:add"])) {
  // 有权限时执行
}

指令实现

typescript
// src/directives/permission/index.ts
export const hasPerm = {
  mounted(el, binding) {
    const userStore = useUserStore()
    const perms = userStore.userInfo?.perms || []
    
    const requiredPerms = binding.value
    const hasPermission = requiredPerms.some(p => perms.includes(p))
    
    if (!hasPermission) {
      el.parentNode?.removeChild(el)
    }
  }
}

权限标识规范

采用 模块:功能:操作 格式:

格式示例
模块:功能:操作sys:user:add
常见操作list, add, edit, delete, export

常见问题

刷新后路由消失?

动态路由在内存中,刷新后需重新生成。路由守卫已处理此场景。

按钮权限不生效?

  1. 检查权限标识是否与后端一致
  2. 确认用户已正确获取权限列表
  3. 打开控制台查看 userStore.userInfo.perms

权限更新不及时?

重新登录,或清除 Pinia 缓存:

typescript
const userStore = useUserStore()
userStore.resetUserInfo()

源码位置

  • 路由守卫:src/router/guards/permission.ts
  • 动态路由生成:src/store/modules/permission-store.ts
  • 权限指令:src/directives/permission/index.ts
  • 权限函数:src/utils/auth.ts

下一步

基于 MIT 许可发布 · 如需部署协助或二开定制,请查看 支持与合作