开发心得 - vue 自定义指令的妙用: 权限控制以及 hover 提示文字

235 阅读2分钟

使用 vue 自定义指令给按钮添加权限控制并 hover 提示

背景

最近接到一个需求: 停服之后不允许用户操作部分页面的按钮, 禁用并 hover 提示 '已停服, 暂不支持操作'.

打开相关页面一瞅, 对着密密麻麻的按钮陷入了沉思...

重写每个按钮? 封装一个高阶组件? 瞬间产生一系列想法, 但都不可避免的要去对每一个按钮进行改造, 既重复又没有营养.

本着追求优雅(能偷懒就偷懒)的原则, 我想到了 vue 的自定义指令, 简单的只需要一句代码, 简单优雅, 节省不少时间(增加摸鱼时间).

思路

  1. 使用自定义指令拿到当前 el, 给当前 el添加一个 class 名, 如directive-disabled, 该class用来提供禁用样式
  2. 使用 Vue.extend构造一个tooltip子类, 用于 hover 时提示
  3. tooltip构造子类插入到之前拿到的el下,使用绝对定位保持跟el一样的大小实现 hover 时提示功能

代码如下

  1. 定义自定义指令, 搭个架子, 这里使用插件的写法
// disabledStopService.js
const disabledStopService = {
    inserted: function(el, _binding, vnode) {
    },
};

const install = {
    install: function() {
        Vue.directive('disabledStopService', disabledStopService);
    },
};

export default install;  

// main.js  
Vue.use(disabledStopService);  // 注意: 需放在实例化 Vue 之前
  1. 通过store中定义的getters拿到停服状态, 若停服, 则给当前el添加directive-disabled类名
const disabledStopService = {
    inserted: function(el: HTMLElement, _binding: any, vnode: any) {
        //  使用第三个参数可以拿到 vue 实例上下文
        const getIsStopService = vnode.context.$store.getters.getIsStopService;

        if (getIsStopService) {
           el.classList.add('directive-disabled');
        }
    },
};
  1. 使用vue.extend构造tooltip子类, 提供 hover 时文案提示功能
const TootipCtor = Vue.extend({
  	// 这里使用的是 elementUi tooltip 组件, 需要提前写好类名directive-hidden, 方便后面写样式保持跟之前的 el 一样大小, 不需要内容, 目的是为了浮在 el 上
    template: `<el-tooltip content="当前账号已停服,无法操作" placement="top" >
            <div class="directive-hidden" />
        </el-tooltip>`,
})
  1. 将子类插入到el
const disabledStopService = {
    inserted: function(el, _binding, vnode) {
        const getIsStopService = vnode.context.$store.getters.getIsStopService;

        if (getIsStopService) {
            el.classList.add('directive-disabled');

            const instance = new TootipCtor();
            const instanceEl = instance.$mount().$el;

            el.appendChild(instanceEl);
        }
    },
};

  1. 最后一步需要将使用原生属性disabledbutton置位禁用态, 防止触发点击事件. 全部代码如下:
import Vue from 'vue';

const TootipCtor = Vue.extend({
    template: `<el-tooltip content="当前账号已停服,无法操作" placement="top" >
            <div class="directive-hidden" />
        </el-tooltip>`,
});

const disabledStopService = {
    inserted: function(el, _binding, vnode) {
      	//  使用第三个参数可以拿到 vue 实例上下文
        const getIsStopService = vnode.context.$store.getters.getIsStopService;

        if (getIsStopService) {
            setTimeout(() => {
              	// 这样可以使用 elementUi 默认禁用样式 
              	el.classList.add('is-disabled');
                el.classList.add('directive-disabled');
              	// 防止触发 button 点击事件
                el.setAttribute('disabled', 'disabled');
            });

            const instance = new TootipCtor();
            const instanceEl = instance.$mount().$el;

          	// 增加指令的使用范围, 不局限于 button上, 如 a 标签, icon, div 等. 通过阻止事件冒泡的方法实现阻止事件触发
            instanceEl.addEventListener('click', e => {
                e.stopPropagation();
                e.cancelBubble = true;
            });
            el.appendChild(instanceEl);
        }
    },
};

const install = {
    install: function() {
        Vue.directive('disabledStopService', disabledStopService);
    },
};

export default install;  

样式:

.directive-disabled {
    position: relative;
    cursor: not-allowed;
    opacity: 0.7;
    .directive-hidden {
        position: absolute;
        height: 100%;
        width: 100%;
        left: 0;
        top: 0;
        cursor: not-allowed;
    }
}

总结

一句话: 只要思想不滑坡, 方法总比困难多.

用的都是常规的方法, 实现也不复杂, 相信能想到这一点的都能做到.

保持好奇, 保持思考🤔

要优雅~