按钮权限管制

按钮权限管制的交互方式无非两种:"不可见""可见不可点"

不可见

不可见的交互方式绝对简略,管制DOM的显示暗藏即可。

Vue中能够通过下述形式实现:

  • v-if 管制其是否显示
  • v-show管制其是否显示,但不够保险,毕竟 v-show 只是把款式改成 display: none,在实在的 DOM 渲染还是存在

React中能够通过下述形式实现:

  • JSX中条件判断
const Demo = () => {  return <div>    {boolean && <Button>submit</Button>}  </div>}
  • 应用高阶组件HOC,抽离判断逻辑
const Demo = () => {  return <div>code...</div>}/** 高阶组件 */const HOC = <T>(Com: Com: React.ComponentType<T>) => {  if (whiteList.includes(code)) return <Com {...this.props} />  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<HTMLDivElement>();    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 (        <div style={{ display: 'inline-block' }} ref={this.divRef}>          <Com {...props} />        </div>      );    }  };};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 按钮权限实现计划