news 2026/9/12 6:36:57

vue-vben-admin 如何用权限码和角色实现按钮级细粒度权限控制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue-vben-admin 如何用权限码和角色实现按钮级细粒度权限控制

vue-vben-admin 如何用权限码和角色实现按钮级细粒度权限控制

【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin

在 vue-vben-admin 中,菜单和路由的显隐只是权限控制的一半,很多业务场景需要控制到按钮级别:同一个页面对不同账号显示不同的操作按钮。框架通过@vben/access包提供了一套按钮级控制能力,判断依据有两类:接口返回的权限码(access code)和登录用户的角色(role),两者都可以用组件、API、指令三种方式写在模板里。本文以 playground 演示应用为运行环境,说明权限码如何从接口流入前端状态、三种控制写法如何区分,以及如何切换账号验证按钮显隐是否符合预期。

准备:跑起带权限演示的 playground 环境

按钮级权限的完整链路(接口返回权限码 → 登录时存入状态 → 模板中判断显隐)在 playground 应用中有可运行的演示,后端由内置的 mock 服务提供,不需要自己搭接口。

环境要求与启动步骤(来自 快速开始):

  • Node.js 22.18.0 及以上版本;
  • 项目只支持使用pnpm安装依赖,默认通过corepack安装指定版本的 pnpm。
cd vue-vben-admin # 使用项目指定的pnpm版本进行依赖安装 npm i -g corepack # 安装依赖 pnpm install

然后只启动 playground 应用,避免在选择界面里逐项挑选:

pnpm run dev:play

启动后在浏览器访问http://localhost:5555。mock 数据里内置了三个账号(见 mock-data.ts),密码均为123456

账号用户名角色权限码
Supervben['super']AC_100100AC_100110AC_100120AC_100010
Adminadmin['admin']AC_100010AC_100020AC_100030
Userjack['user']AC_1000001AC_1000002

后续所有按钮显隐的验证都以这三个账号的差异为基准。

权限码从哪里来:登录流程中的注入点

按钮能否显示,前提是当前登录账号的权限码已经进入前端状态。登录流程位于应用的src/store/auth,playground 中的实现在 store/auth.ts:

const [fetchUserInfoResult, accessCodes] = await Promise.all([ fetchUserInfo(), getAccessCodesApi(), ]); userInfo = fetchUserInfoResult; accessStore.setAccessCodes(accessCodes);

也就是说,登录成功后会并行请求用户信息和权限码,权限码通过accessStore.setAccessCodes写入 access store,之后模板中的判断函数读取的都是这份状态。

对应接口在 api/core/auth.ts 中定义,为一个返回字符串数组的 GET 请求:

export async function getAccessCodesApi() { return requestClient.get<string[]>('/auth/codes'); }

权限码返回的数据结构为字符串数组,例如:['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010']

官方文档要求:找到getAccessCodes对应的接口,可根据业务逻辑进行调整。在本仓库中,mock 实现位于 apps/backend-mock/api/auth/codes.ts,它根据登录用户从 MOCK_CODES 中按用户名匹配出权限码数组返回;替换为真实后端时,只需要让新接口保持"字符串数组"这一返回结构。

三种写法控制按钮:组件、API、指令

拿到权限码后,用@vben/access提供的AccessControl组件和 API 来控制按钮显示与隐藏。以下示例与 docs/src/guide/in-depth/access.md 及 button-control.vue 中的演示一致,权限码均取自上表。

组件方式

AccessControl通过type属性区分判断依据:传type="code"时按权限码判断,codes支持单个或数组(多个权限码只要有一个满足即显示):

<script lang="ts" setup> import { AccessControl, useAccess } from '@vben/access'; const { accessMode, hasAccessByCodes } = useAccess(); </script> <template> <!-- 需要指明 type="code" --> <AccessControl :codes="['AC_100100']" type="code"> <Button> Super 账号可见 ["AC_100100"] </Button> </AccessControl> <AccessControl :codes="['AC_100030']" type="code"> <Button> Admin 账号可见 ["AC_100030"] </Button> </AccessControl> <AccessControl :codes="['AC_1000001']" type="code"> <Button> User 账号可见 ["AC_1000001"] </Button> </AccessControl> <AccessControl :codes="['AC_100100', 'AC_100030']" type="code"> <Button> Super & Admin 账号可见 ["AC_100100","AC_100030"] </Button> </AccessControl> </template>

