Unverified Commit cfa27b18 authored by ddcat1115's avatar ddcat1115 Committed by GitHub

Authority management (#508)

* temp save

* rebase master

* fix same error

* Add a new user and different permissions

* fix eol-last

* add Secured decorator

* fix list redirect bug (#507)

* Mobile menu (#463)

* Increase the sliding menu

* Add a simple animation

* update mobile menu

* update

* update

* update

* rebase master

* recovery import/first

* fix error

* Fix some bugs
Change "ALL" to "NONE"
Remove the "!" Support
After landing successfully reload
Reset the format

* Pump your public logic

* add some test

* Add documents

* use default currentRole in Authorized/AuthorizedRoute

* rename props & change some authority setting

* A big change
😄 unified router and Secured parameters
😭 loginOut logout also changed to reload

* fix siderMeun bugs

* Decoupled SiderMenu

* Remove the handsome head of information

* Add a simple error

* rebase master
parent 74f0a0aa
...@@ -15,3 +15,4 @@ npm-debug.log* ...@@ -15,3 +15,4 @@ npm-debug.log*
/coverage /coverage
.idea .idea
yarn.lock
...@@ -70,13 +70,30 @@ const proxy = { ...@@ -70,13 +70,30 @@ const proxy = {
'GET /api/profile/advanced': getProfileAdvancedData, 'GET /api/profile/advanced': getProfileAdvancedData,
'POST /api/login/account': (req, res) => { 'POST /api/login/account': (req, res) => {
const { password, userName, type } = req.body; const { password, userName, type } = req.body;
if(password === '888888' && userName === 'admin'){
res.send({
status: 'ok',
type,
currentAuthority: 'admin'
});
return ;
}
if(password === '123456' && userName === 'user'){
res.send({
status: 'ok',
type,
currentAuthority: 'user'
});
return ;
}
res.send({ res.send({
status: password === '888888' && userName === 'admin' ? 'ok' : 'error', status: 'error',
type, type,
currentAuthority: 'guest'
}); });
}, },
'POST /api/register': (req, res) => { 'POST /api/register': (req, res) => {
res.send({ status: 'ok' }); res.send({ status: 'ok', currentAuthority: 'user' });
}, },
'GET /api/notices': getNotices, 'GET /api/notices': getNotices,
'GET /api/500': (req, res) => { 'GET /api/500': (req, res) => {
......
...@@ -16,11 +16,12 @@ ...@@ -16,11 +16,12 @@
"lint-staged": "lint-staged", "lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js", "lint-staged:js": "eslint --ext .js",
"test": "jest", "test": "jest",
"test:comps": "jest ./src/components",
"test:all": "node ./tests/run-tests.js" "test:all": "node ./tests/run-tests.js"
}, },
"dependencies": { "dependencies": {
"@antv/data-set": "^0.8.0", "@antv/data-set": "^0.8.0",
"antd": "^3.0.0", "antd": "^3.1.0",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"babel-runtime": "^6.9.2", "babel-runtime": "^6.9.2",
"bizcharts": "^3.1.0-beta.4", "bizcharts": "^3.1.0-beta.4",
...@@ -37,10 +38,10 @@ ...@@ -37,10 +38,10 @@
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qs": "^6.5.0", "qs": "^6.5.0",
"rc-drawer-menu": "^0.5.0", "rc-drawer-menu": "^0.5.0",
"react": "^16.0.0", "react": "^16.2.0",
"react-container-query": "^0.9.1", "react-container-query": "^0.9.1",
"react-document-title": "^2.0.3", "react-document-title": "^2.0.3",
"react-dom": "^16.0.0", "react-dom": "^16.2.0",
"react-fittext": "^1.0.0" "react-fittext": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -25,6 +25,7 @@ const menuData = [{ ...@@ -25,6 +25,7 @@ const menuData = [{
path: 'step-form', path: 'step-form',
}, { }, {
name: '高级表单', name: '高级表单',
authority: 'admin',
path: 'advanced-form', path: 'advanced-form',
}], }],
}, { }, {
...@@ -64,6 +65,7 @@ const menuData = [{ ...@@ -64,6 +65,7 @@ const menuData = [{
}, { }, {
name: '高级详情页', name: '高级详情页',
path: 'advanced', path: 'advanced',
authority: 'admin',
}], }],
}, { }, {
name: '结果页', name: '结果页',
...@@ -83,6 +85,7 @@ const menuData = [{ ...@@ -83,6 +85,7 @@ const menuData = [{
children: [{ children: [{
name: '403', name: '403',
path: '403', path: '403',
authority: 'user',
}, { }, {
name: '404', name: '404',
path: '404', path: '404',
...@@ -97,6 +100,7 @@ const menuData = [{ ...@@ -97,6 +100,7 @@ const menuData = [{
name: '账户', name: '账户',
icon: 'user', icon: 'user',
path: 'user', path: 'user',
authority: 'guest',
children: [{ children: [{
name: '登录', name: '登录',
path: 'login', path: 'login',
...@@ -114,14 +118,15 @@ const menuData = [{ ...@@ -114,14 +118,15 @@ const menuData = [{
target: '_blank', target: '_blank',
}]; }];
function formatter(data, parentPath = '') { function formatter(data, parentPath = '', parentAuthority) {
return data.map((item) => { return data.map((item) => {
const result = { const result = {
...item, ...item,
path: `${parentPath}${item.path}`, path: `${parentPath}${item.path}`,
authority: item.authority || parentAuthority,
}; };
if (item.children) { if (item.children) {
result.children = formatter(item.children, `${parentPath}${item.path}/`); result.children = formatter(item.children, `${parentPath}${item.path}/`, item.authority);
} }
return result; return result;
}); });
......
...@@ -21,17 +21,17 @@ function getFlatMenuData(menus) { ...@@ -21,17 +21,17 @@ function getFlatMenuData(menus) {
let keys = {}; let keys = {};
menus.forEach((item) => { menus.forEach((item) => {
if (item.children) { if (item.children) {
keys[item.path] = item.name; keys[item.path] = { ...item };
keys = { ...keys, ...getFlatMenuData(item.children) }; keys = { ...keys, ...getFlatMenuData(item.children) };
} else { } else {
keys[item.path] = item.name; keys[item.path] = { ...item };
} }
}); });
return keys; return keys;
} }
export const getRouterData = (app) => { export const getRouterData = (app) => {
const routerData = { const routerConfig = {
'/': { '/': {
component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')), component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
}, },
...@@ -45,6 +45,7 @@ export const getRouterData = (app) => { ...@@ -45,6 +45,7 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, ['project', 'activities', 'chart'], () => import('../routes/Dashboard/Workplace')), component: dynamicWrapper(app, ['project', 'activities', 'chart'], () => import('../routes/Dashboard/Workplace')),
// hideInBreadcrumb: true, // hideInBreadcrumb: true,
// name: '工作台', // name: '工作台',
// authority: 'admin',
}, },
'/form/basic-form': { '/form/basic-form': {
component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/BasicForm')), component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/BasicForm')),
...@@ -127,12 +128,14 @@ export const getRouterData = (app) => { ...@@ -127,12 +128,14 @@ export const getRouterData = (app) => {
}; };
// Get name from ./menu.js or just set it in the router data. // Get name from ./menu.js or just set it in the router data.
const menuData = getFlatMenuData(getMenuData()); const menuData = getFlatMenuData(getMenuData());
const routerDataWithName = {}; const routerData = {};
Object.keys(routerData).forEach((item) => { Object.keys(routerConfig).forEach((item) => {
routerDataWithName[item] = { const menuItem = menuData[item.replace(/^\//, '')] || {};
...routerData[item], routerData[item] = {
name: routerData[item].name || menuData[item.replace(/^\//, '')], ...routerConfig[item],
name: routerConfig[item].name || menuItem.name,
authority: routerConfig[item].authority || menuItem.authority,
}; };
}); });
return routerDataWithName; return routerData;
}; };
import React from 'react';
import CheckPermissions from './CheckPermissions';
class Authorized extends React.Component {
render() {
const { children, authority, noMatch = null } = this.props;
const childrenRender = typeof children === 'undefined' ? null : children;
return CheckPermissions(
authority,
childrenRender,
noMatch
);
}
}
export default Authorized;
import React from 'react';
import { Route, Redirect } from 'dva/router';
import Authorized from './Authorized';
class AuthorizedRoute extends React.Component {
render() {
const { component: Component, render, authority,
redirectPath, ...rest } = this.props;
return (
<Authorized
authority={authority}
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
>
<Route
{...rest}
render={props => (Component ? <Component {...props} /> : render(props))}
/>
</Authorized>
);
}
}
export default AuthorizedRoute;
import React from 'react';
import PromiseRender from './PromiseRender';
import { CURRENT } from './index';
/**
* 通用权限检查方法
* Common check permissions method
* @param { 权限判定 Permission judgment type string |array | Promise | Function } authority
* @param { 你的权限 Your permission description type:string} currentAuthority
* @param { 通过的组件 Passing components } target
* @param { 未通过的组件 no pass components } Exception
*/
const checkPermissions = (authority, currentAuthority, target, Exception) => {
// 没有判定权限.默认查看所有
// Retirement authority, return target;
if (!authority) {
return target;
}
// 数组处理
if (authority.constructor.name === 'Array') {
if (authority.includes(currentAuthority)) {
return target;
}
return Exception;
}
// string 处理
if (authority.constructor.name === 'String') {
if (authority === currentAuthority) {
return target;
}
return Exception;
}
// Promise 处理
if (authority.constructor.name === 'Promise') {
return () => (
<PromiseRender ok={target} error={Exception} promise={authority} />
);
}
// Function 处理
if (authority.constructor.name === 'Function') {
try {
const bool = authority();
if (bool) {
return target;
}
return Exception;
} catch (error) {
throw error;
}
}
throw new Error('unsupported parameters');
};
export { checkPermissions };
const check = (authority, target, Exception) => {
return checkPermissions(authority, CURRENT, target, Exception);
};
export default check;
import { checkPermissions } from './CheckPermissions.js';
const target = 'ok';
const error = 'error';
describe('test CheckPermissions', () => {
it('Correct string permission authentication', () => {
expect(checkPermissions('user', 'user', target, error)).toEqual('ok');
});
it('Correct string permission authentication', () => {
expect(checkPermissions('user', 'NULL', target, error)).toEqual('error');
});
it('authority is undefined , return ok', () => {
expect(checkPermissions(null, 'NULL', target, error)).toEqual('ok');
});
it('Wrong string permission authentication', () => {
expect(checkPermissions('admin', 'user', target, error)).toEqual('error');
});
it('Correct Array permission authentication', () => {
expect(checkPermissions(['user', 'admin'], 'user', target, error)).toEqual(
'ok'
);
});
it('Wrong Array permission authentication,currentAuthority error', () => {
expect(
checkPermissions(['user', 'admin'], 'user,admin', target, error)
).toEqual('error');
});
it('Wrong Array permission authentication', () => {
expect(checkPermissions(['user', 'admin'], 'guest', target, error)).toEqual(
'error'
);
});
it('Wrong Function permission authentication', () => {
expect(checkPermissions(() => false, 'guest', target, error)).toEqual(
'error'
);
});
it('Correct Function permission authentication', () => {
expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok');
});
});
import React from 'react';
import { Spin } from 'antd';
export default class PromiseRender extends React.PureComponent {
state = {
component: false,
};
async componentDidMount() {
this.props.promise
.then(() => {
this.setState({
component: this.props.ok,
});
})
.catch(() => {
this.setState({
component: this.props.error,
});
});
}
render() {
const C = this.state.component;
return C ? (
<C {...this.props} />
) : (
<div
style={{
width: '100%',
height: '100%',
margin: 'auto',
paddingTop: 50,
textAlign: 'center',
}}
>
<Spin size="large" />
</div>
);
}
}
import React from 'react';
import Exception from '../Exception/index';
import CheckPermissions from './CheckPermissions';
/**
* 默认不能访问任何页面
* default is "NULL"
*/
const Exception403 = () => (
<Exception type="403" style={{ minHeight: 500, height: '80%' }} />
);
/**
* 用于判断是否拥有权限访问此view权限
* authority 支持传入 string ,funtion:()=>boolean|Promise
* e.g. 'user' 只有user用户能访问
* e.g. 'user,admin' user和 admin 都能访问
* e.g. ()=>boolean 返回true能访问,返回false不能访问
* e.g. Promise then 能访问 catch不能访问
* e.g. authority support incoming string, funtion: () => boolean | Promise
* e.g. 'user' only user user can access
* e.g. 'user, admin' user and admin can access
* e.g. () => boolean true to be able to visit, return false can not be accessed
* e.g. Promise then can not access the visit to catch
* @param {string | function | Promise} authority
* @param {ReactNode} error 非必需参数
*/
const authorize = (authority, error) => {
/**
* conversion into a class
* 防止传入字符串时找不到staticContext造成报错
* String parameters can cause staticContext not found error
*/
let classError = false;
if (error) {
classError = () => error;
}
if (!authority) {
throw new Error('authority is required');
}
return function decideAuthority(targer) {
return CheckPermissions(
authority,
targer,
classError || Exception403
);
};
};
export default authorize;
import Authorized from './Authorized';
import AuthorizedRoute from './AuthorizedRoute';
import Secured from './Secured';
import check from './CheckPermissions.js';
/* eslint-disable import/no-mutable-exports */
let CURRENT = 'NULL';
Authorized.Secured = Secured;
Authorized.AuthorizedRoute = AuthorizedRoute;
Authorized.check = check;
/**
* use authority or getAuthority
* @param {string|()=>String} currentAuthority
*/
const renderAuthorize = (currentAuthority) => {
if (currentAuthority) {
if (currentAuthority.constructor.name === 'Function') {
CURRENT = currentAuthority();
}
if (currentAuthority.constructor.name === 'String') {
CURRENT = currentAuthority;
}
} else {
CURRENT = 'NULL';
}
return Authorized;
};
export { CURRENT };
export default renderAuthorize;
...@@ -77,62 +77,98 @@ export default class SiderMenu extends PureComponent { ...@@ -77,62 +77,98 @@ export default class SiderMenu extends PureComponent {
return itemRegExp.test(path.replace(/^\//, '').replace(/\/$/, '')); return itemRegExp.test(path.replace(/^\//, '').replace(/\/$/, ''));
}); });
} }
getNavMenuItems(menusData) { /**
* 判断是否是http链接.返回 Link 或 a
* Judge whether it is http link.return a or Link
* @memberof SiderMenu
*/
getMenuItemPath = (item) => {
const itemPath = this.conversionPath(item.path);
const icon = getIcon(item.icon);
const { target, name } = item;
// Is it a http link
if (/^https?:\/\//.test(itemPath)) {
return (
<a href={itemPath} target={target}>
{icon}<span>{name}</span>
</a>
);
}
return (
<Link
to={itemPath}
target={target}
replace={itemPath === this.props.location.pathname}
onClick={this.props.isMobile ? () => { this.props.onCollapse(true); } : undefined}
>
{icon}<span>{name}</span>
</Link>
);
}
/**
* get SubMenu or Item
*/
getSubMenuOrItem=(item) => {
if (item.children && item.children.some(child => child.name)) {
return (
<SubMenu
title={
item.icon ? (
<span>
{getIcon(item.icon)}
<span>{item.name}</span>
</span>
) : item.name
}
key={item.key || item.path}
>
{this.getNavMenuItems(item.children)}
</SubMenu>
);
} else {
return (
<Menu.Item key={item.key || item.path}>
{this.getMenuItemPath(item)}
</Menu.Item>
);
}
}
/**
* 获得菜单子节点
* @memberof SiderMenu
*/
getNavMenuItems = (menusData) => {
if (!menusData) { if (!menusData) {
return []; return [];
} }
return menusData.map((item) => { return menusData.map((item) => {
if (!item.name) { if (!item.name || item.hideInMenu) {
return null; return null;
} }
let itemPath; const ItemDom = this.getSubMenuOrItem(item);
if (item.path && item.path.indexOf('http') === 0) { return this.checkPermissionItem(item.authority, ItemDom);
itemPath = item.path;
} else {
itemPath = `/${item.path || ''}`.replace(/\/+/g, '/');
}
if (item.children && item.children.some(child => child.name)) {
return item.hideInMenu ? null :
(
<SubMenu
title={
item.icon ? (
<span>
{getIcon(item.icon)}
<span>{item.name}</span>
</span>
) : item.name
}
key={item.key || item.path}
>
{this.getNavMenuItems(item.children)}
</SubMenu>
);
}
const icon = getIcon(item.icon);
return item.hideInMenu ? null :
(
<Menu.Item key={item.key || item.path}>
{
/^https?:\/\//.test(itemPath) ? (
<a href={itemPath} target={item.target}>
{icon}<span>{item.name}</span>
</a>
) : (
<Link
to={itemPath}
target={item.target}
replace={itemPath === this.props.location.pathname}
onClick={this.props.isMobile ? () => { this.props.onCollapse(true); } : undefined}
>
{icon}<span>{item.name}</span>
</Link>
)
}
</Menu.Item>
);
}); });
} }
// conversion Path
// 转化路径
conversionPath=(path) => {
if (path && path.indexOf('http') === 0) {
return path;
} else {
return `/${path || ''}`.replace(/\/+/g, '/');
}
}
// permission to check
checkPermissionItem = (authority, ItemDom) => {
if (this.props.Authorized && this.props.Authorized.check) {
const { check } = this.props.Authorized;
return check(
authority,
ItemDom
);
}
return ItemDom;
}
handleOpenChange = (openKeys) => { handleOpenChange = (openKeys) => {
const lastOpenKey = openKeys[openKeys.length - 1]; const lastOpenKey = openKeys[openKeys.length - 1];
const isMainMenu = this.menus.some( const isMainMenu = this.menus.some(
......
...@@ -12,9 +12,13 @@ import GlobalFooter from '../components/GlobalFooter'; ...@@ -12,9 +12,13 @@ import GlobalFooter from '../components/GlobalFooter';
import SiderMenu from '../components/SiderMenu'; import SiderMenu from '../components/SiderMenu';
import NotFound from '../routes/Exception/404'; import NotFound from '../routes/Exception/404';
import { getRoutes } from '../utils/utils'; import { getRoutes } from '../utils/utils';
import Authorized from '../utils/Authorized';
import { getMenuData } from '../common/menu'; import { getMenuData } from '../common/menu';
import logo from '../assets/logo.svg'; import logo from '../assets/logo.svg';
const { Content } = Layout;
const { AuthorizedRoute } = Authorized;
/** /**
* 根据菜单取得重定向地址. * 根据菜单取得重定向地址.
*/ */
...@@ -34,7 +38,6 @@ const getRedirect = (item) => { ...@@ -34,7 +38,6 @@ const getRedirect = (item) => {
}; };
getMenuData().forEach(getRedirect); getMenuData().forEach(getRedirect);
const { Content } = Layout;
const query = { const query = {
'screen-xs': { 'screen-xs': {
maxWidth: 575, maxWidth: 575,
...@@ -130,6 +133,10 @@ class BasicLayout extends React.PureComponent { ...@@ -130,6 +133,10 @@ class BasicLayout extends React.PureComponent {
<Layout> <Layout>
<SiderMenu <SiderMenu
logo={logo} logo={logo}
// 不带Authorized参数的情况下如果没有权限,会强制跳到403界面
// If you do not have the Authorized parameter
// you will be forced to jump to the 403 interface without permission
Authorized={Authorized}
menuData={getMenuData()} menuData={getMenuData()}
collapsed={collapsed} collapsed={collapsed}
location={location} location={location}
...@@ -153,19 +160,23 @@ class BasicLayout extends React.PureComponent { ...@@ -153,19 +160,23 @@ class BasicLayout extends React.PureComponent {
<div style={{ minHeight: 'calc(100vh - 260px)' }}> <div style={{ minHeight: 'calc(100vh - 260px)' }}>
<Switch> <Switch>
{ {
redirectData.map(item => getRoutes(match.path, routerData).map(item =>
<Redirect key={item.from} exact from={item.from} to={item.to} /> (
<AuthorizedRoute
key={item.key}
path={item.path}
component={item.component}
exact={item.exact}
authority={item.authority}
redirectPath="/exception/403"
/>
)
) )
} }
{ {
getRoutes(match.path, routerData).map(item => ( redirectData.map(item =>
<Route <Redirect key={item.from} exact from={item.from} to={item.to} />
key={item.key} )
path={item.path}
component={item.component}
exact={item.exact}
/>
))
} }
<Redirect exact from="/" to="/dashboard/analysis" /> <Redirect exact from="/" to="/dashboard/analysis" />
<Route render={NotFound} /> <Route render={NotFound} />
......
import { routerRedux } from 'dva/router';
import { fakeAccountLogin } from '../services/api'; import { fakeAccountLogin } from '../services/api';
import { setAuthority } from '../utils/authority';
export default { export default {
namespace: 'login', namespace: 'login',
...@@ -21,7 +21,11 @@ export default { ...@@ -21,7 +21,11 @@ export default {
}); });
// Login successfully // Login successfully
if (response.status === 'ok') { if (response.status === 'ok') {
yield put(routerRedux.push('/')); // 非常粗暴的跳转,登陆成功之后权限会变成user或admin,会自动重定向到主页
// Login success after permission changes to admin or user
// The refresh will automatically redirect to the home page
// yield put(routerRedux.push('/'));
location.reload();
} }
}, },
*logout(_, { put }) { *logout(_, { put }) {
...@@ -29,14 +33,19 @@ export default { ...@@ -29,14 +33,19 @@ export default {
type: 'changeLoginStatus', type: 'changeLoginStatus',
payload: { payload: {
status: false, status: false,
currentAuthority: 'guest',
}, },
}); });
yield put(routerRedux.push('/user/login')); // yield put(routerRedux.push('/user/login'));
// Login out after permission changes to admin or user
// The refresh will automatically redirect to the login page
location.reload();
}, },
}, },
reducers: { reducers: {
changeLoginStatus(state, { payload }) { changeLoginStatus(state, { payload }) {
setAuthority(payload.currentAuthority);
return { return {
...state, ...state,
status: payload.status, status: payload.status,
......
import React from 'react'; import React from 'react';
import { Router, Route, Switch } from 'dva/router'; import { Router, Switch } from 'dva/router';
import { LocaleProvider, Spin } from 'antd'; import { LocaleProvider, Spin } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN'; import zhCN from 'antd/lib/locale-provider/zh_CN';
import dynamic from 'dva/dynamic'; import dynamic from 'dva/dynamic';
import { getRouterData } from './common/router'; import { getRouterData } from './common/router';
import Authorized from './utils/Authorized';
import styles from './index.less'; import styles from './index.less';
const { AuthorizedRoute } = Authorized;
dynamic.setDefaultLoadingComponent(() => { dynamic.setDefaultLoadingComponent(() => {
return <Spin size="large" className={styles.globalSpin} />; return <Spin size="large" className={styles.globalSpin} />;
}); });
...@@ -19,8 +20,18 @@ function RouterConfig({ history, app }) { ...@@ -19,8 +20,18 @@ function RouterConfig({ history, app }) {
<LocaleProvider locale={zhCN}> <LocaleProvider locale={zhCN}>
<Router history={history}> <Router history={history}>
<Switch> <Switch>
<Route path="/user" render={props => <UserLayout {...props} />} /> <AuthorizedRoute
<Route path="/" render={props => <BasicLayout {...props} />} /> path="/user"
render={props => <UserLayout {...props} />}
authority="guest"
redirectPath="/"
/>
<AuthorizedRoute
path="/"
render={props => <BasicLayout {...props} />}
authority={['admin', 'user']}
redirectPath="/user/login"
/>
</Switch> </Switch>
</Router> </Router>
</LocaleProvider> </LocaleProvider>
......
...@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; ...@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import { Row, Col, Card, Tooltip } from 'antd'; import { Row, Col, Card, Tooltip } from 'antd';
import numeral from 'numeral'; import numeral from 'numeral';
import { Secured } from '../../utils/Authorized';
import { Pie, WaterWave, Gauge, TagCloud } from '../../components/Charts'; import { Pie, WaterWave, Gauge, TagCloud } from '../../components/Charts';
import NumberInfo from '../../components/NumberInfo'; import NumberInfo from '../../components/NumberInfo';
import CountDown from '../../components/CountDown'; import CountDown from '../../components/CountDown';
...@@ -12,6 +12,7 @@ import styles from './Monitor.less'; ...@@ -12,6 +12,7 @@ import styles from './Monitor.less';
const targetTime = new Date().getTime() + 3900000; const targetTime = new Date().getTime() + 3900000;
@Secured('admin')
@connect(state => ({ @connect(state => ({
monitor: state.monitor, monitor: state.monitor,
})) }))
...@@ -40,7 +41,10 @@ export default class Monitor extends PureComponent { ...@@ -40,7 +41,10 @@ export default class Monitor extends PureComponent {
/> />
</Col> </Col>
<Col md={6} sm={12} xs={24}> <Col md={6} sm={12} xs={24}>
<NumberInfo subTitle="销售目标完成率" total="92%" /> <NumberInfo
subTitle="销售目标完成率"
total="92%"
/>
</Col> </Col>
<Col md={6} sm={12} xs={24}> <Col md={6} sm={12} xs={24}>
<NumberInfo subTitle="活动剩余时间" total={<CountDown target={targetTime} />} /> <NumberInfo subTitle="活动剩余时间" total={<CountDown target={targetTime} />} />
...@@ -55,10 +59,7 @@ export default class Monitor extends PureComponent { ...@@ -55,10 +59,7 @@ export default class Monitor extends PureComponent {
</Row> </Row>
<div className={styles.mapChart}> <div className={styles.mapChart}>
<Tooltip title="等待后期实现"> <Tooltip title="等待后期实现">
<img <img src="https://gw.alipayobjects.com/zos/rmsportal/HBWnDEUXCnGnGrRfrpKa.png" alt="map" />
src="https://gw.alipayobjects.com/zos/rmsportal/HBWnDEUXCnGnGrRfrpKa.png"
alt="map"
/>
</Tooltip> </Tooltip>
</div> </div>
</Card> </Card>
...@@ -140,17 +141,20 @@ export default class Monitor extends PureComponent { ...@@ -140,17 +141,20 @@ export default class Monitor extends PureComponent {
</Card> </Card>
</Col> </Col>
<Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}> <Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}>
<Card title="热门搜索" bordered={false}> <Card title="热门搜索" bordered={false} bodyStyle={{ overflow: 'hidden' }}>
<TagCloud data={tags} height={161} /> <TagCloud
data={tags}
height={161}
/>
</Card> </Card>
</Col> </Col>
<Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}> <Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}>
<Card <Card title="资源剩余" bodyStyle={{ textAlign: 'center', fontSize: 0 }} bordered={false}>
title="资源剩余" <WaterWave
bodyStyle={{ textAlign: 'center', fontSize: 0 }} height={161}
bordered={false} title="补贴资金剩余"
> percent={34}
<WaterWave height={161} title="补贴资金剩余" percent={34} /> />
</Card> </Card>
</Col> </Col>
</Row> </Row>
......
...@@ -62,8 +62,8 @@ export default class LoginPage extends Component { ...@@ -62,8 +62,8 @@ export default class LoginPage extends Component {
login.submitting === false && login.submitting === false &&
this.renderMessage('账户或密码错误') this.renderMessage('账户或密码错误')
} }
<UserName name="userName" /> <UserName name="userName" placeholder="admin/user" />
<Password name="password" /> <Password name="password" placeholder="888888/123456" />
</Tab> </Tab>
<Tab key="mobile" tab="手机号登录"> <Tab key="mobile" tab="手机号登录">
{ {
......
import RenderAuthorized from '../components/Authorized';
import { getAuthority } from './authority';
const Authorized = RenderAuthorized(getAuthority());
export default Authorized;
// use localStorage to store the authority info, which might be sent from server in actual project.
export function getAuthority() {
return localStorage.getItem('antd-pro-authority') || 'guest';
}
export function setAuthority(authority) {
return localStorage.setItem('antd-pro-authority', authority);
}
...@@ -124,9 +124,9 @@ export function getRoutes(path, routerData) { ...@@ -124,9 +124,9 @@ export function getRoutes(path, routerData) {
const renderRoutes = renderArr.map((item) => { const renderRoutes = renderArr.map((item) => {
const exact = !routes.some(route => route !== item && getRelation(route, item) === 1); const exact = !routes.some(route => route !== item && getRelation(route, item) === 1);
return { return {
...routerData[`${path}${item}`],
key: `${path}${item}`, key: `${path}${item}`,
path: `${path}${item}`, path: `${path}${item}`,
component: routerData[`${path}${item}`].component,
exact, exact,
}; };
}); });
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment