Button permission control implementation scheme

Background: Some management systems need to add the button permission control function, and the permission control granularity should be extended to the button level

expected

There are two interactive modes for button permission control: “Invisible” and “invisible”.

invisible

Invisible interactions are relatively simple, and we can use V-if to control whether they are displayed or not. Using V-show is fine, but it’s not safe. After all, v-show just changes the style to display: None. It still exists in real DOM rendering, so v-if is more recommended to control invisibility.

Invisible point

“Yes, I can, but you can’t.”

  • Style control (add a disable style), such as cursor: not-allowed, place a grey, etc.;
  • Do not click on that to disable or screen click event, like has the preventDefault/stopProgration can be realized;

The “invisible point” was chosen for the final product requirement, probably because invisible was too simple. (such _)

Train of thought to explore

  • To button click event callback function, add a wrapper function, control its permissions, event interception and triggering. It’s a proxy, a bit of a higher-order component (but too much change to the existing business, so you have to change each @click binding function one by one and abandon the solution);
  • Stop button click event bubbling and trigger, seemingly can preventDefautl/stopProgration, can feel the directive approach to event listeners of DOM elements, allows it to make the normal execution events, are not allowed to intercept shielding;

practices

Finally, the instruction method is chosen, which minimizes cost expansion and avoids changing existing business code logic. Click hijacking elements for permission control:

  • el-button
  • Btn-wrapper (self-wrapping component)
  • Div/span/a label

Concrete implementation:

Permission entry: Vuex control, global use

// After a user logs in, obtain the user permission CODE and store it in store
this.$store.commit('SET_AUTH_CODE', authCodeList);

SET_AUTH_CODE: (state, acthCode) = > {
  if (acthCode) {
      state.autoCodeList = acthCode;
  }
  setStore({
    name: 'autoCodeList'.content: state.autoCodeList || [],
  });
}
Copy the code

Define permission directive

const disableClickFn = (event) = > {
    event && event.stopImmediatePropagation();
}

export const hasPermission = () = > {
    Vue.directive('permission', {
        bind(el, binding) {
            let disalbe = true;
            if (autoCodeList.length && autoCodeList.includes(binding.value)) {
                disable = false;
            }

            if (disable) {
                el.classList.add('permission-disabled');
                el.setAttribute('disabled'.'disabled');
                el.addEventListener('click', disableClickFn, true); }},unbind(el) {
            el.removeEventListener('click', disableClickFn); }}); }Copy the code
  • First, the third addEventListener argument we use useCapture to be true to fire during the capture phase, so the event listener here will fire the callback before @click;
  • Secondly, stopImmediatePropagation was used to prevent event bubbles and other similar event listeners from being triggered.

If multiple event listeners are attached to the same event type of the same element, when the event is triggered, they are called in the order in which they were added. If you execute stopImmediatePropagation() in one of the event listeners, none of the remaining event listeners will be called.

Add disabled CSS styles

.permission-disabled {
    position: relative;
    cursor: not-allowed ! important;
    pointer-events: none; // Prevents the element from becoming a mouse eventborder:none;
    background-image: none;
    &::after {
        content: ' ';
        position: absolute;
        bottom: 0;
        left: 0px;
        right: 0px;
        height: 100%;
        z-index: 9;
        background: rgba(255.255.255.0.5); }}Copy the code

The pointers-Events attribute of CSS3 specifies under what circumstances, if any, a particular graphic element can be the target of mouse events.

The use of pointer-events is a secondary function. It does not necessarily mean that event listeners on elements will never fire. If descendant elements have pointer-events specified and are allowed to be the target of events, parent elements can fire. There’s still a risk, so this is just a side effect.

Global “permission judgment” utility functions

import { getStore } from '@/util/store';
const autoCodeList = getStore({ name: 'autoCodeList', }) || [];

export function hasPermission(authName) {
    return! (autoCodeList.length >0 && autoCodeList.includes(authName));
}
Copy the code

The specific use

Sys /auth/save = sys/auth/save = sys/auth
<el-button v-permission="'sys:auth:save'"> save < / el - button >// Function mode
<el-button :disabled="hasPermission('sys:auth:save')"></el-button>
Copy the code