Commit 7c729cdc authored by 陈帅's avatar 陈帅

Merge branch 'master' into v2

parents ff2fc1a8 494a4681
......@@ -8,7 +8,7 @@ _roadhog-api-doc
# production
/dist
/vscode
/.vscode
# misc
.DS_Store
......@@ -20,5 +20,11 @@ yarn-error.log
yarn.lock
package-lock.json
*bak
<<<<<<< HEAD
jsconfig.json
.vscode
=======
# visual studio code
.history
>>>>>>> master
......@@ -2,21 +2,17 @@ const path = require('path');
export default {
entry: 'src/index.js',
extraBabelPlugins: [
[
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
},
],
],
extraBabelPlugins: [['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }]],
env: {
development: {
extraBabelPlugins: ['dva-hmr'],
},
},
externals: {
'@antv/data-set': 'DataSet',
bizcharts: 'BizCharts',
rollbar: 'rollbar',
},
alias: {
components: path.resolve(__dirname, 'src/components/'),
},
......
......@@ -2,7 +2,11 @@ English | [简体中文](./README.zh-CN.md)
# Ant Design Pro
[![](https://img.shields.io/travis/ant-design/ant-design-pro/master.svg?style=flat-square)](https://travis-ci.org/ant-design/ant-design-pro) [![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master) [![Gitter](https://badges.gitter.im/ant-design/ant-design-pro.svg)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![](https://img.shields.io/travis/ant-design/ant-design-pro/master.svg?style=flat-square)](https://travis-ci.org/ant-design/ant-design-pro)
[![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro#info=devDependencies&view=list)
[![Gitter](https://badges.gitter.im/ant-design/ant-design-pro.svg)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
An out-of-box UI solution for enterprise applications as a React boilerplate.
......
......@@ -2,7 +2,11 @@
# Ant Design Pro
[![](https://img.shields.io/travis/ant-design/ant-design-pro.svg?style=flat-square)](https://travis-ci.org/ant-design/ant-design-pro) [![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master) [![Gitter](https://badges.gitter.im/ant-design/ant-design-pro.svg)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![](https://img.shields.io/travis/ant-design/ant-design-pro/master.svg?style=flat-square)](https://travis-ci.org/ant-design/ant-design-pro)
[![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro#info=devDependencies&view=list)
[![Gitter](https://badges.gitter.im/ant-design/ant-design-pro.svg)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
开箱即用的中台前端/设计解决方案。
......
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"components/*": ["./src/components/*"]
}
}
}
import React from 'react';
import PromiseRender from './PromiseRender';
import { CURRENT } from './index';
import { CURRENT } from './renderAuthorize';
function isPromise(obj) {
return (
......@@ -49,6 +49,10 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => {
if (typeof authority === 'function') {
try {
const bool = authority(currentAuthority);
// 函数执行后返回值是 Promise
if (isPromise(bool)) {
return <PromiseRender ok={target} error={Exception} promise={bool} />;
}
if (bool) {
return target;
}
......
......@@ -2,31 +2,10 @@ 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';
import renderAuthorize from './renderAuthorize';
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;
export default renderAuthorize(Authorized);
......@@ -25,14 +25,14 @@ order: 15
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| children | 正常渲染的元素,权限判断通过时展示 | ReactNode | - |
| authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean` | - |
| authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean | Promise` | - |
| noMatch | 权限异常渲染元素,权限判断不通过时展示 | ReactNode | - |
### Authorized.AuthorizedRoute
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean` | - |
| authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean | Promise` | - |
| redirectPath | 权限异常时重定向的页面路由 | string | - |
其余参数与 `Route` 相同。
......@@ -43,16 +43,16 @@ order: 15
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean` | - |
| authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean | Promise` | - |
| error | 权限异常时渲染元素 | ReactNode | <Exception type="403" /> |
### Authorized.check
函数形式的 Authorized,用于某些不能被 HOC 包裹的组件。 `Authorized.check(authority, target, Exception)`
函数形式的 Authorized,用于某些不能被 HOC 包裹的组件。 `Authorized.check(authority, target, Exception)`
注意:传入一个 Promise 时,无论正确还是错误返回的都是一个 ReactClass。
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean` | - |
| authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean | Promise` | - |
| target | 权限判断通过时渲染的元素 | ReactNode | - |
| Exception | 权限异常时渲染元素 | ReactNode | - |
/* eslint-disable import/no-mutable-exports */
let CURRENT = 'NULL';
/**
* use authority or getAuthority
* @param {string|()=>String} currentAuthority
*/
const renderAuthorize = Authorized => {
return 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 Authorized => renderAuthorize(Authorized);
......@@ -47,7 +47,7 @@ export default class WaterWave extends PureComponent {
const data = percent / 100;
const self = this;
if (!this.node || !data) {
if (!this.node || (data !== 0 && !data)) {
return;
}
......@@ -196,7 +196,10 @@ export default class WaterWave extends PureComponent {
</div>
<div className={styles.text} style={{ width: height }}>
{title && <span>{title}</span>}
<h4>{percent}%</h4>
<h4>
{percent}
%
</h4>
</div>
</div>
);
......
......@@ -73,7 +73,11 @@ class CountDown extends Component {
const s = Math.floor((time - h * hours - m * minutes) / 1000);
return (
<span>
{fixedZero(h)}:{fixedZero(m)}:{fixedZero(s)}
{fixedZero(h)}
:
{fixedZero(m)}
:
{fixedZero(s)}
</span>
);
};
......
......@@ -10,7 +10,8 @@ const Description = ({ term, column, className, children, ...restProps }) => {
return (
<Col className={clsString} {...responsive[column]} {...restProps}>
{term && <div className={styles.term}>{term}</div>}
{children && <div className={styles.detail}>{children}</div>}
{children !== null &&
children !== undefined && <div className={styles.detail}>{children}</div>}
</Col>
);
};
......
......@@ -5,6 +5,7 @@ export interface IEllipsisProps {
lines?: number;
style?: React.CSSProperties;
className?: string;
fullWidthRecognition?: boolean;
}
export default class Ellipsis extends React.Component<IEllipsisProps, any> {}
......@@ -13,3 +13,4 @@ Property | Description | Type | Default
tooltip | tooltip for showing the full text content when hovering over | boolean | -
length | maximum number of characters in the text before being truncated | number | -
lines | maximum number of rows in the text before being truncated | number | `1`
fullWidthRecognition | whether consider full-width character length as 2 when calculate string length | boolean | -
......@@ -8,11 +8,40 @@ import styles from './index.less';
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
const EllipsisText = ({ text, length, tooltip, ...other }) => {
export const getStrFullLength = (str = '') => {
return str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0);
if (charCode >= 0 && charCode <= 128) {
return pre + 1;
} else {
return pre + 2;
}
}, 0);
};
export const cutStrByFullLength = (str = '', maxLength) => {
let showLength = 0;
return str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0);
if (charCode >= 0 && charCode <= 128) {
showLength += 1;
} else {
showLength += 2;
}
if (showLength <= maxLength) {
return pre + cur;
} else {
return pre;
}
}, '');
};
const EllipsisText = ({ text, length, tooltip, fullWidthRecognition, ...other }) => {
if (typeof text !== 'string') {
throw new Error('Ellipsis children must be string.');
}
if (text.length <= length || length < 0) {
const textLength = fullWidthRecognition ? getStrFullLength(text) : text.length;
if (textLength <= length || length < 0) {
return <span {...other}>{text}</span>;
}
const tail = '...';
......@@ -20,7 +49,7 @@ const EllipsisText = ({ text, length, tooltip, ...other }) => {
if (length - tail.length <= 0) {
displayText = '';
} else {
displayText = text.slice(0, length);
displayText = fullWidthRecognition ? cutStrByFullLength(text, length) : text.slice(0, length);
}
if (tooltip) {
......@@ -55,7 +84,8 @@ export default class Ellipsis extends Component {
}
componentDidUpdate(perProps) {
if (this.props.lines !== perProps.lines) {
const { lines } = this.props;
if (lines !== perProps.lines) {
this.computeLine();
}
}
......@@ -80,7 +110,7 @@ export default class Ellipsis extends Component {
// bisection
const len = text.length;
const mid = Math.floor(len / 2);
const mid = Math.ceil(len / 2);
const count = this.bisection(targetHeight, mid, 0, len, text, shadowNode);
......@@ -147,7 +177,15 @@ export default class Ellipsis extends Component {
render() {
const { text, targetCount } = this.state;
const { children, lines, length, className, tooltip, ...restProps } = this.props;
const {
children,
lines,
length,
className,
tooltip,
fullWidthRecognition,
...restProps
} = this.props;
const cls = classNames(styles.ellipsis, className, {
[styles.lines]: lines && !isSupportLineClamp,
......@@ -170,6 +208,7 @@ export default class Ellipsis extends Component {
length={length}
text={children || ''}
tooltip={tooltip}
fullWidthRecognition={fullWidthRecognition}
{...restProps}
/>
);
......
import { getStrFullLength, cutStrByFullLength } from './index.js';
describe('test calculateShowLength', () => {
it('get full length', () => {
expect(getStrFullLength('一二,a,')).toEqual(8);
});
it('cut str by full length', () => {
expect(cutStrByFullLength('一二,a,', 7)).toEqual('一二,a');
});
it('cut str when length small', () => {
expect(cutStrByFullLength('一22三', 5)).toEqual('一22');
});
});
......@@ -14,3 +14,4 @@ order: 10
tooltip | 移动到文本展示完整内容的提示 | boolean | -
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -
lines | 在按照行数截取下最大的行数,超过则截取省略 | number | `1`
fullWidthRecognition | 是否将全角字符的长度视为2来计算字符串长度 | boolean | -
......@@ -6,6 +6,7 @@ export interface IHeaderSearchProps {
onChange?: (value: string) => void;
onPressEnter?: (value: string) => void;
style?: React.CSSProperties;
className?: string;
}
export default class HeaderSearch extends React.Component<IHeaderSearchProps, any> {}
......@@ -2,6 +2,8 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { Input, Icon, AutoComplete } from 'antd';
import classNames from 'classnames';
import Debounce from 'lodash-decorators/debounce';
import Bind from 'lodash-decorators/bind';
import styles from './index.less';
export default class HeaderSearch extends PureComponent {
......@@ -71,6 +73,18 @@ export default class HeaderSearch extends PureComponent {
});
};
// NOTE: 不能小于500,如果长按某键,第一次触发auto repeat的间隔是500ms,小于500会导致触发2次
@Bind()
@Debounce(500, {
leading: true,
trailing: false,
})
debouncePressEnter() {
const { onPressEnter } = this.props;
const value = this.state;
onPressEnter(value);
}
render() {
const { className, placeholder, ...restProps } = this.props;
delete restProps.defaultOpen; // for rc-select not affected
......
......@@ -63,15 +63,20 @@
}
.title,
.content {
flex: auto;
}
.action,
.content,
.extraContent,
.main {
flex: 0 1 auto;
}
.main {
width: 100%;
}
.title,
.action {
margin-bottom: 16px;
......
......@@ -19,26 +19,27 @@ const getFlatMenuKeys = menuData => {
return keys;
};
const SiderMenuWrapper = props =>
props.isMobile ? (
const SiderMenuWrapper = props => {
const { isMobile, menuData, collapsed } = props;
return isMobile ? (
<DrawerMenu
parent={null}
getContainer={null}
level={null}
handleChild={null}
open={!props.collapsed}
open={!collapsed}
onMaskClick={() => {
props.onCollapse(true);
}}
width="256px"
>
<SiderMenu
{...props}
flatMenuKeys={getFlatMenuKeys(props.menuData)}
collapsed={props.isMobile ? false : props.collapsed}
flatMenuKeys={getFlatMenuKeys(menuData)}
collapsed={isMobile ? false : collapsed}
/>
</DrawerMenu>
) : (
<SiderMenu {...props} flatMenuKeys={getFlatMenuKeys(props.menuData)} />
<SiderMenu {...props} flatMenuKeys={getFlatMenuKeys(menuData)} />
);
};
export default SiderMenuWrapper;
......@@ -97,7 +97,8 @@ class StandardTable extends PureComponent {
已选择 <a style={{ fontWeight: 600 }}>{selectedRowKeys.length}</a> 项&nbsp;&nbsp;
{needTotalList.map(item => (
<span style={{ marginLeft: 8 }} key={item.dataIndex}>
{item.title}总计&nbsp;
{item.title}
总计&nbsp;
<span style={{ fontWeight: 600 }}>
{item.render ? item.render(item.total) : item.total}
</span>
......
......@@ -11,7 +11,6 @@
</script>
<script src=" https://gw.alipayobjects.com/os/rmsportal/TKSqiyoUxzrHoMwjViwA.js "></script>
<script src="https://gw.alipayobjects.com/os/antv/assets/data-set/0.8.7/data-set.min.js"></script>
</head>
<body>
......
......@@ -10,8 +10,8 @@ export default {
},
effects: {
*submit(_, { call, put }) {
const response = yield call(fakeRegister);
*submit({ payload }, { call, put }) {
const response = yield call(fakeRegister, payload);
yield put({
type: 'registerHandle',
payload: response,
......
......@@ -35,7 +35,7 @@ export default {
saveCurrentUser(state, action) {
return {
...state,
currentUser: action.payload,
currentUser: action.payload || {},
};
},
changeNotifyCount(state, action) {
......
......@@ -144,7 +144,8 @@ export default class Workplace extends PureComponent {
<div className={styles.statItem}>
<p>团队内排名</p>
<p>
8<span> / 24</span>
8
<span> / 24</span>
</p>
</div>
<div className={styles.statItem}>
......
......@@ -138,7 +138,8 @@ export default class BasicForms extends PureComponent {
{...formItemLayout}
label={
<span>
邀评人<em className={styles.optional}>选填</em>
邀评人
<em className={styles.optional}>选填</em>
</span>
}
>
......@@ -150,7 +151,8 @@ export default class BasicForms extends PureComponent {
{...formItemLayout}
label={
<span>
权重<em className={styles.optional}>选填</em>
权重
<em className={styles.optional}>选填</em>
</span>
}
>
......
......@@ -55,7 +55,11 @@ class Step2 extends React.PureComponent {
</Form.Item>
<Form.Item {...formItemLayout} className={styles.stepFormText} label="转账金额">
<span className={styles.money}>{data.amount}</span>
<span className={styles.uppercase}>{digitUppercase(data.amount)}</span>
<span className={styles.uppercase}>
{digitUppercase(data.amount)}
</span>
</Form.Item>
<Divider style={{ margin: '24px 0' }} />
<Form.Item {...formItemLayout} label="支付密码" required={false}>
......
......@@ -82,7 +82,8 @@ export default class SearchList extends Component {
<div className={styles.description}>{content}</div>
<div className={styles.extra}>
<Avatar src={avatar} size="small" />
<a href={href}>{owner}</a> 发布在 <a href={href}>{href}</a>
<a href={href}>{owner}</a> 发布
<a href={href}>{href}</a>
<em>{moment(updatedAt).format('YYYY-MM-DD HH:mm')}</em>
</div>
</div>
......
......@@ -226,7 +226,7 @@ export default class AdvancedProfile extends Component {
}
render() {
const { stepDirection } = this.state;
const { stepDirection, operationkey } = this.state;
const { profile, loading } = this.props;
const { advancedOperation1, advancedOperation2, advancedOperation3 } = profile;
const contentList = {
......@@ -333,7 +333,8 @@ export default class AdvancedProfile extends Component {
</Card>
<Card title="用户近半年来电记录" style={{ marginBottom: 24 }} bordered={false}>
<div className={styles.noData}>
<Icon type="frown-o" />暂无数据
<Icon type="frown-o" />
暂无数据
</div>
</Card>
<Card
......@@ -342,7 +343,7 @@ export default class AdvancedProfile extends Component {
tabList={operationTabList}
onTabChange={this.onOperationTabChange}
>
{contentList[this.state.operationkey]}
{contentList[operationkey]}
</Card>
</PageHeaderLayout>
);
......
......@@ -16,13 +16,15 @@ const extra = (
您提交的内容有如下错误
</div>
<div style={{ marginBottom: 16 }}>
<Icon style={{ color: '#f5222d', marginRight: 8 }} type="close-circle-o" />您的账户已被冻结
<Icon style={{ color: '#f5222d', marginRight: 8 }} type="close-circle-o" />
您的账户已被冻结
<a style={{ marginLeft: 16 }}>
立即解冻 <Icon type="right" />
</a>
</div>
<div>
<Icon style={{ color: '#f5222d', marginRight: 8 }} type="close-circle-o" />您的账户还不具备申请资格
<Icon style={{ color: '#f5222d', marginRight: 8 }} type="close-circle-o" />
您的账户还不具备申请资格
<a style={{ marginLeft: 16 }}>
立即升级 <Icon type="right" />
</a>
......
......@@ -15,7 +15,8 @@ const desc1 = (
}}
>
<div style={{ margin: '8px 0 4px' }}>
曲丽丽<Icon style={{ marginLeft: 8 }} type="dingding-o" />
曲丽丽
<Icon style={{ marginLeft: 8 }} type="dingding-o" />
</div>
<div>2016-12-12 12:32</div>
</div>
......@@ -24,7 +25,8 @@ const desc1 = (
const desc2 = (
<div style={{ fontSize: 12, position: 'relative', left: 42 }}>
<div style={{ margin: '8px 0 4px' }}>
周毛毛<Icon type="dingding-o" style={{ color: '#00A0E9', marginLeft: 8 }} />
周毛毛
<Icon type="dingding-o" style={{ color: '#00A0E9', marginLeft: 8 }} />
</div>
<div>
<a href="">催一下</a>
......
......@@ -23,7 +23,8 @@ const RegisterResult = ({ location }) => (
type="success"
title={
<div className={styles.title}>
你的账户{location.state ? location.state.account : 'AntDesign@example.com'} 注册成功
你的账户
{location.state ? location.state.account : 'AntDesign@example.com'} 注册成功
</div>
}
description="激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。"
......
......@@ -47,7 +47,11 @@ export default function request(url, options) {
credentials: 'include',
};
const newOptions = { ...defaultOptions, ...options };
if (newOptions.method === 'POST' || newOptions.method === 'PUT') {
if (
newOptions.method === 'POST' ||
newOptions.method === 'PUT' ||
newOptions.method === 'DELETE'
) {
if (!(newOptions.body instanceof FormData)) {
newOptions.headers = {
Accept: 'application/json',
......
......@@ -124,11 +124,10 @@ function getRenderArr(routes) {
let renderArr = [];
renderArr.push(routes[0]);
for (let i = 1; i < routes.length; i += 1) {
let isAdd = false;
// 是否包含
isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);
// 去重
renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1);
// 是否包含
const isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);
if (isAdd) {
renderArr.push(routes[i]);
}
......@@ -164,7 +163,7 @@ export function getRoutes(path, routerData) {
}
/* eslint no-useless-escape:0 */
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/g;
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
export function isUrl(path) {
return reg.test(path);
......
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