[vue] button permission control in vue:


1. Obtain the permission code

To do permission control, you must need a code, whether it is permission code or role code. Generally, the backend will return it once, and then store it globally. After successful login, get it and save it in the global 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);
        }
    }
})

Two, three button-level permission control methods

[1] Function method

An example of use is as follows:

<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>

In essence, it is through v-if, but through a unified permission judgment method 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;
    }
}

It's very simple. Get the current user's permission code list from the global store, and then judge whether there is a permission code required by the current button. If there are multiple permission codes, you only need to satisfy one of them.

【2】Component method

In addition to using the function method, you can also use the component method. Vue vben admin provides an Authority component. The usage example is as follows:

<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>

Use Authority to wrap the button that requires permission control. The permission code required by the button is passed in through the value attribute. Next, let's look at the implementation of the Authority component.

<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>

The hasPermission method is also used. If the current user has the permission code required by the button, the content of the Authority package will be rendered intact, otherwise nothing will be rendered.

【3】Command method

An example of use is as follows:

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

The implementation is as follows:

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,
};

// register global directive

export function setupPermissionDirective(app) {
    
    
  app.directive('auth', authDirective);
}

Only one mounted hook is defined, which is called after the bound element is mounted. The hasPermission method is still used to determine whether the current user has the permission code required by the button inserted through the command. If not, the bound element is directly removed. .

Obviously, there are two problems in the implementation of permission control. One is that the permission of the button cannot be changed dynamically, and the other is that changing the permission of the current user dynamically will not take effect.
[1] Solving the first problem is very simple, because the above only has the logic of deleting elements, and there is no logic of adding them back, so add an updated hook:

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)
}

The main thing is to save the parent node, otherwise you won’t be able to get the original parent node when you want to add it back, and create a comment node for yourself when you delete it, so that you can know where you were when you want to go back next time.

[2] The reason for the second problem is that the user permission data has been modified, but the re-rendering of the button will not be triggered, so we need to find a way to make it trigger. This can be done using the watchEffect method, which we can pass in the updated hook. The method associates the user permission data with the update method of the button, so that when the user permission data changes, it can automatically trigger the re-rendering of the button:

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()
            })
        }
    },
})

The updated logic in the updated hook is extracted into an update method, and then the first update is executed in watchEffect, so that the responsive data of user permissions can be associated with the update method, and subsequent user permission data changes, which can automatically trigger the update method of the rerun.

Guess you like

Origin blog.csdn.net/weixin_53791978/article/details/132243551