前端B端权限控制

发表于 3年以前  | 总阅读数:264 次

后台管理平台内部权限大部分涉及到到两种方式:资源权限 & 数据权限

demo分支:https://github.com/rodchen-king/ant-design-pro-v2/tree/permission-branch

1 . 基本介绍

  • 资源权限:菜单导航栏 & 页面 & 按钮 资源可见权限。
  • 数据权限:对于页面上的数据操作,同一个人同一个页面相同的数据可能存在不同的数据操作权限。

权限纬度

  • 角色维度:大部分的情况为:用户 => 角色 => 权限
  • 用户维度:用户 => 权限

表现形式

  • 基础表现形式还是树结构的展现形式,因为对应的菜单-页面-按钮是一个树的从主干到节点的数据流向。

2 . 权限数据录入与展示

采用树结构进行处理。唯一需要处理的是父子节点的联动关系处理。这里因为不同的公司或者系统可能对于这部分的数据录入方式不同,所以就不贴图了。

3 . 权限数据控制

3.1 用户资源权限流程图

3.2 前端权限控制

前端控制权限也是分为两部分,菜单页面 与 按钮。因为前端权限控制的实现,会因为后台接口形式有所影响,但是大体方向是相同。还是会分为这两块内容。这里对于权限是使用多接口查询权限,初始登录查询页面权限,点击业务页面,查询对应业务页面的资源code。

3.2.1 菜单权限

菜单权限控制需要了解两个概念:

  • 一个是可见的菜单页面 :左侧dom节点
  • 一个是可访问的菜单页面:系统当中路由这一块

这里说的意思是:我们所说的菜单权限控制,大多只是停留在菜单是否可见,但是系统路由的页面可见和页面上的菜单是否可见是两回事情。假设系统路由/path1可见,尽管页面上的没有/path1对应的菜单显示。我们直接在浏览器输入对应的path1,还是可以访问到对应的页面。这是因为系统路由那一块其实我们是没有去处理的。

了解了这个之后,我们需要做菜单页面权限的时候就需要去考虑两块,并且是对应的。

3.2.1.1 路由权限

代码地址: https://github.com/rodchen-king/ant-design-pro-v2/commit/0e7895c56e4962d75ab8ccf4637cefca3f5f71b6#diff-a7acc04e8fb20252554c588f7b7a8564

这里是有两种做法:第一种,控制路由的配置,当然不是路由配置文件里去配置。而是生效的路由配置里去做。第二种,完全不做这里的路由控制,而是在路由跳转到没有权限的页面,写逻辑校验是否有当前的权限,然后手动跳转到403页面。

这里还是先用第一种做法来做:因为这里用第一种做了之后,菜单可见权限自动适配好了。会省去我们很多事情。

a. 路由文件,定义菜单页面权限。并且将exception以及404的路由添加notInAut标志,这个标志说明:这两个路由不走权限校验。同理的还有 /user。

export default [
  // user
  {
    path: '/user',
    component: '../layouts/UserLayout',
    routes: [
      { path: '/user', redirect: '/user/login' },
      { path: '/user/login', component: './User/Login' },
      { path: '/user/register', component: './User/Register' },
      { path: '/user/register-result', component: './User/RegisterResult' },
    ],
  },
  // app
  {
    path: '/',
    component: '../layouts/BasicLayout',
    Routes: ['src/pages/Authorized'],
    authority: ['admin', 'user'],
    routes: [
      // dashboard
      { path: '/', redirect: '/list/table-list' },
      // forms
      {
        path: '/form',
        icon: 'form',
        name: 'form',
        code: 'form_menu',
        routes: [
          {
            path: '/form/basic-form',
            code: 'form_basicForm_page',
            name: 'basicform',
            component: './Forms/BasicForm',
          },
        ],
      },
      // list
      {
        path: '/list',
        icon: 'table',
        name: 'list',
        code: 'list_menu',
        routes: [
          {
            path: '/list/table-list',
            name: 'searchtable',
            code: 'list_tableList_page',
            component: './List/TableList',
          },
        ],
      },
      {
        path: '/profile',
        name: 'profile',
        icon: 'profile',
        code: 'profile_menu',
        routes: [
          // profile
          {
            path: '/profile/basic',
            name: 'basic',
            code: 'profile_basic_page',
            component: './Profile/BasicProfile',
          },
          {
            path: '/profile/advanced',
            name: 'advanced',
            code: 'profile_advanced_page',
            authority: ['admin'],
            component: './Profile/AdvancedProfile',
          },
        ],
      },
      {
        name: 'exception',
        icon: 'warning',
        notInAut: true,
        hideInMenu: true,
        path: '/exception',
        routes: [
          // exception
          {
            path: '/exception/403',
            name: 'not-permission',
            component: './Exception/403',
          },
          {
            path: '/exception/404',
            name: 'not-find',
            component: './Exception/404',
          },
          {
            path: '/exception/500',
            name: 'server-error',
            component: './Exception/500',
          },
          {
            path: '/exception/trigger',
            name: 'trigger',
            hideInMenu: true,
            component: './Exception/TriggerException',
          },
        ],
      },
      {
        notInAut: true,
        component: '404',
      },
    ],
  },
];

