Skip to content

Commit 0d76dfd

Browse files
authored
Merge pull request #139 from ts-react/v1
V1
2 parents cd84f34 + 3b1663a commit 0d76dfd

14 files changed

+110
-69
lines changed

config/router.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ export default [
4949
name: 'exception',
5050
icon: 'warning',
5151
path: '/exception',
52-
authority: '*',
5352
hideInMenu: true,
5453
routes: [
5554
// exception
@@ -105,11 +104,13 @@ export default [
105104
{
106105
path: '/system/user',
107106
name: 'user',
107+
authority: ['system/action1'],
108108
component: './system/users'
109109
},
110110
{
111111
path: '/system/group',
112112
name: 'group',
113+
authority: ['system/action2'],
113114
component: './system/groups'
114115
}
115116
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import isArray from 'lodash/isArray';
3+
import isString from 'lodash/isString';
4+
import Policy from '@jiumao/policy';
5+
6+
export type TAuthority = string[] | string;
7+
8+
/**
9+
* 权限检查方法
10+
* @param { 权限判定 | Permission judgment } authority
11+
* @param { 权限验证方法 | no pass components } policy
12+
*/
13+
const checkAuthority = (
14+
policy?: Policy,
15+
authority?: TAuthority
16+
): boolean => {
17+
let result = true;
18+
19+
// 数组处理
20+
if (isArray(authority)) {
21+
if (!policy.multipleVerify(authority)) {
22+
result = false;
23+
}
24+
}
25+
26+
// string 处理
27+
if (isString(authority)) {
28+
if (!policy.combinationVerify(authority)) {
29+
result = false;
30+
}
31+
}
32+
33+
return result;
34+
};
35+
36+
export default checkAuthority;

src/components/sidebar-menu/sidebar-menu.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import classNames from 'classnames';
33
import PageLoading from '@/components/page-loading';
4-
import { APP_DEFAULT_CONFIG } from '@/config';
4+
import defaultSettings from '@/config/default-settings';
55
import BaseMenu, { IBaseMenuProps } from './base-menu';
66
import { getDefaultCollapsedSubMenus } from './utils';
77
import './sidebar-menu.less';
@@ -12,7 +12,7 @@ export interface ISidebarMenuProps extends IBaseMenuProps {
1212
isMobile?: boolean;
1313
}
1414

15-
const { title } = APP_DEFAULT_CONFIG;
15+
const { title } = defaultSettings;
1616

1717
const SidebarMenu: React.FC<ISidebarMenuProps> = (props) => {
1818
const { prefixCls, className, style, collapsed, logo } = props;

src/config/default-settings.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export interface IDefaultSettings {
1414
iconFontUrl: string;
1515
// 项目标题
1616
title: string;
17+
// 公司名称
18+
company: string;
1719
}
1820

1921
const defaultSettings: IDefaultSettings = {
@@ -23,7 +25,8 @@ const defaultSettings: IDefaultSettings = {
2325
},
2426
fixedHeader: false,
2527
title: 'React Admin Template',
26-
iconFontUrl: ''
28+
iconFontUrl: '',
29+
company: '九毛科技',
2730
};
2831

2932
export default defaultSettings;

src/config/index.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// axios 相关配置
2-
export const AXIOS_DEFAULT_CONFIG = {
1+
// ajax 相关配置
2+
export const AJAX_DEFAULT_CONFIG = {
33
timeout: 20000,
44
withCredentials: true,
55
// 使用webpack DefinePlugin 插件
@@ -10,8 +10,6 @@ export const AXIOS_DEFAULT_CONFIG = {
1010

1111
// 项目相关配置
1212
export const APP_DEFAULT_CONFIG = {
13-
companyName: '九毛科技',
14-
title: 'React Admin Template',
1513
// 免登陆白名单
1614
whiteList: ['/user/*']
1715
};

src/layouts/basic-layout.tsx

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22
import { Layout } from 'antd';
33
import { connect } from 'dva';
44
import classNames from 'classnames';
5+
import Policy from '@jiumao/policy';
56
import useMedia from 'react-media-hook2';
67
import { ContainerQuery } from 'react-container-query';
78
import DocumentTitle from 'react-document-title';
@@ -16,6 +17,7 @@ import './basic-layout.less';
1617
interface IProps
1718
extends Required<ConnectProps>, ISidebarMenuProps {
1819
prefixCls?: string;
20+
policy: Policy;
1921
tabActiveKey?: string;
2022
breadcrumbNameMap?: { [path: string]: IMenu };
2123
setting?: ISettingModelState;
@@ -52,27 +54,24 @@ const BasicLayout: React.FC<IProps> = (props) => {
5254
dispatch,
5355
location,
5456
route,
57+
policy,
5558
menuData,
5659
breadcrumbNameMap,
5760
setting,
5861
children
5962
} = props;
6063
const { fixedHeader, theme } = setting;
6164
const { prefixCls, ...restProps } = props;
62-
const { routes, authority } = route!;
65+
const { routes } = route!;
6366

6467
// constructor
6568
useState(() => {
66-
// 获取当前登录用户信息
67-
// dispatch!({
68-
// type: 'user/fetchCurrent'
69-
// });
7069
// 获取菜单数据
7170
dispatch!({
7271
type: 'menu/getMenuData',
7372
payload: {
7473
routes,
75-
authority
74+
policy
7675
},
7776
});
7877
});
@@ -127,7 +126,8 @@ BasicLayout.defaultProps = {
127126
prefixCls: 'lotus-basic-layout'
128127
};
129128

130-
export default connect(({ menu, setting }: ConnectState) => ({
129+
export default connect(({ menu, setting, user }: ConnectState) => ({
130+
policy: user.policy,
131131
menuData: menu.menuData,
132132
breadcrumbNameMap: menu.breadcrumbNameMap,
133133
setting

src/layouts/copyright.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react';
22
import { Icon } from 'antd';
3-
import { APP_DEFAULT_CONFIG } from '@/config';
3+
import defaultSettings from '@/config/default-settings';
44

5-
const { companyName } = APP_DEFAULT_CONFIG;
5+
const { company } = defaultSettings;
66

77
const Copyright = () => {
88
return (
99
<div>
10-
Copyright <Icon type="copyright" /> 2019{companyName}技术部出品
10+
Copyright <Icon type="copyright" /> 2019{company}技术部出品
1111
</div>
1212
)
1313
};

src/models/menu.ts

+44-27
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,57 @@
11
import { Reducer } from 'redux';
22
import memoizeOne from 'memoize-one';
33
import isEqual from 'lodash/isEqual';
4+
import Policy from '@jiumao/policy';
45
import { formatMessage } from 'umi-plugin-react/locale';
56
import { Effect } from '@/models/connect';
7+
import checkAuthority from '@/components/authorized/check-authority';
68
import { IMenu } from '@/components/sidebar-menu';
79
import defaultSettings from '@/config/default-settings';
810

911
const { menu } = defaultSettings;
12+
let policy: Policy = null;
1013

11-
// 将路由数据转换为菜单数据
12-
function formatter(
14+
function formatterMenu(
1315
data: IRoute[],
14-
parentAuthority?: string[] | string,
1516
parentName?: string,
1617
): IMenu[] {
17-
return data
18-
.filter(item => item.name && item.path)
19-
.map(item => {
20-
const locale = `${parentName || 'menu'}.${item.name!}`;
21-
// if enableMenuLocale use item.name,
22-
// close menu international
23-
const name = menu.disableLocal
24-
? item.name!
25-
: formatMessage({ id: locale, defaultMessage: item.name! });
26-
const result: IMenu = {
27-
...item,
28-
name,
29-
locale,
30-
routes: void 0,
31-
authority: item.authority || parentAuthority,
32-
};
33-
if (item.routes) {
34-
// Reduce memory usage
35-
result.children = formatter(item.routes, item.authority, locale);
18+
let newMenus: IMenu[] = [];
19+
20+
const menus = data.filter(item => item.name && item.path);
21+
22+
menus.forEach(item => {
23+
const locale = `${parentName || 'menu'}.${item.name!}`;
24+
25+
const name = menu.disableLocal
26+
? item.name!
27+
: formatMessage({ id: locale, defaultMessage: item.name! });
28+
29+
const result: IMenu = {
30+
...item,
31+
name,
32+
locale,
33+
routes: void 0,
34+
authority: item.authority || undefined,
35+
};
36+
37+
if (item.routes) {
38+
// Reduce memory usage
39+
result.children = formatterMenu(item.routes, locale);
40+
41+
if (!result.children.length) {
42+
return;
3643
}
37-
return result;
38-
});
44+
}
45+
46+
// 检查权限
47+
let authResult = checkAuthority(policy, result.authority);
48+
49+
if (authResult) {
50+
newMenus.push(result);
51+
}
52+
});
53+
54+
return newMenus;
3955
}
4056

4157
// 获取面包屑映射
@@ -54,7 +70,7 @@ const getBreadcrumbNameMap = (menuData: IMenu[]) => {
5470
return routerMap;
5571
};
5672

57-
const memoizeOneFormatter = memoizeOne(formatter, isEqual);
73+
const memoizeOneFormatter = memoizeOne(formatterMenu, isEqual);
5874
const memoizeOneGetBreadcrumbNameMap = memoizeOne(getBreadcrumbNameMap, isEqual);
5975

6076
// 过滤菜单数据
@@ -97,8 +113,9 @@ const MenuModel: IMenuModel = {
97113
},
98114
effects: {
99115
*getMenuData({ payload, callback }, { put }) {
100-
const { routes, authority } = payload;
101-
const originalMenuData = memoizeOneFormatter(routes, authority);
116+
const { routes } = payload;
117+
policy = payload.policy;
118+
const originalMenuData = memoizeOneFormatter(routes);
102119
const menuData = filterMenuData(originalMenuData);
103120
const breadcrumbNameMap = memoizeOneGetBreadcrumbNameMap(originalMenuData);
104121

src/models/user.ts

-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ const UserModel: IUserModel = {
5555
policy.addPolicy(item);
5656
});
5757

58-
console.log(policy);
59-
6058
yield put({
6159
type: 'saveCurrentUser',
6260
payload: {

src/pages/authorized.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ interface IProps extends ConnectProps {
1515

1616
const AuthComponent: React.FC<IProps> = (props) => {
1717
const {
18+
policy,
1819
loading,
1920
location,
2021
children,
2122
routerData,
2223
dispatch
2324
} = props;
24-
let policy = props.policy;
2525

2626
React.useState(() => {
2727
// 类似 Promise.all 实现比较合理,待优化
@@ -71,7 +71,9 @@ const AuthComponent: React.FC<IProps> = (props) => {
7171
)
7272
};
7373

74-
AuthComponent.defaultProps = {};
74+
AuthComponent.defaultProps = {
75+
policy: null
76+
};
7577

7678
export default connect(({ menu, user, loading }: ConnectState) => ({
7779
policy: user.policy,

src/pages/document.ejs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<meta http-equiv="X-UA-Compatible" content="IE=edge">
77
<meta name="description" content="A admin dashboard application demo built upon Ant Design and Dva.js">
8-
<link rel="icon" type="image/png" href="./favicon.png">
8+
<link rel="icon" type="image/png" href="/react-admin-template/favicon.png">
99
<title>React Admin Template</title>
1010
</head>
1111
<body>

src/utils/path-tools.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { parse, stringify } from 'qs';
1+
import { parse } from 'qs';
22

33
export function parseQuery(data?: string) {
44
const url = data || window.location.href.split('?')[1];
55
return parse(url);
66
}
7-
8-
export function stringifyQuery() {}

src/utils/regexp.ts

-12
This file was deleted.

src/utils/request.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import Axios, { AxiosRequestConfig } from 'axios';
22
import router from 'umi/router';
33
import NProgress from 'nprogress';
44
import 'nprogress/nprogress.css';
5-
import { AXIOS_DEFAULT_CONFIG } from '@/config';
5+
import { AJAX_DEFAULT_CONFIG } from '@/config';
66
import { getCookie } from '@/utils/cookie';
77

8-
Axios.defaults.timeout = AXIOS_DEFAULT_CONFIG.timeout;
9-
Axios.defaults.baseURL = AXIOS_DEFAULT_CONFIG.baseURL;
10-
Axios.defaults.withCredentials = AXIOS_DEFAULT_CONFIG.withCredentials;
8+
Axios.defaults.timeout = AJAX_DEFAULT_CONFIG.timeout;
9+
Axios.defaults.baseURL = AJAX_DEFAULT_CONFIG.baseURL;
10+
Axios.defaults.withCredentials = AJAX_DEFAULT_CONFIG.withCredentials;
1111

1212
function requestSuccess(config) {
1313
// 请求开始,开启进度条

0 commit comments

Comments
 (0)