Become a sponsor

说明
v-perm 和 v-perms 是项目自定义的 Vue 指令,用于按钮级别的权限控制,无权限时自动从 DOM 中移除元素。底层通过 usePermission Hook 检查用户权限列表。
权限指令定义在 src/directives/permission.ts 中:
// 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 中移除元素。
权限检查的核心逻辑在 src/hooks/web/usePermission.ts 中:
// 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 中。
<el-button v-perm="['sys:position:add']">添加岗位</el-button>
<el-button v-perm="['sys:position:batchDelete']">删除</el-button>
<el-button v-perm="['sys:position:export']">导出</el-button><el-button v-perms="['sys:user:add', 'sys:user:import']">高级操作</el-button>import { usePermission } from '@/hooks/web/usePermission';
const { hasPermission } = usePermission();
// 在模板中使用
const showAddBtn = computed(() => hasPermission(['sys:position:add']));<el-button v-if="showAddBtn">添加岗位</el-button>以岗位管理页面为例,实际代码如下:
<!-- src/views/system/position/index.vue -->
<template>
<BasicTable :columns="columns" :request="loadDataTable" ref="tableRef" :actionColumn="actionColumn">
<template #tableTitle>
<el-button type="primary" @click="handleAdd" v-perm="['sys:position:add']">
<template #icon><el-icon><PlusOutlined /></el-icon></template>
添加岗位
</el-button>
<el-button type="danger" @click="handleDelete()" :disabled="!selectionData.length"
v-perm="['sys:position:batchDelete']">
<template #icon><el-icon><Delete /></el-icon></template>
删除
</el-button>
</template>
</BasicTable>
</template>操作列中的权限控制通过 auth 属性实现:
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 装饰器的权限字符串一致:
# 后端
@router.post('/add')
@permission_required("sys:position:add")
@check_demo
def add(request: Request, data: PositionForm):
return position_service.add(request, data)<!-- 前端 -->
<el-button v-perm="['sys:position:add']">添加岗位</el-button>用户权限列表中包含 *:*:* 通配符时,所有权限检查直接返回 true,等同于后端用户 ID=1 的管理员跳过权限检查。
温馨提示
v-perm 指令仅做 UI 层隐藏,真正的权限校验在后端 @permission_required 装饰器中。即使前端绕过指令限制直接调用接口,后端仍会校验权限。
指令在 src/plugins/ 中通过 setupDirectives 全局注册:
// 启动时注册(main.ts)
setupDirectives(app); // 注册 v-perm、v-perms 等指令v-perm(满足任一权限)和 v-perms(满足所有权限)指令通过 usePermission Hook 检查用户权限列表,无权限时自动从 DOM 移除元素。操作列通过 TableAction 组件的 auth 属性控制。权限节点在菜单管理中配置,需要保持前后端字符串完全一致。*:*:* 通配符表示超级管理员。