b. 修改app.js 文件,加载路由

export const dva = {
  config: {
    onError(err) {
      err.preventDefault();
    },
  },
};

let authRoutes = null;

function ergodicRoutes(routes, authKey, authority) {
  routes.forEach(element => {
    if (element.path === authKey) {
      Object.assign(element.authority, authority || []);
    } else if (element.routes) {
      ergodicRoutes(element.routes, authKey, authority);
    }
    return element;
  });
}

function customerErgodicRoutes(routes) {
  const menuAutArray = (localStorage.getItem('routerAutArray') || '').split(',');

  routes.forEach(element => {
    // 没有path的情况下不需要走逻辑检查
    // path 为 /user 不需要走逻辑检查
    if (element.path === '/user' || !element.path) {
      return element;
    }

    // notInAut 为true的情况下不需要走逻辑检查
    if (!element.notInAut) {
      if (menuAutArray.indexOf(element.code) >= 0 || element.path === '/') {
        if (element.routes) {
          // eslint-disable-next-line no-param-reassign
          element.routes = customerErgodicRoutes(element.routes);

          // eslint-disable-next-line no-param-reassign
          element.routes = element.routes.filter(item => !item.isNeedDelete);
        }
      } else {
        // eslint-disable-next-line no-param-reassign
        element.isNeedDelete = true;
      }
    }

    /**
     * 后台接口返回子节点的情况,父节点需要溯源处理
     */
    // notInAut 为true的情况下不需要走逻辑检查
    // if (!element.notInAut) {
    //   if (element.routes) {
    //     // eslint-disable-next-line no-param-reassign
    //     element.routes = customerErgodicRoutes(element.routes);

    //     // eslint-disable-next-line no-param-reassign
    //     if (element.routes.filter(item => item.isNeedSave && !item.hideInMenu).length) {
    //       // eslint-disable-next-line no-param-reassign
    //       element.routes = element.routes.filter(item => item.isNeedSave);
    //       if (element.routes.length) {
    //         // eslint-disable-next-line no-param-reassign
    //         element.isNeedSave = true;
    //       }
    //     }
    //   } else if (menuAutArray.indexOf(element.code) >= 0) {
    //     // eslint-disable-next-line no-param-reassign
    //     element.isNeedSave = true;
    //   }
    // } else {
    //   // eslint-disable-next-line no-param-reassign
    //   element.isNeedSave = true;
    // }

    return element;
  });

  return routes;
}

export function patchRoutes(routes) {
  Object.keys(authRoutes).map(authKey =>
    ergodicRoutes(routes, authKey, authRoutes[authKey].authority),
  );

  customerErgodicRoutes(routes);

  /**
   * 后台接口返回子节点的情况,父节点需要溯源处理
   */
  window.g_routes = routes.filter(item => !item.isNeedDelete);

  /**
   * 后台接口返回子节点的情况,父节点需要溯源处理
   */
  // window.g_routes = routes.filter(item => item.isNeedSave);
}

export function render(oldRender) {
  authRoutes = '';
  oldRender();
}

c. 修改login.js,获取路由当中的code便利获取到,进行查询权限

import { routerRedux } from 'dva/router';
import { stringify } from 'qs';
import { fakeAccountLogin, getFakeCaptcha } from '@/services/api';
import { getAuthorityMenu } from '@/services/authority';
import { setAuthority } from '@/utils/authority';
import { getPageQuery } from '@/utils/utils';
import { reloadAuthorized } from '@/utils/Authorized';
import routes from '../../config/router.config';

