# 权限指令 v-perm
:::tip 说明
`v-perm` 和 `v-perms` 是项目自定义的 Vue 指令,用于按钮级别的权限控制,无权限时自动从 DOM 中移除元素。底层通过 `usePermission` Hook 检查用户权限列表。
:::
## 指令定义
权限指令定义在 `src/directives/permission.ts` 中:
```typescript
// src/directives/permission.ts
import { ObjectDirective } from 'vue';
import { usePermission } from '@/hooks/web/usePermission';
// v-perm:满足任一权限即可显示
export const perm: ObjectDirective = {
mounted(el: HTMLButtonElement, binding) {
if (binding.value == undefined) return;
const { hasSomePermission } = usePermission();
if (!hasSomePermission(binding.value)) {
el.parentNode.removeChild(el);
}
},
};
// v-perms:必须满足所有权限才显示
export const perms: ObjectDirective = {
mounted(el: HTMLButtonElement, binding) {
if (binding.value == undefined) return;
const { hasEveryPermission } = usePermission();
if (!hasEveryPermission(binding.value)) {
el.parentNode.removeChild(el);
}
},
};
```
指令在 `mounted` 钩子中检查当前用户的权限列表,如果不满足权限条件,直接从 DOM 中移除元素。
## usePermission Hook
权限检查的核心逻辑在 `src/hooks/web/usePermission.ts` 中:
```typescript
// src/hooks/web/usePermission.ts
import { useUserStore } from '@/store/modules/user';
export function usePermission() {
const userStore = useUserStore();
function _somePermissions(accesses: string[]) {
const permissionsList = userStore.getPermissions;
if (permissionsList[0] == '*:*:*') return true; // 超级管理员通配符
return accesses.some((item) => permissionsList.includes(item));
}
// 是否包含其中某个权限
function hasSomePermission(accesses?: string | string[]): boolean {
const permissionsList = userStore.getPermissions;
if (permissionsList[0] === '*:*:*') return true;
const list = Array.isArray(accesses) ? accesses : [];
return list.some((access) => permissionsList.includes(access));
}
// 是否包含指定的所有权限
function hasEveryPermission(accesses?: string | string[]): boolean {
const list = Array.isArray(accesses) ? accesses : [];
const permissionsList = userStore.getPermissions;
if (permissionsList[0] === '*:*:*') return true;
return list.every((access) => permissionsList.includes(access));
}
// 判断是否存在权限(可用于 v-if)
function hasPermission(accesses: string[]): boolean {
if (!accesses || !accesses.length) return true;
return _somePermissions(accesses);
}
return { hasPermission, hasEveryPermission, hasSomePermission };
}
```
权限列表在登录后通过 `userStore.GetInfo()` 从后端获取,存储在 Pinia 的 user store 中。
## 基本用法
### v-perm(满足任一权限)
```vue
添加岗位
删除
导出
```
### v-perms(满足所有权限)
```vue
高级操作
```
### 与 v-if 结合
```typescript
import { usePermission } from '@/hooks/web/usePermission';
const { hasPermission } = usePermission();
// 在模板中使用
const showAddBtn = computed(() => hasPermission(['sys:position:add']));
```
```vue
添加岗位
```
## 实际用法(position 模块)
以岗位管理页面为例,实际代码如下:
```vue
添加岗位
删除
```
操作列中的权限控制通过 `auth` 属性实现:
```typescript
const actionColumn = reactive({
width: 200,
label: '操作',
fixed: 'right',
render(record) {
return h(TableAction, {
style: 'button',
actions: [
{
label: '编辑',
icon: 'Edit',
type: 'warning',
onClick: handleEdit.bind(null, record),
auth: ['sys:position:update'], // 权限节点
},
{
label: '删除',
icon: 'Delete',
type: 'danger',
onClick: handleDelete.bind(null, record),
auth: ['sys:position:delete'], // 权限节点
},
],
});
},
});
```
## 权限节点命名规范
权限节点格式:`sys:{module}:{action}`
```
sys:position:page —— 分页查询
sys:position:detail —— 查询详情
sys:position:list —— 获取列表
sys:position:add —— 新增
sys:position:update —— 编辑
sys:position:delete —— 删除
sys:position:batchDelete —— 批量删除
sys:position:status —— 设置状态
sys:position:export —— 导出
sys:position:import —— 导入
```
## 与后端对应
前端 `v-perm` 指令的权限节点必须与后端 `@permission_required` 装饰器的权限字符串一致:
```python
# 后端
class PositionAddHandler(BaseHandler):
@permission_required("sys:position:add")
@check_demo
async def post(self):
return await position_service.add(self)
```
```vue
添加岗位
```
## 超级管理员
用户权限列表中包含 `*:*:*` 通配符时,所有权限检查直接返回 `true`,等同于后端用户 ID=1 的管理员跳过权限检查。
:::tip 温馨提示
`v-perm` 指令仅做 UI 层隐藏,真正的权限校验在后端 `@permission_required` 装饰器中。即使前端绕过指令限制直接调用接口,后端仍会校验权限。
:::
## 指令注册
指令在 `src/plugins/` 中通过 `setupDirectives` 全局注册:
```typescript
// 启动时注册(main.ts)
setupDirectives(app); // 注册 v-perm、v-perms 等指令
```
## 总结
`v-perm`(满足任一权限)和 `v-perms`(满足所有权限)指令通过 `usePermission` Hook 检查用户权限列表,无权限时自动从 DOM 移除元素。操作列通过 `TableAction` 组件的 `auth` 属性控制。权限节点在菜单管理中配置,需要保持前后端字符串完全一致。`*:*:*` 通配符表示超级管理员。