Commit e3a73309 authored by 陈帅's avatar 陈帅

Merge branch 'master' into v2

parents 902bdcb6 67f686a2
version: 2
jobs:
build:
docker:
- image: circleci/node:8.11.2
steps:
- checkout
- run: npm install
- run: npm run build
test:
docker:
- image: circleci/node:8.11.2
steps:
- checkout
- run: sh ./tests/fix_puppeteer.sh
- run: npm install
- run: npm run test:all
workflows:
version: 2
build_and_test:
jobs:
- build
- test
\ No newline at end of file
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
"rollbar": "^2.3.4", "rollbar": "^2.3.4",
"rollup": "^0.62.0", "rollup": "^0.62.0",
"rollup-plugin-json": "^3.0.0", "rollup-plugin-json": "^3.0.0",
"setprototypeof": "^1.1.0",
"url-polyfill": "^1.0.10" "url-polyfill": "^1.0.10"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -29,6 +29,14 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => { ...@@ -29,6 +29,14 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => {
if (authority.indexOf(currentAuthority) >= 0) { if (authority.indexOf(currentAuthority) >= 0) {
return target; return target;
} }
if (Array.isArray(currentAuthority)) {
for (let i = 0; i < currentAuthority.length; i += 1) {
const element = currentAuthority[i];
if (authority.indexOf(element) >= 0) {
return target;
}
}
}
return Exception; return Exception;
} }
...@@ -37,6 +45,14 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => { ...@@ -37,6 +45,14 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => {
if (authority === currentAuthority) { if (authority === currentAuthority) {
return target; return target;
} }
if (Array.isArray(currentAuthority)) {
for (let i = 0; i < currentAuthority.length; i += 1) {
const element = currentAuthority[i];
if (authority.indexOf(element) >= 0) {
return target;
}
}
}
return Exception; return Exception;
} }
......
...@@ -34,4 +34,22 @@ describe('test CheckPermissions', () => { ...@@ -34,4 +34,22 @@ describe('test CheckPermissions', () => {
it('Correct Function permission authentication', () => { it('Correct Function permission authentication', () => {
expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok'); expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok');
}); });
it('authority is string, currentAuthority is array, return ok', () => {
expect(checkPermissions('user', ['user'], target, error)).toEqual('ok');
});
it('authority is string, currentAuthority is array, return ok', () => {
expect(checkPermissions('user', ['user', 'admin'], target, error)).toEqual('ok');
});
it('authority is array, currentAuthority is array, return ok', () => {
expect(checkPermissions(['user', 'admin'], ['user', 'admin'], target, error)).toEqual('ok');
});
it('Wrong Function permission authentication', () => {
expect(checkPermissions(() => false, ['user'], target, error)).toEqual('error');
});
it('Correct Function permission authentication', () => {
expect(checkPermissions(() => true, ['user'], target, error)).toEqual('ok');
});
it('authority is undefined , return ok', () => {
expect(checkPermissions(null, ['user'], target, error)).toEqual('ok');
});
}); });
...@@ -46,8 +46,8 @@ const authorize = (authority, error) => { ...@@ -46,8 +46,8 @@ const authorize = (authority, error) => {
if (!authority) { if (!authority) {
throw new Error('authority is required'); throw new Error('authority is required');
} }
return function decideAuthority(targer) { return function decideAuthority(target) {
const component = CheckPermissions(authority, targer, classError || Exception403); const component = CheckPermissions(authority, target, classError || Exception403);
return checkIsInstantiation(component); return checkIsInstantiation(component);
}; };
}; };
......
...@@ -10,7 +10,10 @@ const renderAuthorize = Authorized => { ...@@ -10,7 +10,10 @@ const renderAuthorize = Authorized => {
if (currentAuthority.constructor.name === 'Function') { if (currentAuthority.constructor.name === 'Function') {
CURRENT = currentAuthority(); CURRENT = currentAuthority();
} }
if (currentAuthority.constructor.name === 'String') { if (
currentAuthority.constructor.name === 'String' ||
currentAuthority.constructor.name === 'Array'
) {
CURRENT = currentAuthority; CURRENT = currentAuthority;
} }
} else { } else {
......
...@@ -19,6 +19,14 @@ class Bar extends Component { ...@@ -19,6 +19,14 @@ class Bar extends Component {
window.removeEventListener('resize', this.resize); window.removeEventListener('resize', this.resize);
} }
handleRoot = n => {
this.root = n;
};
handleRef = n => {
this.node = n;
};
@Bind() @Bind()
@Debounce(400) @Debounce(400)
resize() { resize() {
...@@ -46,14 +54,6 @@ class Bar extends Component { ...@@ -46,14 +54,6 @@ class Bar extends Component {
} }
} }
handleRoot = n => {
this.root = n;
};
handleRef = n => {
this.node = n;
};
render() { render() {
const { const {
height, height,
......
...@@ -139,8 +139,17 @@ export default class Pie extends Component { ...@@ -139,8 +139,17 @@ export default class Pie extends Component {
[styles.legendBlock]: legendBlock, [styles.legendBlock]: legendBlock,
}); });
const {
data: propsData,
selected: propsSelected = true,
tooltip: propsTooltip = true,
} = this.props;
let data = propsData || [];
let selected = propsSelected;
let tooltip = propsTooltip;
const defaultColors = colors; const defaultColors = colors;
let { data, selected, tooltip } = this.props;
data = data || []; data = data || [];
selected = selected || true; selected = selected || true;
tooltip = tooltip || true; tooltip = tooltip || true;
......
...@@ -16,7 +16,8 @@ export default class Radar extends Component { ...@@ -16,7 +16,8 @@ export default class Radar extends Component {
} }
componentDidUpdate(preProps) { componentDidUpdate(preProps) {
if (this.props.data !== preProps.data) { const { data } = this.props;
if (data !== preProps.data) {
this.getLegendData(); this.getLegendData();
} }
} }
......
...@@ -27,7 +27,8 @@ class TagCloud extends Component { ...@@ -27,7 +27,8 @@ class TagCloud extends Component {
} }
componentDidUpdate(preProps) { componentDidUpdate(preProps) {
if (JSON.stringify(preProps.data) !== JSON.stringify(this.props.data)) { const { data } = this.props;
if (JSON.stringify(preProps.data) !== JSON.stringify(data)) {
this.renderChart(this.props); this.renderChart(this.props);
} }
} }
......
...@@ -87,8 +87,8 @@ export default class HeaderSearch extends PureComponent { ...@@ -87,8 +87,8 @@ export default class HeaderSearch extends PureComponent {
render() { render() {
const { className, placeholder, ...restProps } = this.props; const { className, placeholder, ...restProps } = this.props;
delete restProps.defaultOpen; // for rc-select not affected
const { searchMode, value } = this.state; const { searchMode, value } = this.state;
delete restProps.defaultOpen; // for rc-select not affected
const inputClass = classNames(styles.input, { const inputClass = classNames(styles.input, {
[styles.show]: searchMode, [styles.show]: searchMode,
}); });
......
...@@ -74,8 +74,8 @@ class Login extends Component { ...@@ -74,8 +74,8 @@ class Login extends Component {
handleSubmit = e => { handleSubmit = e => {
e.preventDefault(); e.preventDefault();
const { active, type } = this.state; const { active, type } = this.state;
const activeFileds = active[type];
const { form, onSubmit } = this.props; const { form, onSubmit } = this.props;
const activeFileds = active[type];
form.validateFields(activeFileds, { force: true }, (err, values) => { form.validateFields(activeFileds, { force: true }, (err, values) => {
onSubmit(err, values); onSubmit(err, values);
}); });
......
...@@ -180,6 +180,7 @@ export default class PageHeader extends PureComponent { ...@@ -180,6 +180,7 @@ export default class PageHeader extends PureComponent {
tabBarExtraContent, tabBarExtraContent,
loading = false, loading = false,
} = this.props; } = this.props;
const { breadcrumb } = this.state;
const clsString = classNames(styles.pageHeader, className); const clsString = classNames(styles.pageHeader, className);
const activeKeyProps = {}; const activeKeyProps = {};
...@@ -189,7 +190,6 @@ export default class PageHeader extends PureComponent { ...@@ -189,7 +190,6 @@ export default class PageHeader extends PureComponent {
if (tabActiveKey !== undefined) { if (tabActiveKey !== undefined) {
activeKeyProps.activeKey = tabActiveKey; activeKeyProps.activeKey = tabActiveKey;
} }
const { breadcrumb } = this.state;
return ( return (
<Card className={clsString} bodyStyle={{ padding: 0 }} loading={loading}> <Card className={clsString} bodyStyle={{ padding: 0 }} loading={loading}>
{breadcrumb} {breadcrumb}
......
...@@ -6,13 +6,14 @@ import styles from './index.less'; ...@@ -6,13 +6,14 @@ import styles from './index.less';
export default class TopNavHeader extends PureComponent { export default class TopNavHeader extends PureComponent {
render() { render() {
const { theme, grid, logo } = this.props;
return ( return (
<div className={`${styles.head} ${this.props.theme === 'light' ? styles.light : ''}`}> <div className={`${styles.head} ${theme === 'light' ? styles.light : ''}`}>
<div className={`${styles.main} ${this.props.grid === 'Wide' ? styles.wide : ''}`}> <div className={`${styles.main} ${grid === 'Wide' ? styles.wide : ''}`}>
<div className={styles.left}> <div className={styles.left}>
<div className={styles.logo} key="logo" id="logo"> <div className={styles.logo} key="logo" id="logo">
<Link to="/"> <Link to="/">
<img src={this.props.logo} alt="logo" /> <img src={logo} alt="logo" />
<h1>Ant Design Pro</h1> <h1>Ant Design Pro</h1>
</Link> </Link>
</div> </div>
......
...@@ -17,6 +17,9 @@ describe('Login', () => { ...@@ -17,6 +17,9 @@ describe('Login', () => {
afterEach(() => page.close()); afterEach(() => page.close());
it('should login with failure', async () => { it('should login with failure', async () => {
await page.waitForSelector('#userName', {
timeout: 2000,
});
await page.type('#userName', 'mockuser'); await page.type('#userName', 'mockuser');
await page.type('#password', 'wrong_password'); await page.type('#password', 'wrong_password');
await page.click('button[type="submit"]'); await page.click('button[type="submit"]');
...@@ -24,6 +27,9 @@ describe('Login', () => { ...@@ -24,6 +27,9 @@ describe('Login', () => {
}); });
it('should login successfully', async () => { it('should login successfully', async () => {
await page.waitForSelector('#userName', {
timeout: 2000,
});
await page.type('#userName', 'admin'); await page.type('#userName', 'admin');
await page.type('#password', '888888'); await page.type('#password', '888888');
await page.click('button[type="submit"]'); await page.click('button[type="submit"]');
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ant Design Pro</title> <title>Ant Design Pro</title>
<link rel="icon" href="/favicon.png" type="image/x-icon"> <link rel="icon" href="/favicon.png" type="image/x-icon">
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="https://gw.alipayobjects.com/os/rmsportal/nGVBgVyXzzmbAqevIAPy.js"> <script src="https://gw.alipayobjects.com/os/rmsportal/nGVBgVyXzzmbAqevIAPy.js">
</script> </script>
<script src=" https://gw.alipayobjects.com/os/rmsportal/TKSqiyoUxzrHoMwjViwA.js "></script> <script src=" https://gw.alipayobjects.com/os/rmsportal/TKSqiyoUxzrHoMwjViwA.js "></script>
......
import '@babel/polyfill'; import './polyfill';
import 'url-polyfill';
import dva from 'dva'; import dva from 'dva';
import createHistory from 'history/createHashHistory'; import createHistory from 'history/createHashHistory';
......
...@@ -56,6 +56,10 @@ const query = { ...@@ -56,6 +56,10 @@ const query = {
}, },
'screen-xl': { 'screen-xl': {
minWidth: 1200, minWidth: 1200,
maxWidth: 1599,
},
'screen-xxl': {
minWidth: 1600,
}, },
}; };
......
...@@ -4,11 +4,12 @@ import styles from './GridContent.less'; ...@@ -4,11 +4,12 @@ import styles from './GridContent.less';
class GridContent extends PureComponent { class GridContent extends PureComponent {
render() { render() {
const { grid, children } = this.props;
let className = `${styles.main}`; let className = `${styles.main}`;
if (this.props.grid === 'Wide') { if (grid === 'Wide') {
className = `${styles.main} ${styles.wide}`; className = `${styles.main} ${styles.wide}`;
} }
return <div className={className}>{this.props.children}</div>; return <div className={className}>{children}</div>;
} }
} }
......
...@@ -5,7 +5,7 @@ import { Icon } from 'antd'; ...@@ -5,7 +5,7 @@ import { Icon } from 'antd';
import GlobalFooter from '../components/GlobalFooter'; import GlobalFooter from '../components/GlobalFooter';
import styles from './UserLayout.less'; import styles from './UserLayout.less';
import logo from '../assets/logo.svg'; import logo from '../assets/logo.svg';
import { getRoutes } from '../utils/utils'; import { getRoutes, getPageQuery, getQueryPath } from '../utils/utils';
const links = [ const links = [
{ {
...@@ -31,6 +31,14 @@ const copyright = ( ...@@ -31,6 +31,14 @@ const copyright = (
</Fragment> </Fragment>
); );
function getLoginPathWithRedirectPath() {
const params = getPageQuery();
const { redirect } = params;
return getQueryPath('/user/login', {
redirect,
});
}
class UserLayout extends React.PureComponent { class UserLayout extends React.PureComponent {
getPageTitle() { getPageTitle() {
const { routerData, location } = this.props; const { routerData, location } = this.props;
...@@ -66,7 +74,7 @@ class UserLayout extends React.PureComponent { ...@@ -66,7 +74,7 @@ class UserLayout extends React.PureComponent {
exact={item.exact} exact={item.exact}
/> />
))} ))}
<Redirect exact from="/user" to="/user/login" /> <Redirect from="/user" to={getLoginPathWithRedirectPath()} />
</Switch> </Switch>
</div> </div>
<GlobalFooter links={links} copyright={copyright} /> <GlobalFooter links={links} copyright={copyright} />
......
import { routerRedux } from 'dva/router'; import { routerRedux } from 'dva/router';
import { stringify } from 'qs';
import { fakeAccountLogin, getFakeCaptcha } from '../services/api'; import { fakeAccountLogin, getFakeCaptcha } from '../services/api';
import { setAuthority } from '../utils/authority'; import { setAuthority } from '../utils/authority';
import { getPageQuery } from '../utils/utils';
import { reloadAuthorized } from '../utils/Authorized'; import { reloadAuthorized } from '../utils/Authorized';
export default { export default {
...@@ -20,18 +22,28 @@ export default { ...@@ -20,18 +22,28 @@ export default {
// Login successfully // Login successfully
if (response.status === 'ok') { if (response.status === 'ok') {
reloadAuthorized(); reloadAuthorized();
yield put(routerRedux.push('/')); 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.startsWith('/#')) {
redirect = redirect.substr(2);
}
} else {
window.location.href = redirect;
return;
}
}
yield put(routerRedux.replace(redirect || '/'));
} }
}, },
*logout(_, { put, select }) { *getCaptcha({ payload }, { call }) {
try { yield call(getFakeCaptcha, payload);
// get location pathname },
const urlParams = new URL(window.location.href); *logout(_, { put }) {
const pathname = yield select(state => state.routing.location.pathname);
// add the parameters in the url
urlParams.searchParams.set('redirect', pathname);
window.history.replaceState(null, 'login', urlParams.href);
} finally {
yield put({ yield put({
type: 'changeLoginStatus', type: 'changeLoginStatus',
payload: { payload: {
...@@ -40,11 +52,14 @@ export default { ...@@ -40,11 +52,14 @@ export default {
}, },
}); });
reloadAuthorized(); reloadAuthorized();
yield put(routerRedux.push('/user/login')); yield put(
} routerRedux.push({
}, pathname: '/user/login',
*getCaptcha({ payload }, { call }) { search: stringify({
yield call(getFakeCaptcha, payload); redirect: window.location.href,
}),
})
);
}, },
}, },
......
import '@babel/polyfill';
import 'url-polyfill';
import setprototypeof from 'setprototypeof';
// React depends on set/map/requestAnimationFrame
// https://reactjs.org/docs/javascript-environment-requirements.html
// import 'core-js/es6/set';
// import 'core-js/es6/map';
// import 'raf/polyfill'; 只兼容到IE10不需要,况且fetch的polyfill whatwg-fetch也只兼容到IE10
// https://github.com/umijs/umi/issues/413
Object.setPrototypeOf = setprototypeof;
...@@ -5,6 +5,7 @@ import zhCN from 'antd/lib/locale-provider/zh_CN'; ...@@ -5,6 +5,7 @@ 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 Authorized from './utils/Authorized';
import { getQueryPath } from './utils/utils';
import styles from './index.less'; import styles from './index.less';
const { ConnectedRouter } = routerRedux; const { ConnectedRouter } = routerRedux;
...@@ -26,7 +27,9 @@ function RouterConfig({ history, app }) { ...@@ -26,7 +27,9 @@ function RouterConfig({ history, app }) {
path="/" path="/"
render={props => <BasicLayout {...props} />} render={props => <BasicLayout {...props} />}
authority={['admin', 'user']} authority={['admin', 'user']}
redirectPath="/user/login" redirectPath={getQueryPath('/user/login', {
redirect: window.location.href,
})}
/> />
</Switch> </Switch>
</ConnectedRouter> </ConnectedRouter>
......
...@@ -73,11 +73,13 @@ class AdvancedForm extends PureComponent { ...@@ -73,11 +73,13 @@ class AdvancedForm extends PureComponent {
resizeFooterToolbar = () => { resizeFooterToolbar = () => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
const sider = document.querySelectorAll('.ant-layout-sider')[0]; const sider = document.querySelectorAll('.ant-layout-sider')[0];
if (sider) {
const width = `calc(100% - ${sider.style.width})`; const width = `calc(100% - ${sider.style.width})`;
const { width: stateWidth } = this.state; const { width: stateWidth } = this.state;
if (stateWidth !== width) { if (stateWidth !== width) {
this.setState({ width }); this.setState({ width });
} }
}
}); });
}; };
......
// use localStorage to store the authority info, which might be sent from server in actual project. // use localStorage to store the authority info, which might be sent from server in actual project.
export function getAuthority() { export function getAuthority() {
// return localStorage.getItem('antd-pro-authority') || ['admin', 'user'];
return localStorage.getItem('antd-pro-authority') || 'admin'; return localStorage.getItem('antd-pro-authority') || 'admin';
} }
......
import moment from 'moment'; import moment from 'moment';
import React from 'react'; import React from 'react';
import { parse, stringify } from 'qs';
export function fixedZero(val) { export function fixedZero(val) {
return val * 1 < 10 ? `0${val}` : val; return val * 1 < 10 ? `0${val}` : val;
...@@ -162,6 +163,18 @@ export function getRoutes(path, routerData) { ...@@ -162,6 +163,18 @@ export function getRoutes(path, routerData) {
return renderRoutes; return renderRoutes;
} }
export function getPageQuery() {
return parse(window.location.href.split('?')[1]);
}
export function getQueryPath(path = '', query = {}) {
const search = stringify(query);
if (search.length) {
return `${path}?${search}`;
}
return path;
}
/* eslint no-useless-escape:0 */ /* eslint no-useless-escape:0 */
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
......
#!/bin/bash
sudo apt-get update
sudo apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
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