export default {
  namespace: 'login',

  state: {
    status: undefined,
  },

  effects: {
    *login({ payload }, { call, put }) {
      const response = yield call(fakeAccountLogin, payload);
      yield put({
        type: 'changeLoginStatus',
        payload: response,
      });
      // Login successfully
      if (response.status === 'ok') {
        // 这里的数据通过接口返回菜单页面的权限是什么

        const codeArray = [];
        // eslint-disable-next-line no-inner-declarations
        function ergodicRoutes(routesParam) {
          routesParam.forEach(element => {
            if (element.code) {
              codeArray.push(element.code);
            }
            if (element.routes) {
              ergodicRoutes(element.routes);
            }
          });
        }

        ergodicRoutes(routes);
        const authMenuArray = yield call(getAuthorityMenu, codeArray.join(','));
        localStorage.setItem('routerAutArray', authMenuArray.join(','));

        reloadAuthorized();
        const urlParams = new URL(window.location.href);
        const params = getPageQuery();
        let { redirect } = params;
        if (redirect) {
          const redirectUrlParams = new URL(redirect);
          if (redirectUrlParams.origin === urlParams.origin) {
            redirect = redirect.substr(urlParams.origin.length);
            if (redirect.match(/^\/.*#/)) {
              redirect = redirect.substr(redirect.indexOf('#') + 1);
            }
          } else {
            window.location.href = redirect;
            return;
          }
        }
        // yield put(routerRedux.replace(redirect || '/'));

        // 这里之所以用页面跳转,因为路由的重新设置需要页面重新刷新才可以生效
        window.location.href = redirect || '/';
      }
    },

    *getCaptcha({ payload }, { call }) {
      yield call(getFakeCaptcha, payload);
    },

    *logout(_, { put }) {
      yield put({
        type: 'changeLoginStatus',
        payload: {
          status: false,
          currentAuthority: 'guest',
        },
      });
      reloadAuthorized();
      yield put(
        routerRedux.push({
          pathname: '/user/login',
          search: stringify({
            redirect: window.location.href,
          }),
        }),
      );
    },
  },

  reducers: {
    changeLoginStatus(state, { payload }) {
      setAuthority(payload.currentAuthority);
      return {
        ...state,
        status: payload.status,
        type: payload.type,
      };
    },
  },
};

d. 添加service


import request from '@/utils/request';

// 查询菜单权限
export async function getAuthorityMenu(codes) {
  return request(`/api/authority/menu?resCodes=${codes}`);
}

// 查询页面按钮权限
export async function getAuthority(params) {
  return request(`/api/authority?codes=${params}`);
}

3.2.1.2 菜单可见权限

参照上面的方式,这里的菜单可见权限不用做其他的操作。

3.2.2 按钮权限

代码地址: https://github.com/rodchen-king/ant-design-pro-v2/commit/0e7895c56e4962d75ab8ccf4637cefca3f5f71b6#diff-a7acc04e8fb20252554c588f7b7a8564

按钮权限上就涉及到两块,资源权限数据权限。数据获取的方式不同,代码逻辑上会稍微有点不同。核心是业务组件内部的code,在加载的时候就自行累加,然后在页面加载完成的时候,发送请求。拿到数据之后,自行进行权限校验。尽量减少业务页面代码的复杂度。

资源权限逻辑介绍:

  1. PageHeaderWrapper包含的业务页面存在按钮权限
  2. 按钮权限通过AuthorizedButton包含处理,需要添加code。但是业务页面因为是单独页面发送当前页面code集合去查询权限code,然后在AuthorizedButton进行权限逻辑判断。
  3. 所以AuthorizedButtoncomponentWillMount生命周期进行当前业务页面的code累加。累加完成之后,通过PageHeaderWrappercomponentDidMount生命周期函数发送权限请求,拿到权限code,通过公有globalAuthority model读取数据进行权限逻辑判断。
  4. 对于业务页面的调用参考readme进行使用。因为对于弹出框内部的code,在业务列表页面渲染的时候,组件还未加载,所以通过extencode提前将code累加起来进行查询权限。

数据权限介绍:

  1. 涉及数据权限,则直接将对应的数据规则放进AuthorizedButton内部进行判断,需要传入的数据则直接通过props传入即可。因为数据权限的规则不同,这里就没有举例子。
  2. 需要注意的逻辑是资源权限和数据权限是串行的,先判断资源权限,然后判断数据权限。

a. 添加公用authority model


/* eslint-disable no-unused-vars */
/* eslint-disable no-prototype-builtins */
import { getAuthority } from '@/services/authority';

export default {
  namespace: 'globalAuthority',

  state: {
    hasAuthorityCodeArray: [], // 获取当前具有权限的资源code
    pageCodeArray: [], // 用来存储当前页面存在的资源code
  },

  effects: {
    /**
     * 获取当前页面的权限控制
     */
    *getAuthorityForPage({ payload }, { put, call, select }) {
      // 这里的资源code都是自己加载的
      const pageCodeArray = yield select(state => state.globalAuthority.pageCodeArray);
      const response = yield call(getAuthority, pageCodeArray);

      if (pageCodeArray.length) {
        yield put({
          type: 'save',
          payload: {
            hasAuthorityCodeArray: response,
          },
        });
      }
    },

    *plusCode({ payload }, { put, select }) {
      // 组件累加当前页面的code,用来发送请求返回对应的权限code
      const { codeArray = [] } = payload;
      const pageCodeArray = yield select(state => state.globalAuthority.pageCodeArray);

      yield put({
        type: 'save',
        payload: {
          pageCodeArray: pageCodeArray.concat(codeArray),
        },
      });
    },

    // eslint-disable-next-line no-unused-vars
    *resetAuthorityForPage({ payload }, { put, call }) {
      yield put({
        type: 'save',
        payload: {
          hasAuthorityCodeArray: [],
          pageCodeArray: [],
        },
      });
    },
  },

  reducers: {
    save(state, { payload }) {
      return {
        ...state,
        ...payload,
      };
    },
  },
};

b. 修改PageHeaderWrapper文件【因为所有的业务页面都是这个组件的子节点】

import React, { PureComponent } from 'react';
import { FormattedMessage } from 'umi/locale';
import Link from 'umi/link';
import PageHeader from '@/components/PageHeader';
import { connect } from 'dva';
import MenuContext from '@/layouts/MenuContext';
import { Spin } from 'antd';
import GridContent from './GridContent';
import styles from './index.less';

class PageHeaderWrapper extends PureComponent {
  componentDidMount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'globalAuthority/getAuthorityForPage', // 发送请求获取当前页面的权限code
    });
  }

  componentWillUnmount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'globalAuthority/resetAuthorityForPage',
    });
  }

  render() {
    const { children, contentWidth, wrapperClassName, top, loading, ...restProps } = this.props;

    return (
      <Spin spinning={loading}>
        <div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}>
          {top}
          <MenuContext.Consumer>
            {value => (
              <PageHeader
                wide={contentWidth === 'Fixed'}
                home={<FormattedMessage id="menu.home" defaultMessage="Home" />}
                {...value}
                key="pageheader"
                {...restProps}
                linkElement={Link}
                itemRender={item => {
                  if (item.locale) {
                    return <FormattedMessage id={item.locale} defaultMessage={item.title} />;
                  }
                  return item.title;
                }}
              />
            )}
          </MenuContext.Consumer>
          {children ? (
            <div className={styles.content}>
              <GridContent>{children}</GridContent>
            </div>
          ) : null}
        </div>
      </Spin>
    );
  }
}

