使用 vue 自定义指令给按钮添加权限控制并 hover 提示
背景
最近接到一个需求: 停服之后不允许用户操作部分页面的按钮, 禁用并 hover 提示 '已停服, 暂不支持操作'.
打开相关页面一瞅, 对着密密麻麻的按钮陷入了沉思...
重写每个按钮? 封装一个高阶组件? 瞬间产生一系列想法, 但都不可避免的要去对每一个按钮进行改造, 既重复又没有营养.
本着追求优雅(能偷懒就偷懒)的原则, 我想到了 vue 的自定义指令, 简单的只需要一句代码, 简单优雅, 节省不少时间(增加摸鱼时间).
思路
- 使用自定义指令拿到当前
el
, 给当前el
添加一个class
名, 如directive-disabled
, 该class
用来提供禁用样式 - 使用
Vue.extend
构造一个tooltip
子类, 用于 hover 时提示 - 将
tooltip
构造子类插入到之前拿到的el
下,使用绝对定位保持跟el
一样的大小实现 hover 时提示功能
代码如下
- 定义自定义指令, 搭个架子, 这里使用插件的写法
// 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 之前
- 通过
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');
}
},
};
- 使用
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>`,
})
- 将子类插入到
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);
}
},
};
- 最后一步需要将使用原生属性
disabled
将button
置位禁用态, 防止触发点击事件. 全部代码如下:
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;
}
}
总结
一句话: 只要思想不滑坡, 方法总比困难多.
用的都是常规的方法, 实现也不复杂, 相信能想到这一点的都能做到.
保持好奇, 保持思考🤔
要优雅~