背景:平常所接触到的系统权限控制,大部分都是菜单、路由级别的控制,但后台管理系统中,很多操作都是与职责和角色挂钩的,同样一个列表,不同人的操作列并不都一样,有些页面存在一些含有重要数据的组件,也会进行相应的权限控制,仅让领导层能看到。
按钮级权限:根据用户的权限不同,对不同的按钮进行权限控制,对同一组数据,不同的用户是否可以增删改查。对某些用户来说是只读浏览数据,对某些用户来说是可编辑的数据。除了按钮,比如页面中的某个字段,某个div,某个组件要求根据当前用户的权限进行控制时,都可以称为按钮级权限。
因此,有必要实现按钮/组件级别的权限控制,为了更加方便高效地使用更细粒度的权限控制,封装了一个自定义指令插件v-permission,可实现随时注入使用。
v-permission插件
一个基于Vue3进行封装的自定义指令,在这个插件中,你可以检查传入的系统权限列表和用户拥有的权限列表,来确定用户是否具有某个组件/按钮级别的权限,实现更细粒度的权限控制。
功能:
① 指令:有权限时显示 UI,无权限时不渲染。避免出现用户看得见按钮,点击却无反应或出现无权限提示,这种不友好的使用体验。
②方法:可用于一些前置处理,比如进入页面时,根据是否有权限来判断默认渲染哪个组件作为该用户能看到的首页。
方式一:封装成可下载的npm包
在公司中,为了减少重复劳动,防止重复造轮子,开发的这种通用插件是直接传到npm上,再下载到项目中使用的,于是笔者将该插件单独写在一个很简单的小项目中,具体可见:
该插件的源代码及其使用文档均放在该仓库中。
GitHub - yoguoer/v-permissionContribute to yoguoer/v-permission development by creating an account on GitHub.https://github.com/yoguoer/v-permission.git
方式二:在项目中直接封装插件
当然啦,我们也可以直接在项目中去实现这个插件,下面介绍一下操作步骤。
1. 在根目录下新建一个directive文件夹,用来存放我们自己开发的所有插件。为了便于区分,我们再建一个vPermission文件夹,用来存放我们的v-permission插件。
2. 在directive/vPermission/permission.ts文件写入以下内容:
/**
* 权限指令
* 使用: v-permission="{module:'模块名称',auth:'权限key值'}"
* const hasPermi = hasPermissions({ module: 'someModule', auth: 'someAuth' });
*/
/**
* 初始化全局权限判断方法
* permissionList 系统预先配置的权限列表,包含所有权限信息
* permissions 用户当前权限列表(服务端返回接口权限列表数据)
*/
export function initHasPermission(options: {
permissionList: Array<string> | null,
permissions: Array<string> | null
}
) {
const { permissionList = null, permissions = null } = options;
// 返回一个函数,该函数接收一个权限对象并返回是否有权限
return (permission: {
module: string,
auth: string,
}) => {
if (!permissionList || !permissions) {
throw new Error('permissionList or permissions is null');
}
if (permission.module && permission.auth) {
const value = permissionList[permission.module][permission.auth];
return permissions.includes(value);
}
return false;
};
}
// 检查权限并执行相应的操作
function checkPermission(el: any, binding: any, hasPermissions: Function) {
if (typeof binding.value === 'object' && binding.value.module && binding.value.auth) {
if (!hasPermissions(binding.value)) {
el.style.display = 'none';
} else {
el.style.display = ''; // 如果有权限,确保元素可见
}
}
}
// 权限指令
// 创建一个返回指令对象的函数,该函数接受 hasPermissions 函数作为参数
export default function createPermissionDirective(hasPermissions: Function) {
return {
mounted(el: any, binding: any) {
checkPermission(el, binding, hasPermissions);
},
updated(el: any, binding: any) {
checkPermission(el, binding, hasPermissions);
}
};
}
3. 在directive/vPermission/index.ts文件写入以下内容:
import createPermissionDirective from './permission';
import { initHasPermission } from './permission'
// Vue 3 插件定义
const install = function (app: any, options: {
permissionList: Array<string> | null,
permissions: Array<string> | null
} = {
/** permissionList 系统预先配置的权限列表,包含所有权限信息
* permissions 用户当前权限列表(服务端返回接口权限列表数据)
*/
permissionList: null,
permissions: null
}) {
// 初始化权限检查函数
const hasPermissions = initHasPermission(options);
// 添加全局方法 $hasPermissions
app.config.globalProperties.$hasPermissions = hasPermissions;
// 提供全局的权限检查对象
app.provide('hasPermissions', app.config.globalProperties.$hasPermissions);
// 使用 hasPermissions 函数创建指令对象
const permissionDirective = createPermissionDirective(hasPermissions);
// 注册全局指令 v-permission
app.directive('permission', permissionDirective);
}
// 导出插件对象
export default {
install
};
插件使用方法
- 在你的项目根目录中新建一个permission文件夹,并在文件夹下新建一个index.ts文件和一个modules文件夹。
modules文件夹用来放置不同模块的权限控制文件(一般各个模块各自建一个.ts文件),而index.ts用来遍历读取modules下的所有文件,并将所有文件转换为键值对的形式,整合成一个包含系统所有权限信息的对象,即:{ 模块名:{ 该模块的权限列表 }, ... }。
- 在index.ts文件中键入以下代码:
/**
* 权限配置模块文件,统一引入所有权限配置文件
*/
const files = import.meta.glob('./modules/*.ts');
const modules = {};
for (const path in files) {
files[path]().then((mod) => {
let fileNameMatch = path.match(/([^\/\\]+?)\.\w+$/);
let fileName = fileNameMatch ? fileNameMatch[1] : null;
modules[fileName as string] = mod?.default
});
}
export default modules
- 在modules下新建所需要的模块文件,文件中的内容格式形如以下例子(权限key值: 权限标识):
export default {
add: '/add-add',
delete: '/delete-delete',
edit: '/edit-edit',
}
- 引入index.ts中整理好的系统预先配置的权限列表,作为我们所需要的参数permissionList,。
import permissionList from './permission'
- 通过接口获取当前用户的权限列表,作为我们所需要的参数permissions。
import permissions from '存放服务端返回接口权限列表数据的地方'
permissionList形如:
{ "admin": { "add": "/admin-add", "delete": "/admin-delete", "edit": "/admin-edit" }, "user": { "add": "/user-add", "delete": "/user-delete" } }
permissions形如:
[ "/user-add", "/user-delete" ]
- 在项目中的入口文件中引入 v-permission,将前面提到的两个参数作为选项,然后使用插件并传入选项:
import { createApp } from 'vue'
import App from './views/App.vue'
import permission from "@/directive/permission"
import permissionList from '@/permission'
import permissions from '存放服务端返回接口权限列表数据的地方'
const app = createApp(App);
const options = {
permissionList,
permissions
}
app.use(permission, options)
app.mount('#app')
- 在文件中使用。
-
以指令的方式使用:
-
v-permission指令形式:
<el-button v-permission="{ module: '模块名', auth: '权限key值' }"> 有权限则显示 </el-button>
-
v-if指令形式:
<el-button v-if="hasPermissions({ module: '模块名', auth: '权限key值' })"> 有权限则显示 </el-button>
-
-
以方法的方式使用:
import { inject } from "vue"; // 注入权限判断方法 hasPermissions const hasPermissions = inject("hasPermissions"); if (hasPermissions({ module: '模块名', auth: '权限key值' })) { console.log("用户有权限"); } else { console.log("用户没有权限"); }