export default connect(({ setting, globalAuthority, loading }) => ({
  contentWidth: setting.contentWidth,
  globalAuthority,
  loading: loading.models.globalAuthority,
}))(PageHeaderWrapper);

c. 添加AuthorizedButton公共组件

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'dva';

@connect(({ globalAuthority }) => ({
  globalAuthority,
}))
class AuthorizedButton extends Component {
  static contextTypes = {
    isMobile: PropTypes.bool,
  };

  componentWillMount() {
    // extendcode 扩展表格中的code还没有出现的情况
    const {
      dispatch,
      code,
      extendCode = [],
      globalAuthority: { pageCodeArray },
    } = this.props;

    let codeArray = [];

    if (code) {
      codeArray.push(code);
    }

    if (extendCode && extendCode.length) {
      codeArray = codeArray.concat(extendCode);
    }

    // code已经存在,证明是页面数据渲染之后或者弹出框的按钮资源,不需要走dva了
    if (pageCodeArray.indexOf(code) >= 0) {
      return;
    }

    dispatch({
      type: 'globalAuthority/plusCode',
      payload: {
        codeArray,
      },
    });
  }

  checkAuthority = code => {
    const {
      globalAuthority: { hasAuthorityCodeArray },
    } = this.props;

    return hasAuthorityCodeArray.indexOf(code) >= 0; // 资源权限
  };

  render() {
    const { children, code } = this.props;

    return (
      <span style={{ display: this.checkAuthority(code) ? 'inline' : 'none' }}>{children}</span>
    );
  }
}

