5.6.9 • Published 5 months ago

@cimom/vben-effects-access v5.6.9

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@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)

属性名类型默认值说明
permissionstring \| string[]-权限字符串或数组
rolestring \| string[]-角色字符串或数组
mode'or' \| 'and''or'组合模式,'or' 表示满足任一条件,'and' 表示必须同时满足所有条件
fallbackslot-未授权时显示的内容的插槽

钩子函数 (useAccess)

函数名参数返回值说明
hasPermission(permission: string \| string[]) => booleanboolean检查是否有特定权限
hasRole(role: string \| string[]) => booleanboolean检查是否有特定角色
accessible(options: AccessOptions) => booleanboolean组合检查权限和角色

类型定义

// 访问控制选项
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>
5.6.9

5 months ago

5.6.3

5 months ago

5.6.1

5 months ago

5.6.0

5 months ago

5.5.9

5 months ago