当前位置:   article > 正文

按钮级别权限多种控制方法实现_巧妙的 分权分域 按钮 权限

巧妙的 分权分域 按钮 权限

获取权限码

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

  1. import { defineStore } from 'pinia';
  2. export const usePermissionStore = defineStore({
  3. state: () => ({
  4. // 权限代码列表
  5. permCodeList: [],
  6. }),
  7. getters: {
  8. // 获取
  9. getPermCodeList(){
  10. return this.permCodeList;
  11. },
  12. },
  13. actions: {
  14. // 存储
  15. setPermCodeList(codeList) {
  16. this.permCodeList = codeList;
  17. },
  18. // 请求权限码
  19. async changePermissionCode() {
  20. const codeList = await getPermCode();
  21. this.setPermCodeList(codeList);
  22. }
  23. }
  24. })
  25. 复制代码

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

函数方式

使用示例如下:

  1. <template>
  2. <a-button v-if="hasPermission(['20000', '2000010'])" color="error" class="mx-4">
  3. 拥有[20000,2000010]code可见
  4. </a-button>
  5. </template>
  6. <script lang="ts">
  7. import { usePermission } from '/@/hooks/web/usePermission';
  8. export default defineComponent({
  9. setup() {
  10. const { hasPermission } = usePermission();
  11. return { hasPermission };
  12. },
  13. });
  14. </script>
  15. 复制代码

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

  1. export function usePermission() {
  2. function hasPermission(value, def = true) {
  3. // 默认视为有权限
  4. if (!value) {
  5. return def;
  6. }
  7. const allCodeList = permissionStore.getPermCodeList;
  8. if (!isArray(value)) {
  9. return allCodeList.includes(value);
  10. }
  11. // intersection是lodash提供的一个方法,用于返回一个所有给定数组都存在的元素组成的数组
  12. return (intersection(value, allCodeList)).length > 0;
  13. return true;
  14. }
  15. }
  16. 复制代码

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

组件方式

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

  1. <template>
  2. <div>
  3. <Authority :value="RoleEnum.ADMIN">
  4. <a-button type="primary" block> 只有admin角色可见 </a-button>
  5. </Authority>
  6. </div>
  7. </template>
  8. <script>
  9. import { Authority } from '/@/components/Authority';
  10. import { defineComponent } from 'vue';
  11. export default defineComponent({
  12. components: { Authority },
  13. });
  14. </script>
  15. 复制代码

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

  1. <script lang="ts">
  2. import { defineComponent } from 'vue';
  3. import { usePermission } from '/@/hooks/web/usePermission';
  4. import { getSlot } from '/@/utils/helper/tsxHelper';
  5. export default defineComponent({
  6. name: 'Authority',
  7. props: {
  8. value: {
  9. type: [Number, Array, String],
  10. default: '',
  11. },
  12. },
  13. setup(props, { slots }) {
  14. const { hasPermission } = usePermission();
  15. function renderAuth() {
  16. const { value } = props;
  17. if (!value) {
  18. return getSlot(slots);
  19. }
  20. return hasPermission(value) ? getSlot(slots) : null;
  21. }
  22. return () => {
  23. return renderAuth();
  24. };
  25. },
  26. });
  27. </script>
  28. 复制代码

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

指令方式

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

  1. <a-button v-auth="'1000'" type="primary" class="mx-4"> 拥有code ['1000']权限可见 </a-button>
  2. 复制代码

实现如下:

  1. import { usePermission } from '/@/hooks/web/usePermission';
  2. function isAuth(el, binding) {
  3. const { hasPermission } = usePermission();
  4. const value = binding.value;
  5. if (!value) return;
  6. if (!hasPermission(value)) {
  7. el.parentNode?.removeChild(el);
  8. }
  9. }
  10. const mounted = (el, binding) => {
  11. isAuth(el, binding);
  12. };
  13. const authDirective = {
  14. // 在绑定元素的父组件
  15. // 及他自己的所有子节点都挂载完成后调用
  16. mounted,
  17. };
  18. // 注册全局指令
  19. export function setupPermissionDirective(app) {
  20. app.directive('auth', authDirective);
  21. }
  22. 复制代码

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

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

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

  1. app.directive("auth", {
  2. mounted: (el, binding) => {
  3. const value = binding.value
  4. if (!value) return
  5. if (!hasPermission(value)) {
  6. // 挂载的时候没有权限把元素删除
  7. removeEl(el)
  8. }
  9. },
  10. updated(el, binding) {
  11. // 按钮权限码没有变化,不做处理
  12. if (binding.value === binding.oldValue) return
  13. // 判断用户本次和上次权限状态是否一样,一样也不用做处理
  14. let oldHasPermission = hasPermission(binding.oldValue)
  15. let newHasPermission = hasPermission(binding.value)
  16. if (oldHasPermission === newHasPermission) return
  17. // 如果变成有权限,那么把元素添加回来
  18. if (newHasPermission) {
  19. addEl(el)
  20. } else {
  21. // 如果变成没有权限,则把元素删除
  22. removeEl(el)
  23. }
  24. },
  25. })
  26. const hasPermission = (value) => {
  27. return [1, 2, 3].includes(value)
  28. }
  29. const removeEl = (el) => {
  30. // 在绑定元素上存储父级元素
  31. el._parentNode = el.parentNode
  32. // 在绑定元素上存储一个注释节点
  33. el._placeholderNode = document.createComment("auth")
  34. // 使用注释节点来占位
  35. el.parentNode?.replaceChild(el._placeholderNode, el)
  36. }
  37. const addEl = (el) => {
  38. // 替换掉给自己占位的注释节点
  39. el._parentNode?.replaceChild(el, el._placeholderNode)
  40. }
  41. 复制代码

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

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

  1. import { createApp, reactive, watchEffect } from "vue"
  2. const codeList = reactive([1, 2, 3])
  3. const hasPermission = (value) => {
  4. return codeList.includes(value)
  5. }
  6. app.directive("auth", {
  7. updated(el, binding) {
  8. let update = () => {
  9. let valueNotChange = binding.value === binding.oldValue
  10. let oldHasPermission = hasPermission(binding.oldValue)
  11. let newHasPermission = hasPermission(binding.value)
  12. let permissionNotChange = oldHasPermission === newHasPermission
  13. if (valueNotChange && permissionNotChange) return
  14. if (newHasPermission) {
  15. addEl(el)
  16. } else {
  17. removeEl(el)
  18. }
  19. };
  20. if (el._watchEffect) {
  21. update()
  22. } else {
  23. el._watchEffect = watchEffect(() => {
  24. update()
  25. })
  26. }
  27. },
  28. })
  29. 复制代码

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/774103
推荐阅读
相关标签
  

闽ICP备14008679号