export default AuthorizedButton;

d. 添加AuthorizedButton readme文件

https://github.com/rodchen-king/ant-design-pro-v2/blob/permission-branch/src/components/AuthorizedButton/index.md

3.2.3 按钮权限扩展-链接权限控制

代码地址: https://github.com/rodchen-king/ant-design-pro-v2/commit/02914330f17f11f3d6e8b7d5c1239702c6832337

背景:页面上有需要控制跳转链接的权限,有权限则可以跳转,没有权限则不能跳转。

a.公共model添加新的state:codeAuthorityObject

通过redux-devtool,查看到codeAuthorityObject的状态值为:key:code值,value的值为true/false。true代表,有权限,false代表无权限。主要用于开发人员自己做相关处理。

b.需要控制的按钮code,通过其他方式扩展进行code计算,发送请求获取权限

c.获取数据进行数据控制

3.2.4 按钮数据权限

代码地址:https://github.com/rodchen-king/ant-design-pro-v2/commit/463514b0964c4c0187a503d315aa9f088e963f71

背景

数据权限是对于业务组件内部表格组件的数据进行的数据操作权限。列表数据可能归属于不同的数据类型,所以具有不同的数据操作权限。对于批量操作则需要判断选择的数据是否都具有操作权限,然后显示是否可以批量操作,如果有一个没有操作权限,都不能进行操作。

总体思路

场景:比如在商品列表中,每条商品记录后面的“操作”一栏下用三个按钮:【编辑】、【上架/下架】、【删除】,而对于某一个用户,他可以查看所有的商品,但对于某些品牌他可以【上架/下架】但不能【编辑】,则前端需要控制到每一个商品后面的按钮的可用状态,比如用户A对于某一条业务数据(id=1999)有编辑权限,则这条记录上的【编辑】按钮对他来说是可见的(前提是他首先要有【编辑】这个按钮的资源权限),但对于另一条记录(id=1899)是没有【编辑】权限,则这条记录上的【编辑】按钮对他来说是不可见的。

按钮【actType】属性定义

每个数据操作的按钮上加一个属性 “actType”代表这个按钮的动作类型(如:编辑、删除、审核等),这个属性是资源权限的接口返回的,前端在调这个接口时将这个属性记录下来,或者保存到对应的控件中。所以前端可以不用关于这个属性的每个枚举值代表的是什么含义,只需根据接口的返回值赋值就好。用兴趣的同学也可以参考一下actType取值如下:1 可读,2 编辑,3 可读+可写, 4 可收货,8 可发货,16 可配货, 32 可审核,64 可完结

业务接口返回权限类型字段【permissionType】

对于有权限控制的业务数据,列表接口或者详情接口都会返回一个“permissionType”的字段,这个字段代表当前用户对于这条业务数据的权限类型,如当 permissionType=2 代表这个用户对于这条数据有【编辑权限】,permisionType=4 代表这个用户对于这条业务数据有收货的权限,permisionType=6表示这个用户对于这条记录用编辑和发货的权限(6=2+4)

怎么控制按钮的可用状态?

现在列表上有三个按钮,【编辑】、【收货】、【完结】,它们对应的“actType”分别为2、4、64,某一条数据的permissionType=3,这时这三个按钮的状态怎么判断呢,permissionType=3 我们可以分解为 1+2,表示这个用户对于这条记录有“可读”+“编辑”权限,则这三个按钮中,只有【编辑】按钮是可用的。那么判断的公式为:

((data[i].permissionType & obj.actType)==obj.actType)

前端的js数据进行&判断

需要进行数据转换 data.toString(2): 将数据进行2进制转换成二进制字符串。parseInt(permissionType,2) : 二进制字符串转换成二进制数据。

代码修改

接口mock返回数据

response = [{
      "type": 3,
      "name": "创建活动-10001",
      "actType": 0,
      "code": "10001"
    }, {
      "type": 3,
      "name": "编辑-10002",
      "actType": 2,
      "code": "10002"
    }, {
      "type": 3,
      "name": "配置-10005",
      "actType": 4,
      "code": "10005"
    }, {
      "type": 3,
      "name": "订阅警报-10006",
      "actType": 8,
      "code": "10006"
    }, {
      "type": 3,
      "name": "查询详情-20001",
      "actType": 16,
      "code": "20001"
    }, {
      "type": 3,
      "name": "批量操作-10007",
      "actType": 32,
      "code": "10007"
    }, {
      "type": 3,
      "name": "更多操作-10008",
      "actType": 64,
      "code": "10008"
    }]