API 方式

useAccess()暴露hasAccessByCodes,配合v-if使用,适合需要在逻辑里做进一步分支的场景:

<script lang="ts" setup> import { AccessControl, useAccess } from '@vben/access'; const { hasAccessByCodes } = useAccess(); </script> <template> <Button v-if="hasAccessByCodes(['AC_100100'])"> Super 账号可见 ["AC_100100"] </Button> <Button v-if="hasAccessByCodes(['AC_100030'])"> Admin 账号可见 ["AC_100030"] </Button> <Button v-if="hasAccessByCodes(['AC_1000001'])"> User 账号可见 ["AC_1000001"] </Button> <Button v-if="hasAccessByCodes(['AC_100100', 'AC_100030'])"> Super & Admin 账号可见 ["AC_100100","AC_100030"] </Button> </template>

指令方式

指令支持绑定单个或多个权限码:单个时可以直接传入字符串,多个则传入数组。判断不通过时元素会被移除(见 directive.ts 中el?.remove()的实现):

<template> <Button class="mr-4" v-access:code="'AC_100100'"> Super 账号可见 'AC_100100' </Button> <Button class="mr-4" v-access:code="['AC_100030']"> Admin 账号可见 ["AC_100030"] </Button> <Button class="mr-4" v-access:code="['AC_1000001']"> User 账号可见 ["AC_1000001"] </Button> <Button class="mr-4" v-access:code="['AC_100100', 'AC_1000001']"> Super & Admin 账号可见 ["AC_100100","AC_1000001"] </Button> </template>

按角色控制按钮及其适用条件

角色判断方式不需要接口返回的权限码,直接通过userInfo.roles判断按钮是否显示。三种写法与权限码一一对应:

<script lang="ts" setup> import { AccessControl } from '@vben/access'; </script> <template> <AccessControl :codes="['super']"> <Button> Super 角色可见 </Button> </AccessControl> <AccessControl :codes="['admin']"> <Button> Admin 角色可见 </Button> </AccessControl> <AccessControl :codes="['user']"> <Button> User 角色可见 </Button> </AccessControl> <AccessControl :codes="['super', 'admin']"> <Button> Super & Admin 角色可见 </Button> </AccessControl> </template>
<script lang="ts" setup> import { useAccess } from '@vben/access'; const { hasAccessByRoles } = useAccess(); </script> <template> <Button v-if="hasAccessByRoles(['super'])"> Super 账号可见 </Button> <Button v-if="hasAccessByRoles(['admin'])"> Admin 账号可见 </Button> <Button v-if="hasAccessByRoles(['user'])"> User 账号可见 </Button> <Button v-if="hasAccessByRoles(['super', 'admin'])"> Super & Admin 账号可见 </Button> </template>
<template> <Button class="mr-4" v-access:role="'super'"> Super 角色可见 </Button> <Button class="mr-4" v-access:role="['admin']"> Admin 角色可见 </Button> <Button class="mr-4" v-access:role="['user']"> User 角色可见 </Button> <Button class="mr-4" v-access:role="['super', 'admin']"> Super & Admin 角色可见 </Button> </template>

两点需要注意:

  • AccessControl组件的type属性默认为'role'(见 access-control.vue),所以按角色控制时可以不显式写type;按权限码控制时必须写type="code"
  • v-access:role指令只在权限模式为frontend时才按角色判断,其他模式下该指令会走权限码判断逻辑(见 directive.ts 中accessMode.value === 'frontend' && binding.arg === 'role'的分支)。这也解释了为什么演示页面 button-control.vue 只在accessMode === 'frontend'时才渲染角色相关的卡片。

权限模式在应用目录的preferences.ts中通过app.accessMode配置,可选frontendbackendmixed,写法参考 权限文档。以 playground 为例,在 playground/src/preferences.ts 的overridesPreferences.app中加一行:

app: { accessMode: 'frontend', },

