前端如何優(yōu)雅的設計管理端權限控制?
大廠技術 高級前端 Node進階
點擊上方 程序員成長指北,關注公眾號
回復1,加入高級Node交流群
后臺管理平臺內部權限大部分涉及到到兩種方式:資源權限 & 數(shù)據(jù)權限
demo分支:https://github.com/rodchen-king/ant-design-pro-v2/tree/permission-branch
1. 基本介紹
-
資源權限:菜單導航欄 & 頁面 & 按鈕 資源可見權限。 -
數(shù)據(jù)權限:對于頁面上的數(shù)據(jù)操作,同一個人同一個頁面相同的數(shù)據(jù)可能存在不同的數(shù)據(jù)操作權限。
權限緯度
-
角色維度:大部分的情況為:用戶 => 角色 => 權限 -
用戶維度:用戶 => 權限
表現(xiàn)形式
-
基礎表現(xiàn)形式還是樹結構的展現(xiàn)形式,因為對應的菜單-頁面-按鈕是一個樹的從主干到節(jié)點的數(shù)據(jù)流向。
2. 權限數(shù)據(jù)錄入與展示
采用樹結構進行處理。唯一需要處理的是父子節(jié)點的聯(lián)動關系處理。這里因為不同的公司或者系統(tǒng)可能對于這部分的數(shù)據(jù)錄入方式不同,所以就不貼圖了。
3. 權限數(shù)據(jù)控制
3.1 用戶資源權限流程圖
3.2 前端權限控制
前端控制權限也是分為兩部分,菜單頁面 與 按鈕。因為前端權限控制的實現(xiàn),會因為后臺接口形式有所影響,但是大體方向是相同。還是會分為這兩塊內容。這里對于權限是使用多接口查詢權限,初始登錄查詢頁面權限,點擊業(yè)務頁面,查詢對應業(yè)務頁面的資源code。
3.2.1 菜單權限
菜單權限控制需要了解兩個概念:
-
一個是可見的菜單頁面 :左側dom節(jié)點 -
一個是可訪問的菜單頁面:系統(tǒng)當中路由這一塊
這里說的意思是:我們所說的菜單權限控制,大多只是停留在菜單是否可見,但是系統(tǒng)路由的頁面可見和頁面上的菜單是否可見是兩回事情。假設系統(tǒng)路由/path1可見,盡管頁面上的沒有/path1對應的菜單顯示。我們直接在瀏覽器輸入對應的path1,還是可以訪問到對應的頁面。這是因為系統(tǒng)路由那一塊其實我們是沒有去處理的。
了解了這個之后,我們需要做菜單頁面權限的時候就需要去考慮兩塊,并且是對應的。
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;
}
}
/**
* 后臺接口返回子節(jié)點的情況,父節(jié)點需要溯源處理
*/
// 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);
/**
* 后臺接口返回子節(jié)點的情況,父節(jié)點需要溯源處理
*/
window.g_routes = routes.filter(item => !item.isNeedDelete);
/**
* 后臺接口返回子節(jié)點的情況,父節(jié)點需要溯源處理
*/
// 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') {
// 這里的數(shù)據(jù)通過接口返回菜單頁面的權限是什么
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
按鈕權限上就涉及到兩塊,資源權限和數(shù)據(jù)權限。數(shù)據(jù)獲取的方式不同,代碼邏輯上會稍微有點不同。核心是業(yè)務組件內部的code,在加載的時候就自行累加,然后在頁面加載完成的時候,發(fā)送請求。拿到數(shù)據(jù)之后,自行進行權限校驗。盡量減少業(yè)務頁面代碼的復雜度。
資源權限邏輯介紹:
-
PageHeaderWrapper包含的業(yè)務頁面存在按鈕權限 -
按鈕權限通過AuthorizedButton包含處理,需要添加code。但是業(yè)務頁面因為是單獨頁面發(fā)送當前頁面code集合去查詢權限code,然后在AuthorizedButton進行權限邏輯判斷。 -
所以AuthorizedButton的componentWillMount生命周期進行當前業(yè)務頁面的code累加。累加完成之后,通過PageHeaderWrapper的componentDidMount生命周期函數(shù)發(fā)送權限請求,拿到權限code,通過公有globalAuthority model讀取數(shù)據(jù)進行權限邏輯判斷。 -
對于業(yè)務頁面的調用參考readme進行使用。因為對于彈出框內部的code,在業(yè)務列表頁面渲染的時候,組件還未加載,所以通過extencode提前將code累加起來進行查詢權限。
數(shù)據(jù)權限介紹:
-
涉及數(shù)據(jù)權限,則直接將對應的數(shù)據(jù)規(guī)則放進AuthorizedButton內部進行判斷,需要傳入的數(shù)據(jù)則直接通過props傳入即可。因為數(shù)據(jù)權限的規(guī)則不同,這里就沒有舉例子。 -
需要注意的邏輯是資源權限和數(shù)據(jù)權限是串行的,先判斷資源權限,然后判斷數(shù)據(jù)權限。
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,用來發(fā)送請求返回對應的權限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文件【因為所有的業(yè)務頁面都是這個組件的子節(jié)點】
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', // 發(fā)送請求獲取當前頁面的權限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還沒有出現(xiàn)的情況
const {
dispatch,
code,
extendCode = [],
globalAuthority: { pageCodeArray },
} = this.props;
let codeArray = [];
if (code) {
codeArray.push(code);
}
if (extendCode && extendCode.length) {
codeArray = codeArray.concat(extendCode);
}
// code已經存在,證明是頁面數(shù)據(jù)渲染之后或者彈出框的按鈕資源,不需要走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的狀態(tài)值為:key:code值,value的值為true/false。true代表,有權限,false代表無權限。主要用于開發(fā)人員自己做相關處理。