每一个返回的接口权限会将对应的actType一起返回。

getAuthorityForPage代码修改简单修改一下,因为之前返回的是code数组,现在返回的是对象

   /**
     * 获取当前页面的权限控制
     */
    *getAuthorityForPage({ payload }, { put, call, select }) {
      // 这里的资源code都是自己加载的
      const pageCodeArray = yield select(state => state.globalAuthority.pageCodeArray);
      const response = yield call(getAuthority, pageCodeArray);
      const hasAuthorityCodeArray = response || [];
      const codeAuthorityObject = {};

      pageCodeArray.forEach((value, index, array) => {
        codeAuthorityObject[value] = hasAuthorityCodeArray.map(item => item.code).indexOf(value) >= 0;
      });

      // debugger
      yield put({
        type: 'save',
        payload: {
          hasAuthorityCodeArray,
          codeAuthorityObject,
        },
      });
    },

修改AuthorizedButton代码

增加数据权限判断

/* eslint-disable eqeqeq */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'dva';

@connect(({ globalAuthority }) => ({
  globalAuthority,
}))
class AuthorizedButton extends Component {
  static contextTypes = {
    isMobile: PropTypes.bool,
  };

  componentWillMount() {
    // extendcode 扩展表格中的code还没有出现的情况
    const {
      dispatch,
      code,
      extendCode = [],
      globalAuthority: { pageCodeArray },
    } = this.props;

    let codeArray = [];

    if (code) {
      codeArray.push(code);
    }

    if (extendCode && extendCode.length) {
      codeArray = codeArray.concat(extendCode);
    }

    // code已经存在,证明是页面数据渲染之后或者弹出框的按钮资源,不需要走dva了
    if (pageCodeArray.indexOf(code) >= 0) {
      return;
    }

    dispatch({
      type: 'globalAuthority/plusCode',
      payload: {
        codeArray,
      },
    });
  }

  checkAuthority = code => {
    const {
      globalAuthority: { hasAuthorityCodeArray },
    } = this.props;

    return hasAuthorityCodeArray.map(item => item.code).indexOf(code) >= 0 && this.checkDataAuthority(); // 资源权限
  };


  /**
   * 检测数据权限
   */
  checkDataAuthority = () => {
    const {
      globalAuthority: { hasAuthorityCodeArray },
      code,                                         // 当前按钮的code
      actType,                                      // 当前按钮的actType的值通过传递传入
      recordPermissionType,                         // 单条数据的数据操作权限总和
      actTypeArray
    } = this.props;

    if (recordPermissionType || actTypeArray) {     // 单条数据权限校验
      const tempCode = hasAuthorityCodeArray.filter(item => item.code === code)
      let tempActType = ''

      if (actType) {
        tempActType = actType
      } else if (tempCode.length) {
        tempActType = tempCode[0].actType
      } else {
        return true;                                // 默认返回true
      }

      if (actTypeArray) {                           // 批量操作
        return !actTypeArray.some(item => !this.checkPermissionType(item.toString(2), tempActType.toString(2)))
      }

      // 单条数据操作
      return this.checkPermissionType(recordPermissionType.toString(2), tempActType.toString(2))
    } 

    return true;                                    // 如果字段没有值的情况下,证明不需要进行数据权限
  }

  /**
   * 二进制检查当前当前数据是否具有当前权限
   * @param {*} permissionType 
   * @param {*} actType
   */
  checkPermissionType = (permissionType, actType) => 
    // eslint-disable-next-line no-bitwise
     (parseInt(permissionType,2) & parseInt(actType,2)).toString(2) == actType


  render() {
    const { children, code } = this.props;

    return (
      <span style={{ display: this.checkAuthority(code) ? 'inline' : 'none' }}>{children}</span>
    );
  }
}

export default AuthorizedButton;

调用方式

单条数据操作

<AuthoriedButton code="10005" recordPermissionType={record.permissionType}>
  <a onClick={() => this.handleUpdateModalVisible(true, record)}>配置</a>
</AuthoriedButton>

批量操作

 <AuthoriedButton code="10007" actTypeArray={getNotDuplicateArrayById(selectedRows, 'permissionType')}>
     <Button>批量操作</Button>
 </AuthoriedButton>

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/4eFadJaYYszebvjbNIxdYg

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录