该文件注释明确提醒:更改配置后请清空缓存,否则可能不生效。文档同时说明了前端模式的局限:权限相对不自由,如果后台改动角色,前台也需要跟着改动,适合角色较固定的系统;如果角色或权限码经常变化,应使用后端/混合模式,改由接口决定权限。

验证:切换账号观察按钮变化

验证方式直接沿用演示页的设计——切换到不同账号,观察同一页面上按钮集合的变化。以权限码为例,对照表可以预先列出预期结果(依据是上文三个账号的权限码数据):

按钮绑定的权限码vben (Super)admin (Admin)jack (User)
['AC_100100']显示隐藏隐藏
['AC_100030']隐藏显示隐藏
['AC_1000001']隐藏隐藏显示
['AC_100100', 'AC_100030']显示显示隐藏

具体操作路径:

  1. vben/123456登录,进入演示路由/demos/access/button-control(mock 菜单数据中该路由名为AccessButtonControlDemo,对应组件 button-control.vue)。
  2. 确认页面上当前显示 Super 可见的按钮(AC_100100AC_100100+AC_100030组合),不显示 Admin、User 按钮。
  3. 在页面"账号切换"卡片中点击"切换为 Admin 账号",页面内部执行resetAllStores()后重新登录并刷新(router.go(0)),此时AC_100030相关按钮出现、Super 按钮消失。
  4. 再切换到 User 账号(jack),只应看到AC_1000001相关按钮。

页面顶部会显示"当前角色",可以用来确认切换账号已经生效;如果按钮显示与上表不一致,优先检查登录流程里getAccessCodesApi()是否真的被调用并写入了accessStore,以及权限码字符串是否与接口返回值完全一致(权限码是精确字符串匹配,如AC_100100AC_1000001是两个不同的码)。

限制与边界

  • AccessControl组件在codes为空数组时会直接渲染插槽内容(v-if="!codes"),即不传权限码等于不控制,配置漏传不会导致按钮被隐藏,排查"按钮不该显示却显示了"时要先确认codes真的传入了。
  • 按钮级控制只负责前端显示/隐藏。菜单级别还有一个相关但不同的机制:meta.menuVisibleWithForbidden: true可以让菜单可见但访问时跳转 403,这是路由权限配置,与本文的按钮控制不冲突,也不要用它来隐藏按钮。
  • 前端权限码和角色都只控制展示,真实的数据安全仍要由后端接口自行校验,这一点在框架层面不做保证,接入真实后端时需要自行落实。

完成以上配置后,按钮级权限的落地就完整了:接口负责发放权限码,store/auth负责在登录时写入状态,模板里用组件、API 或指令三选一完成显隐判断,最后用三个内置账号的切换演示核对每个按钮的归属是否符合预期。

【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 6:34:52

Prompt as Code:工业级提示工程实践指南

我无法根据当前输入生成符合要求的博文。 原因如下&#xff1a; 项目标题 "awesome-gpt-image-2" 缺乏明确的技术指向、功能定义或领域背景&#xff0c;仅是一个命名风格&#xff08;类似开源项目命名惯例&#xff09;&#xff0c;但未说明其本质是工具库&#xff…

作者头像 李华
网站建设 2026/9/12 6:33:37

Linux离线开发环境部署实战与解决方案

1. 项目概述&#xff1a;Linux开发环境离线安装的核心挑战在嵌入式开发和工业自动化领域&#xff0c;经常遇到需要在内网或隔离环境中搭建完整开发环境的情况。传统在线安装方式依赖网络仓库&#xff0c;而HoRain云方案提供了一套完整的离线部署方法论。我最近在国产化替代项目…

作者头像 李华
网站建设 2026/9/12 6:31:32

具身智能数据采集实验方案:标准化流程与量化测评实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 6:28:41

Jupyter Notebook 7 Tab缩进失效?5步自查到修复的完整排障路径

Jupyter Notebook 7 Tab缩进失效&#xff1f;5步自查到修复的完整排障路径 【免费下载链接】notebook Jupyter Interactive Notebook 项目地址: https://gitcode.com/GitHub_Trending/no/notebook 在 Jupyter Notebook 7 的代码单元格里按 Tab 没反应&#xff0c;手动敲…

作者头像 李华