5.6.9 • Published 5 months ago
@cimom/vben-effects-access v5.6.9
@cimom/vben-effects-access
权限控制工具包,提供了基于角色和权限的访问控制功能,包括指令、组件和钩子函数,用于控制界面元素的显示和功能的访问。
安装
npm install @cimom/vben-effects-access基本使用
权限指令
import { createAccessDirective } from '@cimom/vben-effects-access';
import type { App } from 'vue';
// 在应用启动时注册指令
export function setupDirectives(app: App) {
// 注册权限指令
app.directive('permission', createAccessDirective());
}<template>
<!-- 基于权限控制按钮显示 -->
<button v-permission="'user:create'">创建用户</button>
<!-- 基于角色控制按钮显示 -->
<button v-permission="{ role: 'admin' }">管理员操作</button>
<!-- 组合权限和角色 -->
<button
v-permission="{ permission: 'user:create', role: 'admin', mode: 'or' }"
>
创建用户
</button>
</template>权限组件
<template>
<div>
<!-- 使用权限控制组件包装内容 -->
<AccessControl permission="user:create">
<button>创建用户</button>
</AccessControl>
<!-- 使用角色控制 -->
<AccessControl :role="['admin', 'editor']">
<div>管理员或编辑可见内容</div>
</AccessControl>
<!-- 组合权限和角色,使用 AND 逻辑 -->
<AccessControl permission="user:create" role="admin" mode="and">
<button>管理员创建用户</button>
</AccessControl>
<!-- 自定义未授权时显示的内容 -->
<AccessControl permission="user:delete">
<button>删除用户</button>
<template #fallback>
<div class="no-permission">您没有删除权限</div>
</template>
</AccessControl>
</div>
</template>
<script setup lang="ts">
import { AccessControl } from '@cimom/vben-effects-access';
</script>使用钩子函数
import { useAccess } from '@cimom/vben-effects-access';
// 在组件中使用
const { hasPermission, hasRole, accessible } = useAccess();
// 检查是否有特定权限
if (hasPermission('user:create')) {
// 有创建用户的权限
}
// 检查是否有特定角色
if (hasRole('admin')) {
// 是管理员角色
}
// 组合检查权限和角色
const canCreateUser = accessible({
permission: 'user:create',
role: 'admin',
mode: 'or', // 'or' 或 'and'
});API 参考
权限指令 (v-permission)
指令可以接收以下值:
string: 权限字符串,如'user:create'string[]: 权限字符串数组,如['user:create', 'user:edit']object: 权限配置对象,包含以下属性:permission: 权限字符串或数组role: 角色字符串或数组mode: 组合模式,'or'(默认)或'and'
权限组件 (AccessControl)
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
permission | string \| string[] | - | 权限字符串或数组 |
role | string \| string[] | - | 角色字符串或数组 |
mode | 'or' \| 'and' | 'or' | 组合模式,'or' 表示满足任一条件,'and' 表示必须同时满足所有条件 |
fallback | slot | - | 未授权时显示的内容的插槽 |
钩子函数 (useAccess)
| 函数名 | 参数 | 返回值 | 说明 |
|---|---|---|---|
hasPermission | (permission: string \| string[]) => boolean | boolean | 检查是否有特定权限 |
hasRole | (role: string \| string[]) => boolean | boolean | 检查是否有特定角色 |
accessible | (options: AccessOptions) => boolean | boolean | 组合检查权限和角色 |
类型定义
// 访问控制选项
interface AccessOptions {
// 权限字符串或数组
permission?: string | string[];
// 角色字符串或数组
role?: string | string[];
// 组合模式: 'or' 表示满足任一条件,'and' 表示必须同时满足所有条件
mode?: 'or' | 'and';
}
// 权限指令值类型
type PermissionDirectiveValue = string | string[] | AccessOptions;高级用法
自定义权限检查逻辑
可以通过 createAccessDirective 函数的参数自定义权限检查逻辑:
import { createAccessDirective } from '@cimom/vben-effects-access';
import type { App } from 'vue';
// 自定义权限检查函数
function customPermissionChecker(permission: string | string[]): boolean {
// 实现自定义的权限检查逻辑
return true; // 或 false
}
// 自定义角色检查函数
function customRoleChecker(role: string | string[]): boolean {
// 实现自定义的角色检查逻辑
return true; // 或 false
}
// 在应用启动时注册指令
export function setupDirectives(app: App) {
// 注册权限指令,使用自定义检查逻辑
app.directive(
'permission',
createAccessDirective({
hasPermission: customPermissionChecker,
hasRole: customRoleChecker,
}),
);
}与状态管理结合使用
import { useAccess } from '@cimom/vben-effects-access';
import { useAccessStore } from '@cimom/vben-stores';
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
// 自定义钩子函数,结合状态管理
export function useCustomAccess() {
const accessStore = useAccessStore();
const { permissions, roles } = storeToRefs(accessStore);
// 使用标准的 useAccess 钩子
const { hasPermission, hasRole, accessible } = useAccess();
// 扩展功能
const isAdmin = computed(() => hasRole('admin'));
const canManageUsers = computed(() =>
hasPermission(['user:create', 'user:edit', 'user:delete']),
);
// 返回扩展后的钩子
return {
hasPermission,
hasRole,
accessible,
isAdmin,
canManageUsers,
permissions,
roles,
};
}动态权限路由
import { useAccess } from '@cimom/vben-effects-access';
import { createRouter, createWebHistory } from 'vue-router';
const { accessible } = useAccess();
// 路由配置
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue'),
meta: { title: '仪表盘' },
},
{
path: '/users',
component: () => import('./views/Users.vue'),
meta: {
title: '用户管理',
permission: 'user:view',
role: 'admin',
},
},
// 其他路由...
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 路由守卫
router.beforeEach((to, from, next) => {
if (to.meta.permission || to.meta.role) {
// 检查路由权限
const hasAccess = accessible({
permission: to.meta.permission,
role: to.meta.role,
mode: 'or',
});
if (hasAccess) {
next();
} else {
next('/403'); // 重定向到无权限页面
}
} else {
next();
}
});示例
权限菜单
<template>
<div class="menu">
<div class="menu-item" @click="navigateTo('/')">首页</div>
<AccessControl permission="dashboard:view">
<div class="menu-item" @click="navigateTo('/dashboard')">仪表盘</div>
</AccessControl>
<AccessControl permission="user:view">
<div class="menu-item" @click="navigateTo('/users')">用户管理</div>
<div class="submenu">
<AccessControl permission="user:create">
<div class="menu-item" @click="navigateTo('/users/create')">
创建用户
</div>
</AccessControl>
<AccessControl permission="user:import">
<div class="menu-item" @click="navigateTo('/users/import')">
导入用户
</div>
</AccessControl>
</div>
</AccessControl>
<AccessControl role="admin">
<div class="menu-item" @click="navigateTo('/settings')">系统设置</div>
</AccessControl>
</div>
</template>
<script setup lang="ts">
import { AccessControl } from '@cimom/vben-effects-access';
import { useRouter } from 'vue-router';
const router = useRouter();
function navigateTo(path: string) {
router.push(path);
}
</script>
<style scoped>
.menu {
width: 200px;
border-right: 1px solid #eee;
}
.menu-item {
padding: 12px 16px;
cursor: pointer;
}
.menu-item:hover {
background-color: #f5f5f5;
}
.submenu {
padding-left: 16px;
}
.submenu .menu-item {
font-size: 14px;
}
</style>权限按钮组
<template>
<div class="user-actions">
<AccessControl permission="user:view">
<button class="btn view-btn" @click="viewUser">查看</button>
</AccessControl>
<AccessControl permission="user:edit">
<button class="btn edit-btn" @click="editUser">编辑</button>
</AccessControl>
<AccessControl permission="user:delete" role="admin" mode="and">
<button class="btn delete-btn" @click="deleteUser">删除</button>
</AccessControl>
</div>
</template>
<script setup lang="ts">
import { AccessControl } from '@cimom/vben-effects-access';
const props = defineProps<{
userId: string;
}>();
function viewUser() {
console.log('查看用户:', props.userId);
}
function editUser() {
console.log('编辑用户:', props.userId);
}
function deleteUser() {
console.log('删除用户:', props.userId);
}
</script>
<style scoped>
.user-actions {
display: flex;
gap: 8px;
}
.btn {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.view-btn {
background-color: #e6f7ff;
color: #1890ff;
}
.edit-btn {
background-color: #f6ffed;
color: #52c41a;
}
.delete-btn {
background-color: #fff1f0;
color: #ff4d4f;
}
</style>权限表单
<template>
<form @submit.prevent="submitForm" class="user-form">
<div class="form-item">
<label>用户名</label>
<input v-model="form.username" type="text" />
</div>
<div class="form-item">
<label>邮箱</label>
<input v-model="form.email" type="email" />
</div>
<AccessControl permission="user:role:assign" role="admin">
<div class="form-item">
<label>角色</label>
<select v-model="form.role">
<option value="user">普通用户</option>
<option value="editor">编辑</option>
<option value="admin">管理员</option>
</select>
</div>
</AccessControl>
<div class="form-actions">
<button type="button" @click="cancel">取消</button>
<button type="submit" v-permission="'user:edit'">保存</button>
</div>
</form>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import { AccessControl } from '@cimom/vben-effects-access';
import { useAccess } from '@cimom/vben-effects-access';
const { hasPermission } = useAccess();
const form = reactive({
username: '',
email: '',
role: 'user',
});
function submitForm() {
if (hasPermission('user:edit')) {
console.log('保存用户:', form);
}
}
function cancel() {
console.log('取消编辑');
}
</script>
<style scoped>
.user-form {
max-width: 500px;
padding: 16px;
border: 1px solid #eee;
border-radius: 8px;
}
.form-item {
margin-bottom: 16px;
}
.form-item label {
display: block;
margin-bottom: 4px;
}
.form-item input,
.form-item select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 16px;
}
.form-actions button {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.form-actions button[type='submit'] {
background-color: #1890ff;
color: white;
border-color: #1890ff;
}
</style>