最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

全球实时:记录--按钮级别权限怎么控制

来源:博客园

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

最近的面试中有一个面试官问我按钮级别的权限怎么控制,我说直接v-if啊,他说不够好,我说我们项目中按钮级别的权限控制情况不多,所以v-if就够了,他说不够通用,最后他对我的评价是做过很多东西,但是都不够深入,好吧,那今天我们就来深入深入。

因为我自己没有相关实践,所以接下来就从这个有16.2k星星的后台管理系统项目Vue vben admin中看看它是如何做的。

获取权限码

要做权限控制,肯定需要一个code,无论是权限码还是角色码都可以,一般后端会一次性返回,然后全局存储起来就可以了,Vue vben admin是在登录成功以后获取并保存到全局的store中:


(相关资料图)

import { defineStore } from "pinia";export const usePermissionStore = defineStore({    state: () => ({        // 权限代码列表        permCodeList: [],    }),    getters: {        // 获取        getPermCodeList(){      return this.permCodeList;    },    },    actions: {        // 存储        setPermCodeList(codeList) {            this.permCodeList = codeList;        },        // 请求权限码        async changePermissionCode() {            const codeList = await getPermCode();            this.setPermCodeList(codeList);        }    }})

接下来它提供了三种按钮级别的权限控制方式,一一来看。

函数方式

使用示例如下:

<script lang="ts">  import { usePermission } from "/@/hooks/web/usePermission";  export default defineComponent({    setup() {      const { hasPermission } = usePermission();      return { hasPermission };    },  });</script>

本质上就是通过v-if,只不过是通过一个统一的权限判断方法hasPermission

export function usePermission() {    function hasPermission(value, def = true) {        // 默认视为有权限        if (!value) {            return def;        }        const allCodeList = permissionStore.getPermCodeList;        if (!isArray(value)) {            return allCodeList.includes(value);        }        // intersection是lodash提供的一个方法,用于返回一个所有给定数组都存在的元素组成的数组        return (intersection(value, allCodeList)).length > 0;        return true;    }}

很简单,从全局store中获取当前用户的权限码列表,然后判断其中是否存在当前按钮需要的权限码,如果有多个权限码,只要满足其中一个就可以。

组件方式

除了通过函数方式使用,也可以使用组件方式,Vue vben admin提供了一个Authority组件,使用示例如下:

<script>  import { Authority } from "/@/components/Authority";  import { defineComponent } from "vue";  export default defineComponent({    components: { Authority },  });</script>

使用Authority包裹需要权限控制的按钮即可,该按钮需要的权限码通过value属性传入,接下来看看Authority组件的实现。

<script lang="ts">  import { defineComponent } from "vue";  import { usePermission } from "/@/hooks/web/usePermission";  import { getSlot } from "/@/utils/helper/tsxHelper";  export default defineComponent({    name: "Authority",    props: {      value: {        type: [Number, Array, String],        default: "",      },    },    setup(props, { slots }) {      const { hasPermission } = usePermission();      function renderAuth() {        const { value } = props;        if (!value) {          return getSlot(slots);        }        return hasPermission(value) ? getSlot(slots) : null;      }      return () => {        return renderAuth();      };    },  });</script>

同样还是使用hasPermission方法,如果当前用户存在按钮需要的权限码时就原封不动渲染Authority包裹的内容,否则就啥也不渲染。

指令方式

最后一种就是指令方式,使用示例如下:

 拥有code ["1000"]权限可见 

实现如下:

import { usePermission } from "/@/hooks/web/usePermission";function isAuth(el, binding) {  const { hasPermission } = usePermission();  const value = binding.value;  if (!value) return;  if (!hasPermission(value)) {    el.parentNode?.removeChild(el);  }}const mounted = (el, binding) => {  isAuth(el, binding);};const authDirective = {  // 在绑定元素的父组件  // 及他自己的所有子节点都挂载完成后调用  mounted,};// 注册全局指令export function setupPermissionDirective(app) {  app.directive("auth", authDirective);}

只定义了一个mounted钩子,也就是在绑定元素挂载后调用,依旧是使用hasPermission方法,判断当前用户是否存在通过指令插入的按钮需要的权限码,如果不存在,直接移除绑定的元素。

很明显,Vue vben admin的实现有两个问题,一是不能动态更改按钮的权限,二是动态更改当前用户的权限也不会生效。

解决第一个问题很简单,因为上述只有删除元素的逻辑,没有加回来的逻辑,那么增加一个updated钩子:

app.directive("auth", {    mounted: (el, binding) => {        const value = binding.value        if (!value) return        if (!hasPermission(value)) {            // 挂载的时候没有权限把元素删除            removeEl(el)        }    },    updated(el, binding) {        // 按钮权限码没有变化,不做处理        if (binding.value === binding.oldValue) return        // 判断用户本次和上次权限状态是否一样,一样也不用做处理        let oldHasPermission = hasPermission(binding.oldValue)        let newHasPermission = hasPermission(binding.value)        if (oldHasPermission === newHasPermission) return        // 如果变成有权限,那么把元素添加回来        if (newHasPermission) {            addEl(el)        } else {        // 如果变成没有权限,则把元素删除            removeEl(el)        }    },})const hasPermission = (value) => {    return [1, 2, 3].includes(value)}const removeEl = (el) => {    // 在绑定元素上存储父级元素    el._parentNode = el.parentNode    // 在绑定元素上存储一个注释节点    el._placeholderNode = document.createComment("auth")    // 使用注释节点来占位    el.parentNode?.replaceChild(el._placeholderNode, el)}const addEl = (el) => {    // 替换掉给自己占位的注释节点    el._parentNode?.replaceChild(el, el._placeholderNode)}

主要就是要把父节点保存起来,不然想再添加回去的时候获取不到原来的父节点,另外删除的时候创建一个注释节点给自己占位,这样下次想要回去能知道自己原来在哪。

第二个问题的原因是修改了用户权限数据,但是不会触发按钮的重新渲染,那么我们就需要想办法能让它触发,这个可以使用watchEffect方法,我们可以在updated钩子里通过这个方法将用户权限数据和按钮的更新方法关联起来,这样当用户权限数据改变了,可以自动触发按钮的重新渲染:

import { createApp, reactive, watchEffect } from "vue"const codeList = reactive([1, 2, 3])const hasPermission = (value) => {    return codeList.includes(value)}app.directive("auth", {    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()            })        }    },})

updated钩子里更新的逻辑提取成一个update方法,然后第一次更新在watchEffect中执行,这样用户权限的响应式数据就可以和update方法关联起来,后续用户权限数据改变了,可以自动触发update方法的重新运行。

本文转载于:

https://juejin.cn/post/7209648356530896953

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

关键词: