首页 > 其他分享 >记录--按钮级别权限怎么控制

记录--按钮级别权限怎么控制

时间:2023-05-24 18:35:22浏览次数:42  
标签:el return -- value 按钮 hasPermission const 权限

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

最近的面试中有一个面试官问我按钮级别的权限怎么控制,我说直接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);
        }
    }
})

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

函数方式

使用示例如下:

<template>
  <a-button v-if="hasPermission(['20000', '2000010'])" color="error" class="mx-4">
    拥有[20000,2000010]code可见
  </a-button>
</template>

<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组件,使用示例如下:

<template>
  <div>
    <Authority :value="RoleEnum.ADMIN">
      <a-button type="primary" block> 只有admin角色可见 </a-button>
    </Authority>
  </div>
</template>
<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包裹的内容,否则就啥也不渲染。

指令方式

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

<a-button v-auth="'1000'" type="primary" class="mx-4"> 拥有code ['1000']权限可见 </a-button>

实现如下:

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

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

 

标签:el,return,--,value,按钮,hasPermission,const,权限
From: https://www.cnblogs.com/smileZAZ/p/17429184.html

相关文章

  • HOMER docker版本配置优化
     概述HOMER是一款100%开源的针对SIP/VOIP/RTC的抓包工具和监控工具。HOMER是一款强大的、运营商级、可扩展的数据包和事件捕获系统,是基于HEP/EEP协议的VoIP/RTC监控应用程序,并可以使用即时搜索、处理和存储大量的信令、RTC事件、日志和统计信息。之前的文章中,我们介绍过HOME......
  • .Net使用第三方onnx或ModelBuilder轻松接入AI模型
    ML.Net - 开源的跨平台机器学习框架支持CPU/GPU训练轻松简洁的预测代码可扩展其他的机器学习平台跨平台1.使用Visual Studio的Model Builder训练和使用模型Visual Studio默认安装了Model Builder插件,可以很快地进行一些通用模型类型的训练和部署,提高接入机器学......
  • (转)史上人间清醒的芝大毕业演讲:圆满的人生,是从开放式选择走向甜蜜献身 -- 大卫·布鲁
      https://www.bilibili.com/video/BV1ch411c747/?spm_id_from=333.1007.tianma.1-2-2.click&vd_source=e4991eff671e2c8b3ce1f748b6cca451史上人间清醒的芝大毕业演讲:圆满的人生,是从开放式选择走向甜蜜献身大卫·布鲁克斯(DavidBrooks) the need to be careful about......
  • SQL练习帖
    1.组合两个表题目描述:表:Person+-------------+---------+|列名|类型|+-------------+---------+|PersonId|int||FirstName|varchar||LastName|varchar|+-------------+---------+personId是该表的主键列。该表包含一些人的......
  • Vue中this.$nextTick()的理解与使用方法
    https://www.jb51.net/article/238332.htmhttps://blog.csdn.net/lzfengquan/article/details/118933093......
  • 002 Python 学习笔记
    #通过:f"内容{变量}"的格式,实现快速格式化,如下示例print("-----------字符串快速格式化------------")name="sunwugang"age=18print(f"mynameis{name},ageis{age}")print("mynameis{},ageis{}".format(name,age)) ......
  • Java大文件分片上传/多线程上传插件
    ​上传文件的jsp中的部分上传文件同样可以使用form表单向后端发请求,也可以使用ajax向后端发请求    1. 通过form表单向后端发送请求         <formid="postForm" action="${pageContext.request.contextPath}/UploadServlet" method="post" enctype="multip......
  • 课程个人总结
    在这一学期中,开始提出了目标要求:本学期目标 对于这个目标基本完成,对于AndroidStudio项目有所了解,在项目中完成了Android手机端项目的构建。对于项目来说,了解到项目对我们并非太远,项目也是基于数据库的构建,此外关于条件的限制、界面的美化也是构建项目的基本方法。在对于Androi......
  • [JSOI2007]建筑抢修
    [JSOI2007]建筑抢修跟经典题poj1456非常像。首先如果两个都被选入那么截至时间T2小的放前面肯定更优,所以我们先按T2排序。然后逐个遍历建筑,建立一个维修时间为关键字的大根堆,如果前面花费的总时间+维修的时间小于当前的T2,直接加入。否则判断是否小于堆顶,如果小于堆顶则替换,因为......
  • 开发者们:618电商团战即将开启,“抢流量”想上分,必备这三个大招 | MobTech观察
    最卷的一届“618”大促即将来袭。5月23日晚间开始,各平台陆续对外启动预售。在消费领域加速复苏,全国各地迎来“拼经济”热潮的背景下,各平台纷纷对外喊话,将打造“史上投入最大”的一届“618”。电商平台们想借年中大促漂漂亮亮地打一场“翻身仗”,却发现这场仗并不好打。据万得(Wind)数......