最近做到了一个权限控制的功能。在这个功能中看到了这个功能的写法,了解到了这个权限控制的功能,也引发了思考。除了 v-if ,还有没有更好的,更直观的,更全局的写法呢?带着这个问题,我们先来看一下,这个权限数据的来源。
获取数据的方法也很简单,就是后台接口请求数据。后端在登录后一次性返回所有的权限数据,然后全局 localStorage 或者 store 存储起来。如果发生了修改就可以及时的通知到全局,在使用获取的时候也方便快捷。代码如下:
- import { defineStore } from 'pinia'
- // 声明,初始化
- const defaultState = {
- permissions: {}, // 权限列表
- }
-
- // 设置state,存储
- function setData(obj) {
- Object.keys(obj).forEach((key) => {
- state[key] = obj[key]
- })
- // 每次改动更新缓存
- localStorage.setItem(LOCAL_STORAGE_NAME, JSON.stringify(toRaw(state)))
- }
-
- // 请求权限码
- async function getPermissionList() {
- const res = await ajaxStore.common.getPermissionList()
- if (res.data?.code === '0') {
- const arr = res.data?.data ?? []
- const obj = {}
- arr.forEach((item) => (obj[item.permissionCode] = item.permissionName))
- // 全局存储
- setData({ permissions: obj })
- return state.permissions
- } else {
- return null
- }
- }
获取到数据以后,就可以来思考怎么使用这些数据。目前,所知的有3种权限控制方式,下面我们一个一个的来看。
这是我们最先想到的方法,也是最常用的方法。代码如下:
- <template>
- <div>
- <Tabs :data="tabConfigs"></Tabs>
- <NoPower v-if="!(tabConfigs && tabConfigs.length)"></NoPower>
- </div>
- </template>
- <script setup name="realestateWarning">
- // 获取 permissions 数据
- import useUser from '@/store/user'
- // 获取所有的菜单
- import { realestateWarningTabs } from '@/const/menu'
-
- const userInfo = useUser()
- // 过滤tab权限
- const tabConfigs = computed(() => {
- const { permissions } = userInfo ?? {}
- return (
- realestateWarningTabs.filter((item) => {
- item.label = item.text
- return permissions[item.permissionCode]
- }) ?? []
- )
- })
- </script>
本质就是使用 一个 tabConfigs 来筛选需要显示哪些 tab,其中操作是从全局 store 中获取当前用户的权限码列表,然后判断其中是否存在当前需要的权限码,如果有对应的权限码,就加进来。
我们可以单独写一个组件,在组件内部进行权限的控制操作,主要是通过插槽的形式把内部需要展示的组件引入进来,在需要加权限的地方进行组件的引入,组件内部的逻辑和函数方式基本无异。这样就可以统一管理权限了,使用起来也比较方便。代码如下:
- <template>
- <div>
- <Permission :value="AAAAAA">
- <AAAAAA/>
- </Permission>
- <Permission :value="BBBBBB">
- <BBBBBB/>
- </Permission>
- <Permission :value="CCCCCC">
- <CCCCCC/>
- </Permission>
- </div>
- </template>
- <script>
- import { Permission } from './Permission';
- </script>
- <script>
- // 获取 permissions 数据
- import useUser from '@/store/user'
- // 获取所有的菜单
- import { realestateWarningTabs } from '@/const/menu'
-
- export default defineComponent({
- props: {
- value: {
- type: [Number, Array, String],
- default: '',
- },
- },
- setup(props, { slots }) {
- const userInfo = useUser()
- const { permissions } = userInfo ?? {}
- const { hasPermission } = usePermission();
-
- function renderAuth() {
- const { value } = props;
- if (!value) {
- return getSlot(slots);
- }
- return permissions[value] ? getSlot(slots) : null;
- }
-
- return () => {
- return renderAuth();
- };
- },
- });
- script>
指令方式就是自己写个指令,在需要的组件上使用指令就行了。如下:
引用:
- <template>
- <div>
- <AAAAAA v-permission="AAAAAA"/>
- <BBBBBB v-permission="BBBBBB"/>
- <CCCCCC v-permission="CCCCCC"/>
- </div>
- </template>
编写指令:
- import { createApp, reactive, watchEffect } from "vue"
- const codeList = reactive([1, 2, 3])
-
- const hasPermission = (value) => {
- return codeList.includes(value)
- }
-
- app.directive("permission", {
- updated(el, binding) {
- let update = () => {
- let valueNotChange = binding.value === binding.oldValue
- let oldHasPermission = hasPermission(binding.oldValue)
- let newHasPermission = hasPermission(binding.value)
- let permissionNotChange = oldHasPermission === newHasPermission
- if (valueNotChange && permissionNotChange) return
- if (newHasPermission) {
- addEl(el)
- } else {
- removeEl(el)
- }
- };
- if (el._watchEffect) {
- update()
- } else {
- el._watchEffect = watchEffect(() => {
- update()
- })
- }
- },
- })
在绑定元素挂载后调用,使用 permissions 判断当前用户是否存在通过指令插入的按钮需要的权限码,如果不存在,直接移除绑定的元素。
在实际的开发中也会有其他的控制方式,在各种论坛中也看到了,使用装饰器来做的,其实不管怎么做,都是为了抽离业务逻辑,是我们用起来跟简单为目的。