页面元素权限控制

按钮权限控制 按钮权限控制的交互方式无非两种:"不可见" 和 "可见不可点"。
不可见
不可见的交互方式相对简单,控制DOM的显示隐藏即可。
【页面元素权限控制】Vue中可以通过下述方式实现:

  • v-if 控制其是否显示
  • v-show控制其是否显示,但不够保险,毕竟 v-show 只是把样式改成 display: none,在真实的 DOM 渲染还是存在
React中可以通过下述方式实现:
  • JSX中条件判断
const Demo = () => { return {boolean && }}

  • 使用高阶组件HOC,抽离判断逻辑
const Demo = () => { return code... }/** 高阶组件 */ const HOC = (Com: Com: React.ComponentType) => { if (whiteList.includes(code)) return return null; }

可见不可点
页面中有很多功能点,如果有些功能点用户不能操作而不显示DOM,可能会导致页面布局错乱,或者影响页面的美观,有些产品在控制功能点权限时期望元素“可见不可点”。
Vue中可以通过下述方式实现:
  • 自定义指令Vue.directive。使用addEventListener给元素增加一个捕获事件,捕获事件会优先于 @click 触发,然后在捕获事件方法体中使用 stopImmediatePropagation 阻止事件冒泡和其它相同事件的触发,因此达到控制元素不可点击的目的
如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,它们会按其被添加的顺序被调用。如果在其中一个事件监听器中执行 stopImmediatePropagation() ,那么剩下的事件监听器都不会被调用。 MSDN - stopImmediatePropagation
// 注册自定义指令脚本 /** 权限拦截 */ const interception = (event) => { event && event.stopImmediatePropagation(); }/** 白名单 */ const whiteList = []; /** 注册指令 */ Vue.directive('permission', { bind(el, binding) { /** 不在白名单中 */ if (!whiteList.includes(binding.value)) { el.style.pointerEvents = 'none'; el.setAttribute('disabled', 'disabled'); el.addEventListener('click', interception, true); } }, unbind(el) { el.removeEventListener('click', interception); } });

这里使用 pointer-events 只是一个辅助功能,并不一定意味着元素上的事件监听器永远不会触发,如果后代元素有指定 pointer-events 并允许成为事件目标的话,是可以触发父元素事件,而且单纯依靠 CSS 属性来控制不点击,还是有风险,因此这里仅作辅助作用。
CSS3pointer-events 属性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件的 target。 更多用法参考: MSDN - pointer-events
React中可以通过下述方式实现:
import { Button } from 'antd'; import axios from 'axios'; import React, { Component } from 'react'; /** 按钮权限控制高阶组件 */ const RbacHOC = (Com: any) => { return class WrappedComponent extends Component { divRef = React.createRef(); componentDidMount() { this.fetchWilteList(); }componentWillUnmount() { this.divRef.current?.removeEventListener('click', this.intercept); }/** 获取黑白名单 */ fetchWilteList = async () => { try { const { code, data } = await axios.get(`${url}`).then((res: any) => res.data); if (code === 200 && !data) { this.register(); } } catch (error) { this.register(); } }; /** 注册捕获事件,拦截事件 */ register = () => { this.divRef.current?.addEventListener('click', this.intercept, true); }; /** 拦截函数 */ intercept = (event: Event) => { event.stopImmediatePropagation(); message.info('你没有权限使用该功能!!!'); }; render() { const { props } = this; return (); } }; }; export default RbacHOC;

页面组件中使用:
import React from 'react'; const Demo = () => { return <>coding... }export default RbacHOC(inject('store')(observer(Demo))); // export default inject('store')(observer(RbacHOC(Demo)); // 不能这么写,因为store改变后WrappedComponent组件不会重新渲染,继而传给Demo组件的props不变,Demo也不会重新渲染

如果不想每次点击触发时都去调用接口拿黑白名单,可以将存放黑白名单数据的store传到高阶组件中
import React from 'react'; const Demo = () => { return <>coding... }export default inject('xxxstore')(RbacHOC(inject('store')(observer(Demo))));

参考 基于 Element 按钮权限实现方案

    推荐阅读