b.需要控制的按鈕code,通過其他方式擴展進行code計算,發(fā)送請求獲取權限
c.獲取數(shù)據(jù)進行數(shù)據(jù)控制
3.2.4 按鈕數(shù)據(jù)權限
代碼地址:https://github.com/rodchen-king/ant-design-pro-v2/commit/463514b0964c4c0187a503d315aa9f088e963f71
背景
數(shù)據(jù)權限是對于業(yè)務組件內部表格組件的數(shù)據(jù)進行的數(shù)據(jù)操作權限。列表數(shù)據(jù)可能歸屬于不同的數(shù)據(jù)類型,所以具有不同的數(shù)據(jù)操作權限。對于批量操作則需要判斷選擇的數(shù)據(jù)是否都具有操作權限,然后顯示是否可以批量操作,如果有一個沒有操作權限,都不能進行操作。
總體思路
場景:比如在商品列表中,每條商品記錄后面的“操作”一欄下用三個按鈕:【編輯】、【上架/下架】、【刪除】,而對于某一個用戶,他可以查看所有的商品,但對于某些品牌他可以【上架/下架】但不能【編輯】,則前端需要控制到每一個商品后面的按鈕的可用狀態(tài),比如用戶A對于某一條業(yè)務數(shù)據(jù)(id=1999)有編輯權限,則這條記錄上的【編輯】按鈕對他來說是可見的(前提是他首先要有【編輯】這個按鈕的資源權限),但對于另一條記錄(id=1899)是沒有【編輯】權限,則這條記錄上的【編輯】按鈕對他來說是不可見的。
按鈕【actType】屬性定義
每個數(shù)據(jù)操作的按鈕上加一個屬性 “actType”代表這個按鈕的動作類型(如:編輯、刪除、審核等),這個屬性是資源權限的接口返回的,前端在調這個接口時將這個屬性記錄下來,或者保存到對應的控件中。所以前端可以不用關于這個屬性的每個枚舉值代表的是什么含義,只需根據(jù)接口的返回值賦值就好。用興趣的同學也可以參考一下actType取值如下:1 可讀,2 編輯,3 可讀+可寫, 4 可收貨,8 可發(fā)貨,16 可配貨, 32 可審核,64 可完結
業(yè)務接口返回權限類型字段【permissionType】
對于有權限控制的業(yè)務數(shù)據(jù),列表接口或者詳情接口都會返回一個“permissionType”的字段,這個字段代表當前用戶對于這條業(yè)務數(shù)據(jù)的權限類型,如當 permissionType=2 代表這個用戶對于這條數(shù)據(jù)有【編輯權限】,permisionType=4 代表這個用戶對于這條業(yè)務數(shù)據(jù)有收貨的權限,permisionType=6表示這個用戶對于這條記錄用編輯和發(fā)貨的權限(6=2+4)
怎么控制按鈕的可用狀態(tài)?
現(xiàn)在列表上有三個按鈕,【編輯】、【收貨】、【完結】,它們對應的“actType”分別為2、4、64,某一條數(shù)據(jù)的permissionType=3,這時這三個按鈕的狀態(tài)怎么判斷呢,permissionType=3 我們可以分解為 1+2,表示這個用戶對于這條記錄有“可讀”+“編輯”權限,則這三個按鈕中,只有【編輯】按鈕是可用的。那么判斷的公式為:
((data[i].permissionType & obj.actType)==obj.actType)
前端的js數(shù)據(jù)進行&判斷
需要進行數(shù)據(jù)轉換 data.toString(2): 將數(shù)據(jù)進行2進制轉換成二進制字符串。parseInt(permissionType,2) : 二進制字符串轉換成二進制數(shù)據(jù)。
代碼修改
接口mock返回數(shù)據(jù)
response = [{
"type": 3,
"name": "創(chuàng)建活動-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數(shù)組,現(xiàn)在返回的是對象
/**
* 獲取當前頁面的權限控制
*/
*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代碼
增加數(shù)據(jù)權限判斷
/* 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還沒有出現(xiàn)的情況
const {
dispatch,
code,
extendCode = [],
globalAuthority: { pageCodeArray },
} = this.props;
let codeArray = [];
if (code) {
codeArray.push(code);
}
if (extendCode && extendCode.length) {
codeArray = codeArray.concat(extendCode);
}
// code已經存在,證明是頁面數(shù)據(jù)渲染之后或者彈出框的按鈕資源,不需要走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(); // 資源權限
};
/**
* 檢測數(shù)據(jù)權限
*/
checkDataAuthority = () => {
const {
globalAuthority: { hasAuthorityCodeArray },
code, // 當前按鈕的code
actType, // 當前按鈕的actType的值通過傳遞傳入
recordPermissionType, // 單條數(shù)據(jù)的數(shù)據(jù)操作權限總和
actTypeArray
} = this.props;
if (recordPermissionType || actTypeArray) { // 單條數(shù)據(jù)權限校驗
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)))
}
// 單條數(shù)據(jù)操作
return this.checkPermissionType(recordPermissionType.toString(2), tempActType.toString(2))
}
return true; // 如果字段沒有值的情況下,證明不需要進行數(shù)據(jù)權限
}
/**
* 二進制檢查當前當前數(shù)據(jù)是否具有當前權限
* @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;
調用方式
單條數(shù)據(jù)操作
<AuthoriedButton code="10005" recordPermissionType={record.permissionType}>
<a onClick={() => this.handleUpdateModalVisible(true, record)}>配置</a>
</AuthoriedButton>
批量操作
<AuthoriedButton code="10007" actTypeArray={getNotDuplicateArrayById(selectedRows, 'permissionType')}>
<Button>批量操作</Button>
</AuthoriedButton>
代碼地址:https://github.com/rodchen-king/ant-design-pro-v2/commit/463514b0964c4c0187a503d315aa9f088e963f71
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。
“分享、點贊、在看” 支持一波??
