;
7 |
8 | export type IReactComponent =
9 | | React.StatelessComponent
10 | | React.ComponentClass
11 | | React.ClassicComponentClass
;
12 |
13 | interface Secured {
14 | (authority: authority, error?: React.ReactNode): (target: T) => T;
15 | }
16 |
17 | export interface AuthorizedRouteProps extends RouteProps {
18 | authority: authority;
19 | }
20 | export class AuthorizedRoute extends React.Component {}
21 |
22 | interface check {
23 | (
24 | authority: authority,
25 | target: T,
26 | Exception: S
27 | ): T | S;
28 | }
29 |
30 | interface AuthorizedProps {
31 | authority: authority;
32 | noMatch?: React.ReactNode;
33 | }
34 |
35 | export class Authorized extends React.Component {
36 | static Secured: Secured;
37 | static AuthorizedRoute: typeof AuthorizedRoute;
38 | static check: check;
39 | }
40 |
41 | declare function renderAuthorize(currentAuthority: string): typeof Authorized;
42 |
43 | export default renderAuthorize;
44 |
--------------------------------------------------------------------------------
/src/components/Authorized/index.js:
--------------------------------------------------------------------------------
1 | import Authorized from './Authorized';
2 | import AuthorizedRoute from './AuthorizedRoute';
3 | import Secured from './Secured';
4 | import check from './CheckPermissions.js';
5 |
6 | /* eslint-disable import/no-mutable-exports */
7 | let CURRENT = 'NULL';
8 |
9 | Authorized.Secured = Secured;
10 | Authorized.AuthorizedRoute = AuthorizedRoute;
11 | Authorized.check = check;
12 |
13 | /**
14 | * use authority or getAuthority
15 | * @param {string|()=>String} currentAuthority
16 | */
17 | const renderAuthorize = currentAuthority => {
18 | if (currentAuthority) {
19 | if (currentAuthority.constructor.name === 'Function') {
20 | CURRENT = currentAuthority();
21 | }
22 | if (currentAuthority.constructor.name === 'String') {
23 | CURRENT = currentAuthority;
24 | }
25 | } else {
26 | CURRENT = 'NULL';
27 | }
28 | return Authorized;
29 | };
30 |
31 | export { CURRENT };
32 | export default renderAuthorize;
33 |
--------------------------------------------------------------------------------
/src/components/Authorized/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: Authorized
4 | zh-CN: Authorized
5 | subtitle: 权限
6 | cols: 1
7 | order: 15
8 | ---
9 |
10 | 权限组件,通过比对现有权限与准入权限,决定相关元素的展示。
11 |
12 | ## API
13 |
14 | ### RenderAuthorized
15 |
16 | `RenderAuthorized: (currentAuthority: string | () => string) => Authorized`
17 |
18 | 权限组件默认 export RenderAuthorized 函数,它接收当前权限作为参数,返回一个权限对象,该对象提供以下几种使用方式。
19 |
20 |
21 | ### Authorized
22 |
23 | 最基础的权限控制。
24 |
25 | | 参数 | 说明 | 类型 | 默认值 |
26 | |----------|------------------------------------------|-------------|-------|
27 | | children | 正常渲染的元素,权限判断通过时展示 | ReactNode | - |
28 | | authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean` | - |
29 | | noMatch | 权限异常渲染元素,权限判断不通过时展示 | ReactNode | - |
30 |
31 | ### Authorized.AuthorizedRoute
32 |
33 | | 参数 | 说明 | 类型 | 默认值 |
34 | |----------|------------------------------------------|-------------|-------|
35 | | authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean` | - |
36 | | redirectPath | 权限异常时重定向的页面路由 | string | - |
37 |
38 | 其余参数与 `Route` 相同。
39 |
40 | ### Authorized.Secured
41 |
42 | 注解方式,`@Authorized.Secured(authority, error)`
43 |
44 | | 参数 | 说明 | 类型 | 默认值 |
45 | |----------|------------------------------------------|-------------|-------|
46 | | authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean` | - |
47 | | error | 权限异常时渲染元素 | ReactNode | |
48 |
49 | ### Authorized.check
50 |
51 | 函数形式的 Authorized,用于某些不能被 HOC 包裹的组件。 `Authorized.check(authority, target, Exception)`
52 | 注意:传入一个 Promise 时,无论正确还是错误返回的都是一个 ReactClass。
53 |
54 | | 参数 | 说明 | 类型 | 默认值 |
55 | |----------|------------------------------------------|-------------|-------|
56 | | authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean` | - |
57 | | target | 权限判断通过时渲染的元素 | ReactNode | - |
58 | | Exception | 权限异常时渲染元素 | ReactNode | - |
59 |
--------------------------------------------------------------------------------
/src/components/AvatarList/AvatarItem.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IAvatarItemProps {
3 | tips: React.ReactNode;
4 | src: string;
5 | style?: React.CSSProperties;
6 | }
7 |
8 | export default class AvatarItem extends React.Component {
9 | constructor(props: IAvatarItemProps);
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/AvatarList/demo/simple.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title:
4 | zh-CN: 基础样例
5 | en-US: Basic Usage
6 | ---
7 |
8 | Simplest of usage.
9 |
10 | ````jsx
11 | import AvatarList from 'ant-design-pro/lib/AvatarList';
12 |
13 | ReactDOM.render(
14 |
15 |
16 |
17 |
18 |
19 | , mountNode);
20 | ````
21 |
--------------------------------------------------------------------------------
/src/components/AvatarList/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import AvatarItem from './AvatarItem';
3 |
4 | export interface IAvatarListProps {
5 | size?: 'large' | 'small' | 'mini' | 'default';
6 | style?: React.CSSProperties;
7 | children: React.ReactElement | Array>;
8 | }
9 |
10 | export default class AvatarList extends React.Component {
11 | public static Item: typeof AvatarItem;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/AvatarList/index.en-US.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: AvatarList
3 | order: 1
4 | cols: 1
5 | ---
6 |
7 | A list of user's avatar for project or group member list frequently. If a large or small AvatarList is desired, set the `size` property to either `large` or `small` and `mini` respectively. Omit the `size` property for a AvatarList with the default size.
8 |
9 | ## API
10 |
11 | ### AvatarList
12 |
13 | | Property | Description | Type | Default |
14 | |----------|------------------------------------------|-------------|-------|
15 | | size | size of list | `large`、`small` 、`mini`, `default` | `default` |
16 |
17 | ### AvatarList.Item
18 |
19 | | Property | Description | Type | Default |
20 | |----------|------------------------------------------|-------------|-------|
21 | | tips | title tips for avatar item | ReactNode\/string | - |
22 | | src | the address of the image for an image avatar | string | - |
23 |
--------------------------------------------------------------------------------
/src/components/AvatarList/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tooltip, Avatar } from 'antd';
3 | import classNames from 'classnames';
4 |
5 | import styles from './index.less';
6 |
7 | const AvatarList = ({ children, size, ...other }) => {
8 | const childrenWithProps = React.Children.map(children, child =>
9 | React.cloneElement(child, {
10 | size,
11 | })
12 | );
13 |
14 | return (
15 |
18 | );
19 | };
20 |
21 | const Item = ({ src, size, tips, onClick = () => {} }) => {
22 | const cls = classNames(styles.avatarItem, {
23 | [styles.avatarItemLarge]: size === 'large',
24 | [styles.avatarItemSmall]: size === 'small',
25 | [styles.avatarItemMini]: size === 'mini',
26 | });
27 |
28 | return (
29 |
30 | {tips ? (
31 |
32 |
33 |
34 | ) : (
35 |
36 | )}
37 |
38 | );
39 | };
40 |
41 | AvatarList.Item = Item;
42 |
43 | export default AvatarList;
44 |
--------------------------------------------------------------------------------
/src/components/AvatarList/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .avatarList {
4 | display: inline-block;
5 | ul {
6 | display: inline-block;
7 | margin-left: 8px;
8 | font-size: 0;
9 | }
10 | }
11 |
12 | .avatarItem {
13 | display: inline-block;
14 | font-size: @font-size-base;
15 | margin-left: -8px;
16 | width: @avatar-size-base;
17 | height: @avatar-size-base;
18 | :global {
19 | .ant-avatar {
20 | border: 1px solid #fff;
21 | }
22 | }
23 | }
24 |
25 | .avatarItemLarge {
26 | width: @avatar-size-lg;
27 | height: @avatar-size-lg;
28 | }
29 |
30 | .avatarItemSmall {
31 | width: @avatar-size-sm;
32 | height: @avatar-size-sm;
33 | }
34 |
35 | .avatarItemMini {
36 | width: 20px;
37 | height: 20px;
38 | :global {
39 | .ant-avatar {
40 | width: 20px;
41 | height: 20px;
42 | line-height: 20px;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/AvatarList/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: AvatarList
3 | subtitle: 用户头像列表
4 | order: 1
5 | cols: 1
6 | ---
7 |
8 | 一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。
9 |
10 | ## API
11 |
12 | ### AvatarList
13 |
14 | | 参数 | 说明 | 类型 | 默认值 |
15 | |----------|------------------------------------------|-------------|-------|
16 | | size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` |
17 |
18 | ### AvatarList.Item
19 |
20 | | 参数 | 说明 | 类型 | 默认值 |
21 | |----------|------------------------------------------|-------------|-------|
22 | | tips | 头像展示文案 | ReactNode\/string | - |
23 | | src | 头像图片连接 | string | - |
24 |
--------------------------------------------------------------------------------
/src/components/Charts/Bar/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IBarProps {
3 | title: React.ReactNode;
4 | color?: string;
5 | padding?: [number, number, number, number];
6 | height: number;
7 | data: Array<{
8 | x: string;
9 | y: number;
10 | }>;
11 | autoLabel?: boolean;
12 | style?: React.CSSProperties;
13 | }
14 |
15 | export default class Bar extends React.Component {}
16 |
--------------------------------------------------------------------------------
/src/components/Charts/ChartCard/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IChartCardProps {
3 | title: React.ReactNode;
4 | action?: React.ReactNode;
5 | total?: React.ReactNode | function | number;
6 | footer?: React.ReactNode;
7 | contentHeight?: number;
8 | avatar?: React.ReactNode;
9 | style?: React.CSSProperties;
10 | }
11 |
12 | export default class ChartCard extends React.Component {}
13 |
--------------------------------------------------------------------------------
/src/components/Charts/ChartCard/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card, Spin } from 'antd';
3 | import classNames from 'classnames';
4 |
5 | import styles from './index.less';
6 |
7 | const renderTotal = total => {
8 | let totalDom;
9 | switch (typeof total) {
10 | case undefined:
11 | totalDom = null;
12 | break;
13 | case 'function':
14 | totalDom = {total()}
;
15 | break;
16 | default:
17 | totalDom = {total}
;
18 | }
19 | return totalDom;
20 | };
21 |
22 | const ChartCard = ({
23 | loading = false,
24 | contentHeight,
25 | title,
26 | avatar,
27 | action,
28 | total,
29 | footer,
30 | children,
31 | ...rest
32 | }) => {
33 | const content = (
34 |
35 |
40 |
{avatar}
41 |
42 |
43 | {title}
44 | {action}
45 |
46 | {renderTotal(total)}
47 |
48 |
49 | {children && (
50 |
53 | )}
54 | {footer && (
55 |
60 | {footer}
61 |
62 | )}
63 |
64 | );
65 |
66 | return (
67 |
68 | {
69 |
70 | {content}
71 |
72 | }
73 |
74 | );
75 | };
76 |
77 | export default ChartCard;
78 |
--------------------------------------------------------------------------------
/src/components/Charts/ChartCard/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .chartCard {
4 | position: relative;
5 | .chartTop {
6 | position: relative;
7 | overflow: hidden;
8 | width: 100%;
9 | }
10 | .chartTopMargin {
11 | margin-bottom: 12px;
12 | }
13 | .chartTopHasMargin {
14 | margin-bottom: 20px;
15 | }
16 | .metaWrap {
17 | float: left;
18 | }
19 | .avatar {
20 | position: relative;
21 | top: 4px;
22 | float: left;
23 | margin-right: 20px;
24 | img {
25 | border-radius: 100%;
26 | }
27 | }
28 | .meta {
29 | color: @text-color-secondary;
30 | font-size: @font-size-base;
31 | line-height: 22px;
32 | height: 22px;
33 | }
34 | .action {
35 | cursor: pointer;
36 | position: absolute;
37 | top: 0;
38 | right: 0;
39 | }
40 | .total {
41 | overflow: hidden;
42 | text-overflow: ellipsis;
43 | word-break: break-all;
44 | white-space: nowrap;
45 | color: @heading-color;
46 | margin-top: 4px;
47 | margin-bottom: 0;
48 | font-size: 30px;
49 | line-height: 38px;
50 | height: 38px;
51 | }
52 | .content {
53 | margin-bottom: 12px;
54 | position: relative;
55 | width: 100%;
56 | }
57 | .contentFixed {
58 | position: absolute;
59 | left: 0;
60 | bottom: 0;
61 | width: 100%;
62 | }
63 | .footer {
64 | border-top: 1px solid @border-color-split;
65 | padding-top: 9px;
66 | margin-top: 8px;
67 | & > * {
68 | position: relative;
69 | }
70 | }
71 | .footerMargin {
72 | margin-top: 20px;
73 | }
74 | }
75 |
76 | .spin :global(.ant-spin-container) {
77 | overflow: visible;
78 | }
79 |
--------------------------------------------------------------------------------
/src/components/Charts/Field/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IFieldProps {
3 | label: React.ReactNode;
4 | value: React.ReactNode;
5 | style?: React.CSSProperties;
6 | }
7 |
8 | export default class Field extends React.Component {}
9 |
--------------------------------------------------------------------------------
/src/components/Charts/Field/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import styles from './index.less';
4 |
5 | const Field = ({ label, value, ...rest }) => (
6 |
7 | {label}
8 | {value}
9 |
10 | );
11 |
12 | export default Field;
13 |
--------------------------------------------------------------------------------
/src/components/Charts/Field/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .field {
4 | white-space: nowrap;
5 | overflow: hidden;
6 | text-overflow: ellipsis;
7 | margin: 0;
8 | span {
9 | font-size: @font-size-base;
10 | line-height: 22px;
11 | }
12 | span:last-child {
13 | margin-left: 8px;
14 | color: @heading-color;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/Charts/Gauge/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IGaugeProps {
3 | title: React.ReactNode;
4 | color?: string;
5 | height: number;
6 | bgColor?: number;
7 | percent: number;
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export default class Gauge extends React.Component {}
12 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniArea/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | // g2已经更新到3.0
4 | // 不带的写了
5 |
6 | export interface IAxis {
7 | title: any;
8 | line: any;
9 | gridAlign: any;
10 | labels: any;
11 | tickLine: any;
12 | grid: any;
13 | }
14 |
15 | export interface IMiniAreaProps {
16 | color?: string;
17 | height: number;
18 | borderColor?: string;
19 | line?: boolean;
20 | animate?: boolean;
21 | xAxis?: IAxis;
22 | yAxis?: IAxis;
23 | data: Array<{
24 | x: number;
25 | y: number;
26 | }>;
27 | }
28 |
29 | export default class MiniArea extends React.Component {}
30 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniBar/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IMiniBarProps {
3 | color?: string;
4 | height: number;
5 | data: Array<{
6 | x: number | string;
7 | y: number;
8 | }>;
9 | style?: React.CSSProperties;
10 | }
11 |
12 | export default class MiniBar extends React.Component {}
13 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniBar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Chart, Tooltip, Geom } from 'bizcharts';
3 | import autoHeight from '../autoHeight';
4 | import styles from '../index.less';
5 |
6 | @autoHeight()
7 | export default class MiniBar extends React.Component {
8 | render() {
9 | const { height, forceFit = true, color = '#1890FF', data = [] } = this.props;
10 |
11 | const scale = {
12 | x: {
13 | type: 'cat',
14 | },
15 | y: {
16 | min: 0,
17 | },
18 | };
19 |
20 | const padding = [36, 5, 30, 5];
21 |
22 | const tooltip = [
23 | 'x*y',
24 | (x, y) => ({
25 | name: x,
26 | value: y,
27 | }),
28 | ];
29 |
30 | // for tooltip not to be hide
31 | const chartHeight = height + 54;
32 |
33 | return (
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniProgress/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IMiniProgressProps {
3 | target: number;
4 | color?: string;
5 | strokeWidth?: number;
6 | percent?: number;
7 | style?: React.CSSProperties;
8 | }
9 |
10 | export default class MiniProgress extends React.Component {}
11 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniProgress/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tooltip } from 'antd';
3 |
4 | import styles from './index.less';
5 |
6 | const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 | );
26 |
27 | export default MiniProgress;
28 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniProgress/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .miniProgress {
4 | padding: 5px 0;
5 | position: relative;
6 | width: 100%;
7 | .progressWrap {
8 | background-color: @background-color-base;
9 | position: relative;
10 | }
11 | .progress {
12 | transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
13 | border-radius: 1px 0 0 1px;
14 | background-color: @primary-color;
15 | width: 0;
16 | height: 100%;
17 | }
18 | .target {
19 | position: absolute;
20 | top: 0;
21 | bottom: 0;
22 | span {
23 | border-radius: 100px;
24 | position: absolute;
25 | top: 0;
26 | left: 0;
27 | height: 4px;
28 | width: 2px;
29 | }
30 | span:last-child {
31 | top: auto;
32 | bottom: 0;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/Charts/Pie/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IPieProps {
3 | animate?: boolean;
4 | color?: string;
5 | height: number;
6 | hasLegend?: boolean;
7 | padding?: [number, number, number, number];
8 | percent?: number;
9 | data?: Array<{
10 | x: string | string;
11 | y: number;
12 | }>;
13 | total?: string | function;
14 | title?: React.ReactNode;
15 | tooltip?: boolean;
16 | valueFormat?: (value: string) => string | React.ReactNode;
17 | subTitle?: React.ReactNode;
18 | }
19 |
20 | export default class Pie extends React.Component {}
21 |
--------------------------------------------------------------------------------
/src/components/Charts/Pie/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .pie {
4 | position: relative;
5 | .chart {
6 | position: relative;
7 | }
8 | &.hasLegend .chart {
9 | width: ~'calc(100% - 240px)';
10 | }
11 | .legend {
12 | position: absolute;
13 | right: 0;
14 | min-width: 200px;
15 | top: 50%;
16 | transform: translateY(-50%);
17 | margin: 0 20px;
18 | list-style: none;
19 | padding: 0;
20 | li {
21 | cursor: pointer;
22 | margin-bottom: 16px;
23 | height: 22px;
24 | line-height: 22px;
25 | &:last-child {
26 | margin-bottom: 0;
27 | }
28 | }
29 | }
30 | .dot {
31 | border-radius: 8px;
32 | display: inline-block;
33 | margin-right: 8px;
34 | position: relative;
35 | top: -1px;
36 | height: 8px;
37 | width: 8px;
38 | }
39 | .line {
40 | background-color: @border-color-split;
41 | display: inline-block;
42 | margin-right: 8px;
43 | width: 1px;
44 | height: 16px;
45 | }
46 | .legendTitle {
47 | color: @text-color;
48 | }
49 | .percent {
50 | color: @text-color-secondary;
51 | }
52 | .value {
53 | position: absolute;
54 | right: 0;
55 | }
56 | .title {
57 | margin-bottom: 8px;
58 | }
59 | .total {
60 | position: absolute;
61 | left: 50%;
62 | top: 50%;
63 | text-align: center;
64 | height: 62px;
65 | transform: translate(-50%, -50%);
66 | & > h4 {
67 | color: @text-color-secondary;
68 | font-size: 14px;
69 | line-height: 22px;
70 | height: 22px;
71 | margin-bottom: 8px;
72 | font-weight: normal;
73 | }
74 | & > p {
75 | color: @heading-color;
76 | display: block;
77 | font-size: 1.2em;
78 | height: 32px;
79 | line-height: 32px;
80 | white-space: nowrap;
81 | }
82 | }
83 | }
84 |
85 | .legendBlock {
86 | &.hasLegend .chart {
87 | width: 100%;
88 | margin: 0 0 32px 0;
89 | }
90 | .legend {
91 | position: relative;
92 | transform: none;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/components/Charts/Radar/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IRadarProps {
3 | title?: React.ReactNode;
4 | height: number;
5 | padding?: [number, number, number, number];
6 | hasLegend?: boolean;
7 | data: Array<{
8 | name: string;
9 | label: string;
10 | value: string;
11 | }>;
12 | style?: React.CSSProperties;
13 | }
14 |
15 | export default class Radar extends React.Component {}
16 |
--------------------------------------------------------------------------------
/src/components/Charts/Radar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .radar {
4 | .legend {
5 | margin-top: 16px;
6 | .legendItem {
7 | position: relative;
8 | text-align: center;
9 | cursor: pointer;
10 | color: @text-color-secondary;
11 | line-height: 22px;
12 | p {
13 | margin: 0;
14 | }
15 | h6 {
16 | color: @heading-color;
17 | padding-left: 16px;
18 | font-size: 24px;
19 | line-height: 32px;
20 | margin-top: 4px;
21 | margin-bottom: 0;
22 | }
23 | &:after {
24 | background-color: @border-color-split;
25 | position: absolute;
26 | top: 8px;
27 | right: 0;
28 | height: 40px;
29 | width: 1px;
30 | content: '';
31 | }
32 | }
33 | > :last-child .legendItem:after {
34 | display: none;
35 | }
36 | .dot {
37 | border-radius: 6px;
38 | display: inline-block;
39 | margin-right: 6px;
40 | position: relative;
41 | top: -1px;
42 | height: 6px;
43 | width: 6px;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/Charts/TagCloud/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface ITagCloudProps {
3 | data: Array<{
4 | name: string;
5 | value: number;
6 | }>;
7 | height: number;
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export default class TagCloud extends React.Component {}
12 |
--------------------------------------------------------------------------------
/src/components/Charts/TagCloud/index.less:
--------------------------------------------------------------------------------
1 | .tagCloud {
2 | overflow: hidden;
3 | canvas {
4 | transform: scale(0.25);
5 | transform-origin: 0 0;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/Charts/TimelineChart/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface ITimelineChartProps {
3 | data: Array<{
4 | x: string;
5 | y1: string;
6 | y2: string;
7 | }>;
8 | titleMap: { y1: string; y2: string };
9 | padding?: [number, number, number, number];
10 | height?: number;
11 | style?: React.CSSProperties;
12 | }
13 |
14 | export default class TimelineChart extends React.Component {}
15 |
--------------------------------------------------------------------------------
/src/components/Charts/TimelineChart/index.less:
--------------------------------------------------------------------------------
1 | .timelineChart {
2 | background: #fff;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/Charts/WaterWave/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IWaterWaveProps {
3 | title: React.ReactNode;
4 | color?: string;
5 | height: number;
6 | percent: number;
7 | style?: React.CSSProperties;
8 | }
9 |
10 | export default class WaterWave extends React.Component {}
11 |
--------------------------------------------------------------------------------
/src/components/Charts/WaterWave/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .waterWave {
4 | display: inline-block;
5 | position: relative;
6 | transform-origin: left;
7 | .text {
8 | position: absolute;
9 | left: 0;
10 | top: 32px;
11 | text-align: center;
12 | width: 100%;
13 | span {
14 | color: @text-color-secondary;
15 | font-size: 14px;
16 | line-height: 22px;
17 | }
18 | h4 {
19 | color: @heading-color;
20 | line-height: 32px;
21 | font-size: 24px;
22 | }
23 | }
24 | .waterWaveCanvasWrapper {
25 | transform: scale(0.5);
26 | transform-origin: 0 0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Charts/autoHeight.js:
--------------------------------------------------------------------------------
1 | /* eslint eqeqeq: 0 */
2 | import React from 'react';
3 |
4 | function computeHeight(node) {
5 | const totalHeight = parseInt(getComputedStyle(node).height, 10);
6 | const padding =
7 | parseInt(getComputedStyle(node).paddingTop, 10) +
8 | parseInt(getComputedStyle(node).paddingBottom, 10);
9 | return totalHeight - padding;
10 | }
11 |
12 | function getAutoHeight(n) {
13 | if (!n) {
14 | return 0;
15 | }
16 |
17 | let node = n;
18 |
19 | let height = computeHeight(node);
20 |
21 | while (!height) {
22 | node = node.parentNode;
23 | if (node) {
24 | height = computeHeight(node);
25 | } else {
26 | break;
27 | }
28 | }
29 |
30 | return height;
31 | }
32 |
33 | const autoHeight = () => WrappedComponent => {
34 | return class extends React.Component {
35 | state = {
36 | computedHeight: 0,
37 | };
38 |
39 | componentDidMount() {
40 | const { height } = this.props;
41 | if (!height) {
42 | const h = getAutoHeight(this.root);
43 | // eslint-disable-next-line
44 | this.setState({ computedHeight: h });
45 | }
46 | }
47 |
48 | handleRoot = node => {
49 | this.root = node;
50 | };
51 |
52 | render() {
53 | const { height } = this.props;
54 | const { computedHeight } = this.state;
55 | const h = height || computedHeight;
56 | return (
57 | {h > 0 && }
58 | );
59 | }
60 | };
61 | };
62 |
63 | export default autoHeight;
64 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/bar.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 4
3 | title: 柱状图
4 | ---
5 |
6 | 通过设置 `x`,`y` 属性,可以快速的构建出一个漂亮的柱状图,各种纬度的关系则是通过自定义的数据展现。
7 |
8 | ````jsx
9 | import { Bar } from 'ant-design-pro/lib/Charts';
10 |
11 | const salesData = [];
12 | for (let i = 0; i < 12; i += 1) {
13 | salesData.push({
14 | x: `${i + 1}月`,
15 | y: Math.floor(Math.random() * 1000) + 200,
16 | });
17 | }
18 |
19 | ReactDOM.render(
20 |
25 | , mountNode);
26 | ````
27 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/gauge.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 7
3 | title: 仪表盘
4 | ---
5 |
6 | 仪表盘是一种进度展示方式,可以更直观的展示当前的进展情况,通常也可表示占比。
7 |
8 | ````jsx
9 | import { Gauge } from 'ant-design-pro/lib/Charts';
10 |
11 | ReactDOM.render(
12 |
17 | , mountNode);
18 | ````
19 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/mini-area.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 2
3 | col: 2
4 | title: 迷你区域图
5 | ---
6 |
7 | ````jsx
8 | import { MiniArea } from 'ant-design-pro/lib/Charts';
9 | import moment from 'moment';
10 |
11 | const visitData = [];
12 | const beginDay = new Date().getTime();
13 | for (let i = 0; i < 20; i += 1) {
14 | visitData.push({
15 | x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
16 | y: Math.floor(Math.random() * 100) + 10,
17 | });
18 | }
19 |
20 | ReactDOM.render(
21 |
27 | , mountNode);
28 | ````
29 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/mini-bar.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 2
3 | col: 2
4 | title: 迷你柱状图
5 | ---
6 |
7 | 迷你柱状图更适合展示简单的区间数据,简洁的表现方式可以很好的减少大数据量的视觉展现压力。
8 |
9 | ````jsx
10 | import { MiniBar } from 'ant-design-pro/lib/Charts';
11 | import moment from 'moment';
12 |
13 | const visitData = [];
14 | const beginDay = new Date().getTime();
15 | for (let i = 0; i < 20; i += 1) {
16 | visitData.push({
17 | x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
18 | y: Math.floor(Math.random() * 100) + 10,
19 | });
20 | }
21 |
22 | ReactDOM.render(
23 |
27 | , mountNode);
28 | ````
29 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/mini-pie.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 6
3 | title: 迷你饼状图
4 | ---
5 |
6 | 通过简化 `Pie` 属性的设置,可以快速的实现极简的饼状图,可配合 `ChartCard` 组合展
7 | 现更多业务场景。
8 |
9 | ```jsx
10 | import { Pie } from 'ant-design-pro/lib/Charts';
11 |
12 | ReactDOM.render(
13 | ,
14 | mountNode
15 | );
16 | ```
17 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/mini-progress.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 3
3 | title: 迷你进度条
4 | ---
5 |
6 | ````jsx
7 | import { MiniProgress } from 'ant-design-pro/lib/Charts';
8 |
9 | ReactDOM.render(
10 |
11 | , mountNode);
12 | ````
13 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/pie.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 5
3 | title: 饼状图
4 | ---
5 |
6 | ```jsx
7 | import { Pie, yuan } from 'ant-design-pro/lib/Charts';
8 |
9 | const salesPieData = [
10 | {
11 | x: '家用电器',
12 | y: 4544,
13 | },
14 | {
15 | x: '食用酒水',
16 | y: 3321,
17 | },
18 | {
19 | x: '个护健康',
20 | y: 3113,
21 | },
22 | {
23 | x: '服饰箱包',
24 | y: 2341,
25 | },
26 | {
27 | x: '母婴产品',
28 | y: 1231,
29 | },
30 | {
31 | x: '其他',
32 | y: 1231,
33 | },
34 | ];
35 |
36 | ReactDOM.render(
37 | (
42 | now.y + pre, 0))
45 | }}
46 | />
47 | )}
48 | data={salesPieData}
49 | valueFormat={val => }
50 | height={294}
51 | />,
52 | mountNode,
53 | );
54 | ```
55 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/radar.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 7
3 | title: 雷达图
4 | ---
5 |
6 | ````jsx
7 | import { Radar, ChartCard } from 'ant-design-pro/lib/Charts';
8 |
9 | const radarOriginData = [
10 | {
11 | name: '个人',
12 | ref: 10,
13 | koubei: 8,
14 | output: 4,
15 | contribute: 5,
16 | hot: 7,
17 | },
18 | {
19 | name: '团队',
20 | ref: 3,
21 | koubei: 9,
22 | output: 6,
23 | contribute: 3,
24 | hot: 1,
25 | },
26 | {
27 | name: '部门',
28 | ref: 4,
29 | koubei: 1,
30 | output: 6,
31 | contribute: 5,
32 | hot: 7,
33 | },
34 | ];
35 | const radarData = [];
36 | const radarTitleMap = {
37 | ref: '引用',
38 | koubei: '口碑',
39 | output: '产量',
40 | contribute: '贡献',
41 | hot: '热度',
42 | };
43 | radarOriginData.forEach((item) => {
44 | Object.keys(item).forEach((key) => {
45 | if (key !== 'name') {
46 | radarData.push({
47 | name: item.name,
48 | label: radarTitleMap[key],
49 | value: item[key],
50 | });
51 | }
52 | });
53 | });
54 |
55 | ReactDOM.render(
56 |
57 |
62 |
63 | , mountNode);
64 | ````
65 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/tag-cloud.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 9
3 | title: 标签云
4 | ---
5 |
6 | 标签云是一套相关的标签以及与此相应的权重展示方式,一般典型的标签云有 30 至 150 个标签,而权重影响使用的字体大小或其他视觉效果。
7 |
8 | ````jsx
9 | import { TagCloud } from 'ant-design-pro/lib/Charts';
10 |
11 | const tags = [];
12 | for (let i = 0; i < 50; i += 1) {
13 | tags.push({
14 | name: `TagClout-Title-${i}`,
15 | value: Math.floor((Math.random() * 50)) + 20,
16 | });
17 | }
18 |
19 | ReactDOM.render(
20 |
24 | , mountNode);
25 | ````
26 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/timeline-chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 9
3 | title: 带有时间轴的图表
4 | ---
5 |
6 | 使用 `TimelineChart` 组件可以实现带有时间轴的柱状图展现,而其中的 `x` 属性,则是时间值的指向,默认最多支持同时展现两个指标,分别是 `y1` 和 `y2`。
7 |
8 | ````jsx
9 | import { TimelineChart } from 'ant-design-pro/lib/Charts';
10 |
11 | const chartData = [];
12 | for (let i = 0; i < 20; i += 1) {
13 | chartData.push({
14 | x: (new Date().getTime()) + (1000 * 60 * 30 * i),
15 | y1: Math.floor(Math.random() * 100) + 1000,
16 | y2: Math.floor(Math.random() * 100) + 10,
17 | });
18 | }
19 |
20 | ReactDOM.render(
21 |
26 | , mountNode);
27 | ````
28 |
--------------------------------------------------------------------------------
/src/components/Charts/demo/waterwave.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 8
3 | title: 水波图
4 | ---
5 |
6 | 水波图是一种比例的展示方式,可以更直观的展示关键值的占比。
7 |
8 | ````jsx
9 | import { WaterWave } from 'ant-design-pro/lib/Charts';
10 |
11 | ReactDOM.render(
12 |
13 |
18 |
19 | , mountNode);
20 | ````
21 |
--------------------------------------------------------------------------------
/src/components/Charts/g2.js:
--------------------------------------------------------------------------------
1 | // 全局 G2 设置
2 | import { track, setTheme } from 'bizcharts';
3 |
4 | track(false);
5 |
6 | const config = {
7 | defaultColor: '#1089ff',
8 | shape: {
9 | interval: {
10 | fillOpacity: 1,
11 | },
12 | },
13 | };
14 |
15 | setTheme(config);
16 |
--------------------------------------------------------------------------------
/src/components/Charts/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as numeral from 'numeral';
2 | export { default as ChartCard } from './ChartCard';
3 | export { default as Bar } from './Bar';
4 | export { default as Pie } from './Pie';
5 | export { default as Radar } from './Radar';
6 | export { default as Gauge } from './Gauge';
7 | export { default as MiniArea } from './MiniArea';
8 | export { default as MiniBar } from './MiniBar';
9 | export { default as MiniProgress } from './MiniProgress';
10 | export { default as Field } from './Field';
11 | export { default as WaterWave } from './WaterWave';
12 | export { default as TagCloud } from './TagCloud';
13 | export { default as TimelineChart } from './TimelineChart';
14 |
15 | declare const yuan: (value: number | string) => string;
16 |
17 | export { yuan };
18 |
--------------------------------------------------------------------------------
/src/components/Charts/index.js:
--------------------------------------------------------------------------------
1 | import numeral from 'numeral';
2 | import './g2';
3 | import ChartCard from './ChartCard';
4 | import Bar from './Bar';
5 | import Pie from './Pie';
6 | import Radar from './Radar';
7 | import Gauge from './Gauge';
8 | import MiniArea from './MiniArea';
9 | import MiniBar from './MiniBar';
10 | import MiniProgress from './MiniProgress';
11 | import Field from './Field';
12 | import WaterWave from './WaterWave';
13 | import TagCloud from './TagCloud';
14 | import TimelineChart from './TimelineChart';
15 |
16 | const yuan = val => `¥ ${numeral(val).format('0,0')}`;
17 |
18 | export {
19 | yuan,
20 | Bar,
21 | Pie,
22 | Gauge,
23 | Radar,
24 | MiniBar,
25 | MiniArea,
26 | MiniProgress,
27 | ChartCard,
28 | Field,
29 | WaterWave,
30 | TagCloud,
31 | TimelineChart,
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/Charts/index.less:
--------------------------------------------------------------------------------
1 | .miniChart {
2 | position: relative;
3 | width: 100%;
4 | .chartContent {
5 | position: absolute;
6 | bottom: -28px;
7 | width: 100%;
8 | > div {
9 | margin: 0 -5px;
10 | overflow: hidden;
11 | }
12 | }
13 | .chartLoading {
14 | position: absolute;
15 | top: 16px;
16 | left: 50%;
17 | margin-left: -7px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/CountDown/demo/simple.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title:
4 | zh-CN: 基本
5 | en-US: Basic
6 | ---
7 |
8 | ## zh-CN
9 |
10 | 简单的倒计时组件使用。
11 |
12 | ## en-US
13 |
14 | The simplest usage.
15 |
16 | ````jsx
17 | import CountDown from 'ant-design-pro/lib/CountDown';
18 |
19 | const targetTime = new Date().getTime() + 3900000;
20 |
21 | ReactDOM.render(
22 |
23 | , mountNode);
24 | ````
25 |
--------------------------------------------------------------------------------
/src/components/CountDown/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface ICountDownProps {
3 | format?: (time: number) => void;
4 | target: Date | number;
5 | onEnd?: () => void;
6 | style?: React.CSSProperties;
7 | }
8 |
9 | export default class CountDown extends React.Component {}
10 |
--------------------------------------------------------------------------------
/src/components/CountDown/index.en-US.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CountDown
3 | cols: 1
4 | order: 3
5 | ---
6 |
7 | Simple CountDown Component.
8 |
9 | ## API
10 |
11 | | Property | Description | Type | Default |
12 | |----------|------------------------------------------|-------------|-------|
13 | | format | Formatter of time | Function(time) | |
14 | | target | Target time | Date | - |
15 | | onEnd | Countdown to the end callback | funtion | -|
16 |
--------------------------------------------------------------------------------
/src/components/CountDown/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CountDown
3 | subtitle: 倒计时
4 | cols: 1
5 | order: 3
6 | ---
7 |
8 | 倒计时组件。
9 |
10 | ## API
11 |
12 | | 参数 | 说明 | 类型 | 默认值 |
13 | |----------|------------------------------------------|-------------|-------|
14 | | format | 时间格式化显示 | Function(time) | |
15 | | target | 目标时间 | Date | - |
16 | | onEnd | 倒计时结束回调 | funtion | -|
17 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/Description.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default class Description extends React.Component<
4 | {
5 | term: React.ReactNode;
6 | style?: React.CSSProperties;
7 | },
8 | any
9 | > {}
10 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/Description.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import { Col } from 'antd';
5 | import styles from './index.less';
6 | import responsive from './responsive';
7 |
8 | const Description = ({ term, column, className, children, ...restProps }) => {
9 | const clsString = classNames(styles.description, className);
10 | return (
11 |
12 | {term && {term}
}
13 | {children && {children}
}
14 |
15 | );
16 | };
17 |
18 | Description.defaultProps = {
19 | term: '',
20 | };
21 |
22 | Description.propTypes = {
23 | term: PropTypes.node,
24 | };
25 |
26 | export default Description;
27 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/DescriptionList.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import { Row } from 'antd';
4 | import styles from './index.less';
5 |
6 | export default ({
7 | className,
8 | title,
9 | col = 3,
10 | layout = 'horizontal',
11 | gutter = 32,
12 | children,
13 | size,
14 | ...restProps
15 | }) => {
16 | const clsString = classNames(styles.descriptionList, styles[layout], className, {
17 | [styles.small]: size === 'small',
18 | [styles.large]: size === 'large',
19 | });
20 | const column = col > 4 ? 4 : col;
21 | return (
22 |
23 | {title ?
{title}
: null}
24 |
25 | {React.Children.map(children, child => React.cloneElement(child, { column }))}
26 |
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: Basic
4 | ---
5 |
6 | 基本描述列表。
7 |
8 | ````jsx
9 | import DescriptionList from 'ant-design-pro/lib/DescriptionList';
10 |
11 | const { Description } = DescriptionList;
12 |
13 | ReactDOM.render(
14 |
15 |
16 | A free, open source, cross-platform,
17 | graphical web browser developed by the
18 | Mozilla Corporation and hundreds of
19 | volunteers.
20 |
21 |
22 | A free, open source, cross-platform,
23 | graphical web browser developed by the
24 | Mozilla Corporation and hundreds of
25 | volunteers.
26 |
27 |
28 | A free, open source, cross-platform,
29 | graphical web browser developed by the
30 | Mozilla Corporation and hundreds of
31 | volunteers.
32 |
33 |
34 | , mountNode);
35 | ````
36 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/demo/vertical.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title: Vertical
4 | ---
5 |
6 | 垂直布局。
7 |
8 | ````jsx
9 | import DescriptionList from 'ant-design-pro/lib/DescriptionList';
10 |
11 | const { Description } = DescriptionList;
12 |
13 | ReactDOM.render(
14 |
15 |
16 | A free, open source, cross-platform,
17 | graphical web browser developed by the
18 | Mozilla Corporation and hundreds of
19 | volunteers.
20 |
21 |
22 | A free, open source, cross-platform,
23 | graphical web browser developed by the
24 | Mozilla Corporation and hundreds of
25 | volunteers.
26 |
27 |
28 | A free, open source, cross-platform,
29 | graphical web browser developed by the
30 | Mozilla Corporation and hundreds of
31 | volunteers.
32 |
33 |
34 | , mountNode);
35 | ````
36 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Description from './Description';
3 |
4 | export interface IDescriptionListProps {
5 | layout?: 'horizontal' | 'vertical';
6 | col?: number;
7 | title: React.ReactNode;
8 | gutter?: number;
9 | size?: 'large' | 'small';
10 | style?: React.CSSProperties;
11 | }
12 |
13 | export default class DescriptionList extends React.Component {
14 | public static Description: typeof Description;
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/index.js:
--------------------------------------------------------------------------------
1 | import DescriptionList from './DescriptionList';
2 | import Description from './Description';
3 |
4 | DescriptionList.Description = Description;
5 | export default DescriptionList;
6 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .descriptionList {
4 | // offset the padding-bottom of last row
5 | :global {
6 | .ant-row {
7 | margin-bottom: -16px;
8 | overflow: hidden;
9 | }
10 | }
11 |
12 | .title {
13 | font-size: 14px;
14 | color: @heading-color;
15 | font-weight: 500;
16 | margin-bottom: 16px;
17 | }
18 |
19 | .term {
20 | line-height: 22px;
21 | padding-bottom: 16px;
22 | margin-right: 8px;
23 | color: @heading-color;
24 | white-space: nowrap;
25 | display: table-cell;
26 |
27 | &:after {
28 | content: ':';
29 | margin: 0 8px 0 2px;
30 | position: relative;
31 | top: -0.5px;
32 | }
33 | }
34 |
35 | .detail {
36 | line-height: 22px;
37 | width: 100%;
38 | padding-bottom: 16px;
39 | color: @text-color;
40 | display: table-cell;
41 | }
42 |
43 | &.small {
44 | // offset the padding-bottom of last row
45 | :global {
46 | .ant-row {
47 | margin-bottom: -8px;
48 | }
49 | }
50 | .title {
51 | margin-bottom: 12px;
52 | color: @text-color;
53 | }
54 | .term,
55 | .detail {
56 | padding-bottom: 8px;
57 | }
58 | }
59 |
60 | &.large {
61 | .title {
62 | font-size: 16px;
63 | }
64 | }
65 |
66 | &.vertical {
67 | .term {
68 | padding-bottom: 8px;
69 | display: block;
70 | }
71 |
72 | .detail {
73 | display: block;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: DescriptionList
4 | zh-CN: DescriptionList
5 | subtitle: 描述列表
6 | cols: 1
7 | order: 4
8 | ---
9 |
10 | 成组展示多个只读字段,常见于详情页的信息展示。
11 |
12 | ## API
13 |
14 | ### DescriptionList
15 |
16 | | 参数 | 说明 | 类型 | 默认值 |
17 | |----------|------------------------------------------|-------------|-------|
18 | | layout | 布局方式 | Enum{'horizontal', 'vertical'} | 'horizontal' |
19 | | col | 指定信息最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/components/DescriptionList#响应式规则)决定 | number(0 < col <= 4) | 3 |
20 | | title | 列表标题 | ReactNode | - |
21 | | gutter | 列表项间距,单位为 `px` | number | 32 |
22 | | size | 列表型号,可以设置为 `large` `small` | Enum{'large', 'small'} | - |
23 |
24 | #### 响应式规则
25 |
26 | | 窗口宽度 | 展示列数 |
27 | |---------------------|---------------------------------------------|
28 | | `≥768px` | `col` |
29 | | `≥576px` | `col < 2 ? col : 2` |
30 | | `<576px` | `1` |
31 |
32 | ### DescriptionList.Description
33 |
34 | | 参数 | 说明 | 类型 | 默认值 |
35 | |----------|------------------------------------------|-------------|-------|
36 | | term | 列表项标题 | ReactNode | - |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/responsive.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 1: { xs: 24 },
3 | 2: { xs: 24, sm: 12 },
4 | 3: { xs: 24, sm: 12, md: 8 },
5 | 4: { xs: 24, sm: 12, md: 6 },
6 | };
7 |
--------------------------------------------------------------------------------
/src/components/EditableItem/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Input, Icon } from 'antd';
3 | import styles from './index.less';
4 |
5 | export default class EditableItem extends PureComponent {
6 | state = {
7 | value: this.props.value,
8 | editable: false,
9 | };
10 | handleChange = e => {
11 | const { value } = e.target;
12 | this.setState({ value });
13 | };
14 | check = () => {
15 | this.setState({ editable: false });
16 | if (this.props.onChange) {
17 | this.props.onChange(this.state.value);
18 | }
19 | };
20 | edit = () => {
21 | this.setState({ editable: true });
22 | };
23 | render() {
24 | const { value, editable } = this.state;
25 | return (
26 |
27 | {editable ? (
28 |
29 |
30 |
31 |
32 | ) : (
33 |
34 | {value || ' '}
35 |
36 |
37 | )}
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/EditableItem/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .editableItem {
4 | line-height: @input-height-base;
5 | display: table;
6 | width: 100%;
7 | margin-top: (@font-size-base * @line-height-base - @input-height-base) / 2;
8 |
9 | .wrapper {
10 | display: table-row;
11 |
12 | & > * {
13 | display: table-cell;
14 | }
15 |
16 | & > *:first-child {
17 | width: 85%;
18 | }
19 |
20 | .icon {
21 | cursor: pointer;
22 | text-align: right;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/EditableLinkGroup/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent, createElement } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Button } from 'antd';
4 | import styles from './index.less';
5 |
6 | // TODO: 添加逻辑
7 |
8 | class EditableLinkGroup extends PureComponent {
9 | static defaultProps = {
10 | links: [],
11 | onAdd: () => {},
12 | linkElement: 'a',
13 | };
14 |
15 | static propTypes = {
16 | links: PropTypes.array,
17 | onAdd: PropTypes.func,
18 | linkElement: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
19 | };
20 |
21 | render() {
22 | const { links, linkElement, onAdd } = this.props;
23 | return (
24 |
25 | {links.map(link =>
26 | createElement(
27 | linkElement,
28 | {
29 | key: `linkGroup-item-${link.id || link.title}`,
30 | to: link.href,
31 | href: link.href,
32 | },
33 | link.title
34 | )
35 | )}
36 | {
37 |
38 | 添加
39 |
40 | }
41 |
42 | );
43 | }
44 | }
45 |
46 | export default EditableLinkGroup;
47 |
--------------------------------------------------------------------------------
/src/components/EditableLinkGroup/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .linkGroup {
4 | padding: 20px 0 8px 24px;
5 | font-size: 0;
6 | & > a {
7 | color: @text-color;
8 | display: inline-block;
9 | font-size: @font-size-base;
10 | margin-bottom: 13px;
11 | width: 25%;
12 | &:hover {
13 | color: @primary-color;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/Ellipsis/demo/line.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title: 按照行数省略
4 | ---
5 |
6 | 通过设置 `lines` 属性指定最大行数,如果超过这个行数的文本会自动截取。但是在这种模式下所有 `children` 将会被转换成纯文本。
7 |
8 | 并且注意在这种模式下,外容器需要有指定的宽度(或设置自身宽度)。
9 |
10 | ````jsx
11 | import Ellipsis from 'ant-design-pro/lib/Ellipsis';
12 |
13 | const article = There were injuries alleged in three cases in 2015 , and a fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.
;
14 |
15 | ReactDOM.render(
16 |
17 | {article}
18 |
19 | , mountNode);
20 | ````
21 |
--------------------------------------------------------------------------------
/src/components/Ellipsis/demo/number.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 按照字符数省略
4 | ---
5 |
6 | 通过设置 `length` 属性指定文本最长长度,如果超过这个长度会自动截取。
7 |
8 | ````jsx
9 | import Ellipsis from 'ant-design-pro/lib/Ellipsis';
10 |
11 | const article = 'There were injuries alleged in three cases in 2015, and a fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.';
12 |
13 | ReactDOM.render(
14 |
15 | {article}
16 |
Show Tooltip
17 | {article}
18 |
19 | , mountNode);
20 | ````
21 |
--------------------------------------------------------------------------------
/src/components/Ellipsis/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IEllipsisProps {
3 | tooltip?: boolean;
4 | length?: number;
5 | lines?: number;
6 | style?: React.CSSProperties;
7 | className?: string;
8 | }
9 |
10 | export default class Ellipsis extends React.Component {}
11 |
--------------------------------------------------------------------------------
/src/components/Ellipsis/index.less:
--------------------------------------------------------------------------------
1 | .ellipsis {
2 | overflow: hidden;
3 | display: inline-block;
4 | word-break: break-all;
5 | width: 100%;
6 | }
7 |
8 | .lines {
9 | position: relative;
10 | .shadow {
11 | display: block;
12 | position: relative;
13 | color: transparent;
14 | opacity: 0;
15 | z-index: -999;
16 | }
17 | }
18 |
19 | .lineClamp {
20 | position: relative;
21 | overflow: hidden;
22 | text-overflow: ellipsis;
23 | display: -webkit-box;
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Ellipsis/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: Ellipsis
4 | zh-CN: Ellipsis
5 | subtitle: 文本自动省略号
6 | cols: 1
7 | order: 10
8 | ---
9 |
10 | 文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。
11 |
12 | ## API
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | tooltip | 移动到文本展示完整内容的提示 | boolean | -
17 | length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -
18 | lines | 在按照行数截取下最大的行数,超过则截取省略 | number | `1`
19 |
--------------------------------------------------------------------------------
/src/components/Exception/demo/403.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 2
3 | title: 403
4 | ---
5 |
6 | 403 页面,配合自定义操作。
7 |
8 | ````jsx
9 | import Exception from 'ant-design-pro/lib/Exception';
10 | import { Button } from 'antd';
11 |
12 | const actions = (
13 |
14 | 回到首页
15 | 查看详情
16 |
17 | );
18 | ReactDOM.render(
19 |
20 | , mountNode);
21 | ````
22 |
--------------------------------------------------------------------------------
/src/components/Exception/demo/404.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 404
4 | ---
5 |
6 | 404 页面。
7 |
8 | ````jsx
9 | import Exception from 'ant-design-pro/lib/Exception';
10 |
11 | ReactDOM.render(
12 |
13 | , mountNode);
14 | ````
15 |
--------------------------------------------------------------------------------
/src/components/Exception/demo/500.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title: 500
4 | ---
5 |
6 | 500 页面。
7 |
8 | ````jsx
9 | import Exception from 'ant-design-pro/lib/Exception';
10 |
11 | ReactDOM.render(
12 |
13 | , mountNode);
14 | ````
15 |
--------------------------------------------------------------------------------
/src/components/Exception/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IExceptionProps {
3 | type?: '403' | '404' | '500';
4 | title?: React.ReactNode;
5 | desc?: React.ReactNode;
6 | img?: string;
7 | actions?: React.ReactNode;
8 | linkElement?: React.ReactNode;
9 | style?: React.CSSProperties;
10 | }
11 |
12 | export default class Exception extends React.Component {}
13 |
--------------------------------------------------------------------------------
/src/components/Exception/index.js:
--------------------------------------------------------------------------------
1 | import React, { createElement } from 'react';
2 | import classNames from 'classnames';
3 | import { Button } from 'antd';
4 | import config from './typeConfig';
5 | import styles from './index.less';
6 |
7 | export default ({ className, linkElement = 'a', type, title, desc, img, actions, ...rest }) => {
8 | const pageType = type in config ? type : '404';
9 | const clsString = classNames(styles.exception, className);
10 | return (
11 |
12 |
18 |
19 |
{title || config[pageType].title}
20 |
{desc || config[pageType].desc}
21 |
22 | {actions ||
23 | createElement(
24 | linkElement,
25 | {
26 | to: '/',
27 | href: '/',
28 | },
29 | 返回首页
30 | )}
31 |
32 |
33 |
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/src/components/Exception/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .exception {
4 | display: flex;
5 | align-items: center;
6 | height: 100%;
7 |
8 | .imgBlock {
9 | flex: 0 0 62.5%;
10 | width: 62.5%;
11 | padding-right: 152px;
12 | zoom: 1;
13 | &:before,
14 | &:after {
15 | content: ' ';
16 | display: table;
17 | }
18 | &:after {
19 | clear: both;
20 | visibility: hidden;
21 | font-size: 0;
22 | height: 0;
23 | }
24 | }
25 |
26 | .imgEle {
27 | height: 360px;
28 | width: 100%;
29 | max-width: 430px;
30 | float: right;
31 | background-repeat: no-repeat;
32 | background-position: 50% 50%;
33 | background-size: contain;
34 | }
35 |
36 | .content {
37 | flex: auto;
38 |
39 | h1 {
40 | color: #434e59;
41 | font-size: 72px;
42 | font-weight: 600;
43 | line-height: 72px;
44 | margin-bottom: 24px;
45 | }
46 |
47 | .desc {
48 | color: @text-color-secondary;
49 | font-size: 20px;
50 | line-height: 28px;
51 | margin-bottom: 16px;
52 | }
53 |
54 | .actions {
55 | button:not(:last-child) {
56 | margin-right: 8px;
57 | }
58 | }
59 | }
60 | }
61 |
62 | @media screen and (max-width: @screen-xl) {
63 | .exception {
64 | .imgBlock {
65 | padding-right: 88px;
66 | }
67 | }
68 | }
69 |
70 | @media screen and (max-width: @screen-sm) {
71 | .exception {
72 | display: block;
73 | text-align: center;
74 | .imgBlock {
75 | padding-right: 0;
76 | margin: 0 auto 24px;
77 | }
78 | }
79 | }
80 |
81 | @media screen and (max-width: @screen-xs) {
82 | .exception {
83 | .imgBlock {
84 | margin-bottom: -24px;
85 | overflow: hidden;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/components/Exception/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: Exception
4 | zh-CN: Exception
5 | subtitle: 异常
6 | cols: 1
7 | order: 5
8 | ---
9 |
10 | 异常页用于对页面特定的异常状态进行反馈。通常,它包含对错误状态的阐述,并向用户提供建议或操作,避免用户感到迷失和困惑。
11 |
12 | ## API
13 |
14 | | 参数 | 说明 | 类型 | 默认值 |
15 | |-------------|------------------------------------------|-------------|-------|
16 | | type | 页面类型,若配置,则自带对应类型默认的 `title`,`desc`,`img`,此默认设置可以被 `title`,`desc`,`img` 覆盖 | Enum {'403', '404', '500'} | - |
17 | | title | 标题 | ReactNode | - |
18 | | desc | 补充描述 | ReactNode | - |
19 | | img | 背景图片地址 | string | - |
20 | | actions | 建议操作,配置此属性时默认的『返回首页』按钮不生效 | ReactNode | - |
21 | | linkElement | 定义链接的元素,默认为 `a` | string\|ReactElement | - |
22 |
--------------------------------------------------------------------------------
/src/components/Exception/typeConfig.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | 403: {
3 | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
4 | title: '403',
5 | desc: '抱歉,你无权访问该页面',
6 | },
7 | 404: {
8 | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
9 | title: '404',
10 | desc: '抱歉,你访问的页面不存在',
11 | },
12 | 500: {
13 | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
14 | title: '500',
15 | desc: '抱歉,服务器出错了',
16 | },
17 | };
18 |
19 | export default config;
20 |
--------------------------------------------------------------------------------
/src/components/FooterToolbar/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title:
4 | zh-CN: 演示
5 | en-US: demo
6 | iframe: 400
7 | ---
8 |
9 | ## zh-CN
10 |
11 | 浮动固定页脚。
12 |
13 | ## en-US
14 |
15 | Fixed to the footer.
16 |
17 | ````jsx
18 | import FooterToolbar from 'ant-design-pro/lib/FooterToolbar';
19 | import { Button } from 'antd';
20 |
21 | ReactDOM.render(
22 |
23 |
Content Content Content Content
24 |
Content Content Content Content
25 |
Content Content Content Content
26 |
Content Content Content Content
27 |
Content Content Content Content
28 |
Content Content Content Content
29 |
Content Content Content Content
30 |
Content Content Content Content
31 |
Content Content Content Content
32 |
Content Content Content Content
33 |
Content Content Content Content
34 |
Content Content Content Content
35 |
Content Content Content Content
36 |
Content Content Content Content
37 |
Content Content Content Content
38 |
39 | Cancel
40 | Submit
41 |
42 |
43 | , mountNode);
44 | ````
--------------------------------------------------------------------------------
/src/components/FooterToolbar/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IFooterToolbarProps {
3 | extra: React.ReactNode;
4 | style?: React.CSSProperties;
5 | }
6 |
7 | export default class FooterToolbar extends React.Component {}
8 |
--------------------------------------------------------------------------------
/src/components/FooterToolbar/index.en-US.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: FooterToolbar
3 | cols: 1
4 | order: 6
5 | ---
6 |
7 | A toolbar fixed at the bottom.
8 |
9 | ## Usage
10 |
11 | It is fixed at the bottom of the content area and does not move along with the scroll bar, which is usually used for data collection and submission for long pages.
12 |
13 | ## API
14 |
15 | Property | Description | Type | Default
16 | ---------|-------------|------|--------
17 | children | toolbar content, align to the right | ReactNode | -
18 | extra | extra information, align to the left | ReactNode | -
--------------------------------------------------------------------------------
/src/components/FooterToolbar/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import classNames from 'classnames';
3 | import styles from './index.less';
4 |
5 | export default class FooterToolbar extends Component {
6 | render() {
7 | const { children, className, extra, ...restProps } = this.props;
8 | return (
9 |
10 |
{extra}
11 |
{children}
12 |
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/FooterToolbar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .toolbar {
4 | position: fixed;
5 | width: 100%;
6 | bottom: 0;
7 | right: 0;
8 | height: 56px;
9 | line-height: 56px;
10 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
11 | background: #fff;
12 | border-top: 1px solid @border-color-split;
13 | padding: 0 24px;
14 | z-index: 9;
15 |
16 | &:after {
17 | content: '';
18 | display: block;
19 | clear: both;
20 | }
21 |
22 | .left {
23 | float: left;
24 | }
25 |
26 | .right {
27 | float: right;
28 | }
29 |
30 | button + button {
31 | margin-left: 8px;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/FooterToolbar/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: FooterToolbar
3 | subtitle: 底部工具栏
4 | cols: 1
5 | order: 6
6 | ---
7 |
8 | 固定在底部的工具栏。
9 |
10 | ## 何时使用
11 |
12 | 固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。
13 |
14 | ## API
15 |
16 | 参数 | 说明 | 类型 | 默认值
17 | ----|------|-----|------
18 | children | 工具栏内容,向右对齐 | ReactNode | -
19 | extra | 额外信息,向左对齐 | ReactNode | -
20 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 演示
4 | iframe: 400
5 | ---
6 |
7 | 基本页脚。
8 |
9 | ````jsx
10 | import GlobalFooter from 'ant-design-pro/lib/GlobalFooter';
11 | import { Icon } from 'antd';
12 |
13 | const links = [{
14 | key: '帮助',
15 | title: '帮助',
16 | href: '',
17 | }, {
18 | key: 'github',
19 | title: ,
20 | href: 'https://github.com/ant-design/ant-design-pro',
21 | blankTarget: true,
22 | }, {
23 | key: '条款',
24 | title: '条款',
25 | href: '',
26 | blankTarget: true,
27 | }];
28 |
29 | const copyright = Copyright 2017 蚂蚁金服体验技术部出品
;
30 |
31 | ReactDOM.render(
32 |
36 | , mountNode);
37 | ````
38 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IGlobalFooterProps {
3 | links?: Array<{
4 | title: React.ReactNode;
5 | href: string;
6 | blankTarget?: boolean;
7 | }>;
8 | copyright?: React.ReactNode;
9 | style?: React.CSSProperties;
10 | }
11 |
12 | export default class GlobalFooter extends React.Component {}
13 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import styles from './index.less';
4 |
5 | export default ({ className, links, copyright }) => {
6 | const clsString = classNames(styles.globalFooter, className);
7 | return (
8 |
9 | {links && (
10 |
17 | )}
18 | {copyright &&
{copyright}
}
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .globalFooter {
4 | padding: 0 16px;
5 | margin: 48px 0 24px 0;
6 | text-align: center;
7 |
8 | .links {
9 | margin-bottom: 8px;
10 |
11 | a {
12 | color: @text-color-secondary;
13 | transition: all 0.3s;
14 |
15 | &:not(:last-child) {
16 | margin-right: 40px;
17 | }
18 |
19 | &:hover {
20 | color: @text-color;
21 | }
22 | }
23 | }
24 |
25 | .copyright {
26 | color: @text-color-secondary;
27 | font-size: @font-size-base;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: GlobalFooter
4 | zh-CN: GlobalFooter
5 | subtitle: 全局页脚
6 | cols: 1
7 | order: 7
8 | ---
9 |
10 | 页脚属于全局导航的一部分,作为对顶部导航的补充,通过传递数据控制展示内容。
11 |
12 | ## API
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | links | 链接数据 | array<{ title: ReactNode, href: string, blankTarget?: boolean }> | -
17 | copyright | 版权信息 | ReactNode | -
18 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 全局搜索
4 | ---
5 |
6 | 通常放置在导航工具条右侧。(点击搜索图标预览效果)
7 |
8 | ````jsx
9 | import HeaderSearch from 'ant-design-pro/lib/HeaderSearch';
10 |
11 | ReactDOM.render(
12 |
22 | {
26 | console.log('input', value); // eslint-disable-line
27 | }}
28 | onPressEnter={(value) => {
29 | console.log('enter', value); // eslint-disable-line
30 | }}
31 | />
32 |
33 | , mountNode);
34 | ````
35 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IHeaderSearchProps {
3 | placeholder?: string;
4 | dataSource?: string[];
5 | onSearch?: (value: string) => void;
6 | onChange?: (value: string) => void;
7 | onPressEnter?: (value: string) => void;
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export default class HeaderSearch extends React.Component {}
12 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .headerSearch {
4 | :global(.anticon-search) {
5 | cursor: pointer;
6 | font-size: 16px;
7 | }
8 | .input {
9 | transition: width 0.3s, margin-left 0.3s;
10 | width: 0;
11 | background: transparent;
12 | border-radius: 0;
13 | :global(.ant-select-selection) {
14 | background: transparent;
15 | }
16 | input {
17 | border: 0;
18 | padding-left: 0;
19 | padding-right: 0;
20 | box-shadow: none !important;
21 | }
22 | &,
23 | &:hover,
24 | &:focus {
25 | border-bottom: 1px solid @border-color-base;
26 | }
27 | &.show {
28 | width: 210px;
29 | margin-left: 8px;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: HeaderSearch
4 | zh-CN: HeaderSearch
5 | subtitle: 顶部搜索框
6 | cols: 1
7 | order: 8
8 | ---
9 |
10 | 通常作为全局搜索的入口,放置在导航工具条右侧。
11 |
12 | ## API
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | placeholder | 占位文字 | string | -
17 | dataSource | 当前提示内容列表 | string[] | -
18 | onSearch | 选择某项或按下回车时的回调 | function(value) | -
19 | onChange | 输入搜索字符的回调 | function(value) | -
20 | onPressEnter | 按下回车时的回调 | function(value) | -
21 | defaultOpen | 输入框首次显示是否打开 | boolean | false
22 |
--------------------------------------------------------------------------------
/src/components/ImageWrapper/index.js:
--------------------------------------------------------------------------------
1 | // index.js
2 | import React from 'react';
3 | import styles from './index.less'; // import style in css modules way
4 |
5 | export default ({ src, desc, style }) => (
6 |
7 |
8 | {desc &&
{desc}
}
9 |
10 | );
11 |
--------------------------------------------------------------------------------
/src/components/ImageWrapper/index.less:
--------------------------------------------------------------------------------
1 | // index.less
2 | .imageWrapper {
3 | padding: 0 20px 8px;
4 | background: #f2f4f5;
5 | width: 400px;
6 | margin: 0 auto;
7 | text-align: center;
8 | }
9 |
10 | .img {
11 | vertical-align: middle;
12 | max-width: calc(100% - 32px);
13 | margin: 2.4em 1em;
14 | box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35);
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Login/LoginSubmit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import { Button, Form } from 'antd';
4 | import styles from './index.less';
5 |
6 | const FormItem = Form.Item;
7 |
8 | export default ({ className, ...rest }) => {
9 | const clsString = classNames(styles.submit, className);
10 | return (
11 |
12 |
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/Login/LoginTab.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Tabs } from 'antd';
4 |
5 | const { TabPane } = Tabs;
6 |
7 | const generateId = (() => {
8 | let i = 0;
9 | return (prefix = '') => {
10 | i += 1;
11 | return `${prefix}${i}`;
12 | };
13 | })();
14 |
15 | export default class LoginTab extends Component {
16 | static __ANT_PRO_LOGIN_TAB = true;
17 | static contextTypes = {
18 | tabUtil: PropTypes.object,
19 | };
20 | constructor(props) {
21 | super(props);
22 | this.uniqueId = generateId('login-tab-');
23 | }
24 | componentWillMount() {
25 | if (this.context.tabUtil) {
26 | this.context.tabUtil.addTab(this.uniqueId);
27 | }
28 | }
29 | render() {
30 | return ;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/Login/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Button from 'antd/lib/button';
3 | export interface LoginProps {
4 | defaultActiveKey?: string;
5 | onTabChange?: (key: string) => void;
6 | style?: React.CSSProperties;
7 | onSubmit?: (error: any, values: any) => void;
8 | }
9 |
10 | export interface TabProps {
11 | key?: string;
12 | tab?: React.ReactNode;
13 | }
14 | export class Tab extends React.Component {}
15 |
16 | export interface LoginItemProps {
17 | name?: string;
18 | rules?: any[];
19 | style?: React.CSSProperties;
20 | onGetCaptcha?: () => void;
21 | }
22 |
23 | export class LoginItem extends React.Component {}
24 |
25 | export default class Login extends React.Component {
26 | static Tab: typeof Tab;
27 | static UserName: typeof LoginItem;
28 | static Password: typeof LoginItem;
29 | static Mobile: typeof LoginItem;
30 | static Captcha: typeof LoginItem;
31 | static Submit: typeof Button;
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/Login/index.en-US.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Login
3 | cols: 1
4 | order: 15
5 | ---
6 |
7 | Support multiple common ways of login with built-in controls. You can choose your own combinations and use with your custom controls.
8 |
9 | ## API
10 |
11 | ### Login
12 |
13 | Property | Description | Type | Default
14 | ----|------|-----|------
15 | defaultActiveKey | default key to activate the tab panel | String | -
16 | onTabChange | callback on changing tabs | (key) => void | -
17 | onSubmit | callback on submit | (err, values) => void | -
18 |
19 | ### Login.Tab
20 |
21 | Property | Description | Type | Default
22 | ----|------|-----|------
23 | key | key of the tab | String | -
24 | tab | displayed text of the tab | ReactNode | -
25 |
26 | ### Login.UserName
27 |
28 | Property | Description | Type | Default
29 | ----|------|-----|------
30 | name | name of the control, also the key of the submitted data | String | -
31 | rules | validation rules, same with [option.rules](getFieldDecorator(id, options)) in Form getFieldDecorator(id, options) | object[] | -
32 |
33 | Apart from the above properties, Login.Username also support all properties of antd.Input, together with the default values of basic settings, such as _placeholder_, _size_ and _prefix_. All of these default values can be over-written.
34 |
35 | ### Login.Password, Login.Mobile are the same as Login.UserName
36 |
37 | ### Login.Captcha
38 |
39 | Property | Description | Type | Default
40 | ----|------|-----|------
41 | onGetCaptcha | callback on getting a new Captcha | () => void | -
42 |
43 | Apart from the above properties, _Login.Captcha_ support the same properties with _Login.UserName_.
44 |
45 | ### Login.Submit
46 |
47 | Support all properties of _antd.Button_.
--------------------------------------------------------------------------------
/src/components/Login/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .login {
4 | .tabs {
5 | padding: 0 2px;
6 | margin: 0 -2px;
7 | :global {
8 | .ant-tabs-tab {
9 | font-size: 16px;
10 | line-height: 24px;
11 | }
12 | .ant-input-affix-wrapper .ant-input:not(:first-child) {
13 | padding-left: 34px;
14 | }
15 | }
16 | }
17 |
18 | :global {
19 | .ant-tabs .ant-tabs-bar {
20 | border-bottom: 0;
21 | margin-bottom: 24px;
22 | text-align: center;
23 | }
24 |
25 | .ant-form-item {
26 | margin-bottom: 24px;
27 | }
28 | }
29 |
30 | .prefixIcon {
31 | font-size: @font-size-base;
32 | color: @disabled-color;
33 | }
34 |
35 | .getCaptcha {
36 | display: block;
37 | width: 100%;
38 | }
39 |
40 | .submit {
41 | width: 100%;
42 | margin-top: 24px;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/Login/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Login
3 | subtitle: 登录
4 | cols: 1
5 | order: 15
6 | ---
7 |
8 | 支持多种登录方式切换,内置了几种常见的登录控件,可以灵活组合,也支持和自定义控件配合使用。
9 |
10 | ## API
11 |
12 | ### Login
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | defaultActiveKey | 默认激活 tab 面板的 key | String | -
17 | onTabChange | 切换页签时的回调 | (key) => void | -
18 | onSubmit | 点击提交时的回调 | (err, values) => void | -
19 |
20 | ### Login.Tab
21 |
22 | 参数 | 说明 | 类型 | 默认值
23 | ----|------|-----|------
24 | key | 对应选项卡的 key | String | -
25 | tab | 选项卡头显示文字 | ReactNode | -
26 |
27 | ### Login.UserName
28 |
29 | 参数 | 说明 | 类型 | 默认值
30 | ----|------|-----|------
31 | name | 控件标记,提交数据中同样以此为 key | String | -
32 | rules | 校验规则,同 Form getFieldDecorator(id, options) 中 [option.rules 的规则](getFieldDecorator(id, options)) | object[] | -
33 |
34 | 除上述属性以外,Login.UserName 还支持 antd.Input 的所有属性,并且自带默认的基础配置,包括 `placeholder` `size` `prefix` 等,这些基础配置均可被覆盖。
35 |
36 | ### Login.Password、Login.Mobile 同 Login.UserName
37 |
38 | ### Login.Captcha
39 |
40 | 参数 | 说明 | 类型 | 默认值
41 | ----|------|-----|------
42 | onGetCaptcha | 点击获取校验码的回调 | () => void | -
43 |
44 | 除上述属性以外,Login.Captcha 支持的属性与 Login.UserName 相同。
45 |
46 | ### Login.Submit
47 |
48 | 支持 antd.Button 的所有属性。
49 |
--------------------------------------------------------------------------------
/src/components/Login/map.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Input, Icon } from 'antd';
3 | import styles from './index.less';
4 |
5 | const map = {
6 | UserName: {
7 | component: Input,
8 | props: {
9 | size: 'large',
10 | prefix: ,
11 | placeholder: 'admin',
12 | },
13 | rules: [
14 | {
15 | required: true,
16 | message: 'Please enter username!',
17 | },
18 | ],
19 | },
20 | Password: {
21 | component: Input,
22 | props: {
23 | size: 'large',
24 | prefix: ,
25 | type: 'password',
26 | placeholder: '888888',
27 | },
28 | rules: [
29 | {
30 | required: true,
31 | message: 'Please enter password!',
32 | },
33 | ],
34 | },
35 | Mobile: {
36 | component: Input,
37 | props: {
38 | size: 'large',
39 | prefix: ,
40 | placeholder: 'mobile number',
41 | },
42 | rules: [
43 | {
44 | required: true,
45 | message: 'Please enter mobile number!',
46 | },
47 | {
48 | pattern: /^1\d{10}$/,
49 | message: 'Wrong mobile number format!',
50 | },
51 | ],
52 | },
53 | Captcha: {
54 | component: Input,
55 | props: {
56 | size: 'large',
57 | prefix: ,
58 | placeholder: 'captcha',
59 | },
60 | rules: [
61 | {
62 | required: true,
63 | message: 'Please enter Captcha!',
64 | },
65 | ],
66 | },
67 | };
68 |
69 | export default map;
70 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeIconTab.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface INoticeIconData {
3 | avatar?: string;
4 | title?: React.ReactNode;
5 | description?: React.ReactNode;
6 | datetime?: React.ReactNode;
7 | extra?: React.ReactNode;
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export interface INoticeIconTabProps {
12 | list?: INoticeIconData[];
13 | title?: string;
14 | emptyText?: React.ReactNode;
15 | emptyImage?: string;
16 | style?: React.CSSProperties;
17 | }
18 |
19 | export default class NoticeIconTab extends React.Component {}
20 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeList.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Avatar, List } from 'antd';
3 | import classNames from 'classnames';
4 | import styles from './NoticeList.less';
5 |
6 | export default function NoticeList({
7 | data = [],
8 | onClick,
9 | onClear,
10 | title,
11 | locale,
12 | emptyText,
13 | emptyImage,
14 | }) {
15 | if (data.length === 0) {
16 | return (
17 |
18 | {emptyImage ?
: null}
19 |
{emptyText || locale.emptyText}
20 |
21 | );
22 | }
23 | return (
24 |
25 |
26 | {data.map((item, i) => {
27 | const itemCls = classNames(styles.item, {
28 | [styles.read]: item.read,
29 | });
30 | return (
31 | onClick(item)}>
32 | : null}
35 | title={
36 |
37 | {item.title}
38 |
{item.extra}
39 |
40 | }
41 | description={
42 |
43 |
44 | {item.description}
45 |
46 |
{item.datetime}
47 |
48 | }
49 | />
50 |
51 | );
52 | })}
53 |
54 |
55 | {locale.clear}
56 | {title}
57 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeList.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .list {
4 | max-height: 400px;
5 | overflow: auto;
6 | .item {
7 | transition: all 0.3s;
8 | overflow: hidden;
9 | cursor: pointer;
10 | padding-left: 24px;
11 | padding-right: 24px;
12 |
13 | .meta {
14 | width: 100%;
15 | }
16 |
17 | .avatar {
18 | background: #fff;
19 | margin-top: 4px;
20 | }
21 |
22 | &.read {
23 | opacity: 0.4;
24 | }
25 | &:last-child {
26 | border-bottom: 0;
27 | }
28 | &:hover {
29 | background: @primary-1;
30 | }
31 | .title {
32 | font-weight: normal;
33 | margin-bottom: 8px;
34 | }
35 | .description {
36 | font-size: 12px;
37 | line-height: @line-height-base;
38 | }
39 | .datetime {
40 | font-size: 12px;
41 | margin-top: 4px;
42 | line-height: @line-height-base;
43 | }
44 | .extra {
45 | float: right;
46 | color: @text-color-secondary;
47 | font-weight: normal;
48 | margin-right: 0;
49 | margin-top: -1.5px;
50 | }
51 | }
52 | }
53 |
54 | .notFound {
55 | text-align: center;
56 | padding: 73px 0 88px 0;
57 | color: @text-color-secondary;
58 | img {
59 | display: inline-block;
60 | margin-bottom: 16px;
61 | height: 76px;
62 | }
63 | }
64 |
65 | .clear {
66 | height: 46px;
67 | line-height: 46px;
68 | text-align: center;
69 | color: @text-color;
70 | border-radius: 0 0 @border-radius-base @border-radius-base;
71 | border-top: 1px solid @border-color-split;
72 | transition: all 0.3s;
73 | cursor: pointer;
74 |
75 | &:hover {
76 | color: @heading-color;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title: 通知图标
4 | ---
5 |
6 | 通常用在导航工具栏上。
7 |
8 | ````jsx
9 | import NoticeIcon from 'ant-design-pro/lib/NoticeIcon';
10 |
11 | ReactDOM.render( , mountNode);
12 | ````
13 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.d.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import NoticeIconTab, { INoticeIconData } from './NoticeIconTab';
3 |
4 | export interface INoticeIconProps {
5 | count?: number;
6 | className?: string;
7 | loading?: boolean;
8 | onClear?: (tableTile: string) => void;
9 | onItemClick?: (item: INoticeIconData, tabProps: INoticeIconProps) => void;
10 | onTabChange?: (tableTile: string) => void;
11 | popupAlign?: {
12 | points?: [string, string];
13 | offset?: [number, number];
14 | targetOffset?: [number, number];
15 | overflow?: any;
16 | useCssRight?: boolean;
17 | useCssBottom?: boolean;
18 | useCssTransform?: boolean;
19 | };
20 | style?: React.CSSProperties;
21 | onPopupVisibleChange?: (visible: boolean) => void;
22 | popupVisible?: boolean;
23 | locale?: { emptyText: string; clear: string };
24 | }
25 |
26 | export default class NoticeIcon extends React.Component {
27 | public static Tab: typeof NoticeIconTab;
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .popover {
4 | width: 336px;
5 | :global(.ant-popover-inner-content) {
6 | padding: 0;
7 | }
8 | }
9 |
10 | .noticeButton {
11 | cursor: pointer;
12 | display: inline-block;
13 | transition: all 0.3s;
14 | }
15 |
16 | .icon {
17 | font-size: 16px;
18 | padding: 4px;
19 | }
20 |
21 | .tabs {
22 | :global {
23 | .ant-tabs-nav-scroll {
24 | text-align: center;
25 | }
26 | .ant-tabs-bar {
27 | margin-bottom: 4px;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: NoticeIcon
4 | zh-CN: NoticeIcon
5 | subtitle: 通知菜单
6 | cols: 1
7 | order: 9
8 | ---
9 |
10 | 用在导航工具栏上,作为整个产品统一的通知中心。
11 |
12 | ## API
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | count | 图标上的消息总数 | number | -
17 | loading | 弹出卡片加载状态 | boolean | false
18 | onClear | 点击清空按钮的回调 | function(tabTitle) | -
19 | onItemClick | 点击列表项的回调 | function(item, tabProps) | -
20 | onTabChange | 切换页签的回调 | function(tabTitle) | -
21 | popupAlign | 弹出卡片的位置配置 | Object [alignConfig](https://github.com/yiminghe/dom-align#alignconfig-object-details) | -
22 | onPopupVisibleChange | 弹出卡片显隐的回调 | function(visible) | -
23 | popupVisible | 控制弹层显隐 | boolean | -
24 | locale | 默认文案 | Object | `{ emptyText: '暂无数据', clear: '清空' }`
25 |
26 | ### NoticeIcon.Tab
27 |
28 | 参数 | 说明 | 类型 | 默认值
29 | ----|------|-----|------
30 | title | 消息分类的页签标题 | string | -
31 | list | 列表数据,格式参照下表 | Array | `[]`
32 | emptyText | 针对每个 Tab 定制空数据文案 | ReactNode | -
33 | emptyImage | 针对每个 Tab 定制空数据图片 | string | -
34 |
35 | ### Tab data
36 |
37 | 参数 | 说明 | 类型 | 默认值
38 | ----|------|-----|------
39 | avatar | 头像图片链接 | string | -
40 | title | 标题 | ReactNode | -
41 | description | 描述信息 | ReactNode | -
42 | datetime | 时间戳 | ReactNode | -
43 | extra | 额外信息,在列表项右上角 | ReactNode | -
44 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 演示
4 | ---
5 |
6 | 各种数据文案的展现方式。
7 |
8 | ````jsx
9 | import NumberInfo from 'ant-design-pro/lib/NumberInfo';
10 | import numeral from 'numeral';
11 |
12 | ReactDOM.render(
13 |
14 | 本周访问}
16 | total={numeral(12321).format('0,0')}
17 | status="up"
18 | subTotal={17.1}
19 | />
20 |
21 | , mountNode);
22 | ````
23 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface INumberInfoProps {
3 | title?: React.ReactNode | string;
4 | subTitle?: React.ReactNode | string;
5 | total?: React.ReactNode | string;
6 | status?: 'up' | 'down';
7 | theme?: string;
8 | gap?: number;
9 | subTotal?: number;
10 | style?: React.CSSProperties;
11 | }
12 |
13 | export default class NumberInfo extends React.Component {}
14 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Icon } from 'antd';
3 | import classNames from 'classnames';
4 | import styles from './index.less';
5 |
6 | export default ({ theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest }) => (
7 |
13 | {title &&
{title}
}
14 | {subTitle &&
{subTitle}
}
15 |
16 |
17 | {total}
18 | {suffix && {suffix} }
19 |
20 | {(status || subTotal) && (
21 |
22 | {subTotal}
23 | {status && }
24 |
25 | )}
26 |
27 |
28 | );
29 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .numberInfo {
4 | .suffix {
5 | color: @text-color;
6 | font-size: 16px;
7 | font-style: normal;
8 | margin-left: 4px;
9 | }
10 | .numberInfoTitle {
11 | color: @text-color;
12 | font-size: @font-size-lg;
13 | margin-bottom: 16px;
14 | transition: all 0.3s;
15 | }
16 | .numberInfoSubTitle {
17 | color: @text-color-secondary;
18 | font-size: @font-size-base;
19 | height: 22px;
20 | line-height: 22px;
21 | overflow: hidden;
22 | text-overflow: ellipsis;
23 | word-break: break-all;
24 | white-space: nowrap;
25 | }
26 | .numberInfoValue {
27 | margin-top: 4px;
28 | font-size: 0;
29 | overflow: hidden;
30 | text-overflow: ellipsis;
31 | word-break: break-all;
32 | white-space: nowrap;
33 | & > span {
34 | color: @heading-color;
35 | display: inline-block;
36 | line-height: 32px;
37 | height: 32px;
38 | font-size: 24px;
39 | margin-right: 32px;
40 | }
41 | .subTotal {
42 | color: @text-color-secondary;
43 | font-size: @font-size-lg;
44 | vertical-align: top;
45 | margin-right: 0;
46 | i {
47 | font-size: 12px;
48 | transform: scale(0.82);
49 | margin-left: 4px;
50 | }
51 | :global {
52 | .anticon-caret-up {
53 | color: @red-6;
54 | }
55 | .anticon-caret-down {
56 | color: @green-6;
57 | }
58 | }
59 | }
60 | }
61 | }
62 | .numberInfolight {
63 | .numberInfoValue {
64 | & > span {
65 | color: @text-color;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: NumberInfo
4 | zh-CN: NumberInfo
5 | subtitle: 数据文本
6 | cols: 1
7 | order: 10
8 | ---
9 |
10 | 常用在数据卡片中,用于突出展示某个业务数据。
11 |
12 | ## API
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | title | 标题 | ReactNode\|string | -
17 | subTitle | 子标题 | ReactNode\|string | -
18 | total | 总量 | ReactNode\|string | -
19 | subTotal | 子总量 | ReactNode\|string | -
20 | status | 增加状态 | 'up \| down' | -
21 | theme | 状态样式 | string | 'light'
22 | gap | 设置数字和描述直接的间距(像素) | number | 8
23 |
--------------------------------------------------------------------------------
/src/components/PageHeader/demo/image.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 2
3 | title: With Image
4 | ---
5 |
6 | 带图片的页头。
7 |
8 | ````jsx
9 | import PageHeader from 'ant-design-pro/lib/PageHeader';
10 |
11 | const content = (
12 |
13 |
段落示意:蚂蚁金服务设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。
14 |
25 |
26 | );
27 |
28 | const extra = (
29 |
30 |
31 |
32 | );
33 |
34 | const breadcrumbList = [{
35 | title: '一级菜单',
36 | href: '/',
37 | }, {
38 | title: '二级菜单',
39 | href: '/',
40 | }, {
41 | title: '三级菜单',
42 | }];
43 |
44 | ReactDOM.render(
45 |
53 | , mountNode);
54 | ````
55 |
56 |
76 |
--------------------------------------------------------------------------------
/src/components/PageHeader/demo/simple.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 3
3 | title: Simple
4 | ---
5 |
6 | 简单的页头。
7 |
8 | ````jsx
9 | import PageHeader from 'ant-design-pro/lib/PageHeader';
10 |
11 | const breadcrumbList = [{
12 | title: '一级菜单',
13 | href: '/',
14 | }, {
15 | title: '二级菜单',
16 | href: '/',
17 | }, {
18 | title: '三级菜单',
19 | }];
20 |
21 | ReactDOM.render(
22 |
25 | , mountNode);
26 | ````
27 |
28 |
33 |
--------------------------------------------------------------------------------
/src/components/PageHeader/demo/structure.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: Structure
4 | ---
5 |
6 | 基本结构,具备响应式布局功能,主要断点为 768px 和 576px,拖动窗口改变大小试试看。
7 |
8 | ````jsx
9 | import PageHeader from 'ant-design-pro/lib/PageHeader';
10 |
11 | const breadcrumbList = [{
12 | title: '面包屑',
13 | }];
14 |
15 | const tabList = [{
16 | key: '1',
17 | tab: '页签一',
18 | }, {
19 | key: '2',
20 | tab: '页签二',
21 | }, {
22 | key: '3',
23 | tab: '页签三',
24 | }];
25 |
26 | ReactDOM.render(
27 | }
31 | logo={logo
}
32 | action={action
}
33 | content={content
}
34 | extraContent={extraContent
}
35 | breadcrumbList={breadcrumbList}
36 | tabList={tabList}
37 | tabActiveKey="1"
38 | />
39 |
40 | , mountNode);
41 | ````
42 |
43 |
69 |
--------------------------------------------------------------------------------
/src/components/PageHeader/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IPageHeaderProps {
3 | title?: React.ReactNode | string;
4 | logo?: React.ReactNode | string;
5 | action?: React.ReactNode | string;
6 | content?: React.ReactNode;
7 | extraContent?: React.ReactNode;
8 | routes?: any[];
9 | params?: any;
10 | breadcrumbList?: Array<{ title: React.ReactNode; href?: string }>;
11 | tabList?: Array<{ key: string; tab: React.ReactNode }>;
12 | tabActiveKey?: string;
13 | tabDefaultActiveKey?: string;
14 | onTabChange?: (key: string) => void;
15 | tabBarExtraContent?: React.ReactNode;
16 | linkElement?: React.ReactNode;
17 | style?: React.CSSProperties;
18 | }
19 |
20 | export default class PageHeader extends React.Component {}
21 |
--------------------------------------------------------------------------------
/src/components/PageHeader/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: PageHeader
4 | zh-CN: PageHeader
5 | subtitle: 页头
6 | cols: 1
7 | order: 11
8 | ---
9 |
10 | 页头用来声明页面的主题,包含了用户所关注的最重要的信息,使用户可以快速理解当前页面是什么以及它的功能。
11 |
12 | ## API
13 |
14 | | 参数 | 说明 | 类型 | 默认值 |
15 | |----------|------------------------------------------|-------------|-------|
16 | | title | title 区域 | ReactNode | - |
17 | | logo | logo区域 | ReactNode | - |
18 | | action | 操作区,位于 title 行的行尾 | ReactNode | - |
19 | | content | 内容区 | ReactNode | - |
20 | | extraContent | 额外内容区,位于content的右侧 | ReactNode | - |
21 | | breadcrumbList | 面包屑数据,配置了此属性时 `routes` `params` `location` `breadcrumbNameMap` 无效 | array<{title: ReactNode, href?: string}> | - |
22 | | routes | 面包屑相关属性,router 的路由栈信息 | object[] | - |
23 | | params | 面包屑相关属性,路由的参数 | object | - |
24 | | location | 面包屑相关属性,当前的路由信息 | object | - |
25 | | breadcrumbNameMap | 面包屑相关属性,路由的地址-名称映射表 | object | - |
26 | | tabList | tab 标题列表 | array<{key: string, tab: ReactNode}> | - |
27 | | tabActiveKey | 当前高亮的 tab 项 | string | - |
28 | | tabDefaultActiveKey | 默认高亮的 tab 项 | string | 第一项 |
29 | | onTabChange | 切换面板的回调 | (key) => void | - |
30 | | linkElement | 定义链接的元素,默认为 `a`,可传入 react-router 的 Link | string\|ReactElement | - |
31 |
32 | > 面包屑的配置方式有三种,一是直接配置 `breadcrumbList`,二是结合 `react-router@2` `react-router@3`,配置 `routes` 及 `params` 实现,类似 [面包屑 Demo](https://ant.design/components/breadcrumb-cn/#components-breadcrumb-demo-router),三是结合 `react-router@4`,配置 `location` `breadcrumbNameMap`,优先级依次递减,脚手架中使用最后一种。 对于后两种用法,你也可以将 `routes` `params` 及 `location` `breadcrumbNameMap` 放到 context 中,组件会自动获取。
33 |
--------------------------------------------------------------------------------
/src/components/PageHeader/index.test.js:
--------------------------------------------------------------------------------
1 | import { getBreadcrumb } from './index';
2 | import { urlToList } from '../_utils/pathTools';
3 |
4 | const routerData = {
5 | '/dashboard/analysis': {
6 | name: '分析页',
7 | },
8 | '/userinfo': {
9 | name: '用户列表',
10 | },
11 | '/userinfo/:id': {
12 | name: '用户信息',
13 | },
14 | '/userinfo/:id/addr': {
15 | name: '收货订单',
16 | },
17 | };
18 | describe('test getBreadcrumb', () => {
19 | it('Simple url', () => {
20 | expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual('分析页');
21 | });
22 | it('Parameters url', () => {
23 | expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual('用户信息');
24 | });
25 | it('The middle parameter url', () => {
26 | expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual('收货订单');
27 | });
28 | it('Loop through the parameters', () => {
29 | const urlNameList = urlToList('/userinfo/2144/addr').map(url => {
30 | return getBreadcrumb(routerData, url).name;
31 | });
32 | expect(urlNameList).toEqual(['用户列表', '用户信息', '收货订单']);
33 | });
34 |
35 | it('a path', () => {
36 | const urlNameList = urlToList('/userinfo').map(url => {
37 | return getBreadcrumb(routerData, url).name;
38 | });
39 | expect(urlNameList).toEqual(['用户列表']);
40 | });
41 | it('Secondary path', () => {
42 | const urlNameList = urlToList('/userinfo/2144').map(url => {
43 | return getBreadcrumb(routerData, url).name;
44 | });
45 | expect(urlNameList).toEqual(['用户列表', '用户信息']);
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/src/components/Result/demo/error.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 2
3 | title: Failed
4 | ---
5 |
6 | 提交失败。
7 |
8 | ````jsx
9 | import Result from 'ant-design-pro/lib/Result';
10 | import { Button, Icon } from 'antd';
11 |
12 | const extra = (
13 |
14 |
15 | 您提交的内容有如下错误:
16 |
17 |
18 |
您的账户已被冻结
19 |
立即解冻
20 |
21 |
22 |
您的账户还不具备申请资格
23 |
立即升级
24 |
25 |
26 | );
27 |
28 | const actions = 返回修改 ;
29 |
30 | ReactDOM.render(
31 |
38 | , mountNode);
39 | ````
40 |
--------------------------------------------------------------------------------
/src/components/Result/demo/structure.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: Structure
4 | ---
5 |
6 | 结构包含 `处理结果`,`补充信息` 以及 `操作建议` 三个部分,其中 `处理结果` 由 `提示图标`,`标题` 和 `结果描述` 组成。
7 |
8 | ````jsx
9 | import Result from 'ant-design-pro/lib/Result';
10 |
11 | ReactDOM.render(
12 | 标题}
15 | description={结果描述
}
16 | extra="其他补充信息,自带灰底效果"
17 | actions={操作建议,一般放置按钮组
}
18 | />
19 | , mountNode);
20 | ````
21 |
--------------------------------------------------------------------------------
/src/components/Result/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IResultProps {
3 | type: 'success' | 'error';
4 | title: React.ReactNode;
5 | description?: React.ReactNode;
6 | extra?: React.ReactNode;
7 | actions?: React.ReactNode;
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export default class Result extends React.Component {}
12 |
--------------------------------------------------------------------------------
/src/components/Result/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import { Icon } from 'antd';
4 | import styles from './index.less';
5 |
6 | export default function Result({
7 | className,
8 | type,
9 | title,
10 | description,
11 | extra,
12 | actions,
13 | ...restProps
14 | }) {
15 | const iconMap = {
16 | error: ,
17 | success: ,
18 | };
19 | const clsString = classNames(styles.result, className);
20 | return (
21 |
22 |
{iconMap[type]}
23 |
{title}
24 | {description &&
{description}
}
25 | {extra &&
{extra}
}
26 | {actions &&
{actions}
}
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Result/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .result {
4 | text-align: center;
5 | width: 72%;
6 | margin: 0 auto;
7 |
8 | .icon {
9 | font-size: 72px;
10 | line-height: 72px;
11 | margin-bottom: 24px;
12 |
13 | & > .success {
14 | color: @success-color;
15 | }
16 |
17 | & > .error {
18 | color: @error-color;
19 | }
20 | }
21 |
22 | .title {
23 | font-size: 24px;
24 | color: @heading-color;
25 | font-weight: 500;
26 | line-height: 32px;
27 | margin-bottom: 16px;
28 | }
29 |
30 | .description {
31 | font-size: 14px;
32 | line-height: 22px;
33 | color: @text-color-secondary;
34 | margin-bottom: 24px;
35 | }
36 |
37 | .extra {
38 | background: #fafafa;
39 | padding: 24px 40px;
40 | border-radius: @border-radius-sm;
41 | text-align: left;
42 | }
43 |
44 | .actions {
45 | margin-top: 32px;
46 |
47 | button:not(:last-child) {
48 | margin-right: 8px;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/Result/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: Result
4 | zh-CN: Result
5 | subtitle: 处理结果
6 | cols: 1
7 | order: 12
8 | ---
9 |
10 | 结果页用于对用户进行的一系列任务处理结果进行反馈。
11 |
12 | ## API
13 |
14 | | 参数 | 说明 | 类型 | 默认值 |
15 | |----------|------------------------------------------|-------------|-------|
16 | | type | 类型,不同类型自带对应的图标 | Enum {'success', 'error'} | - |
17 | | title | 标题 | ReactNode | - |
18 | | description | 结果描述 | ReactNode | - |
19 | | extra | 补充信息,有默认的灰色背景 | ReactNode | - |
20 | | actions | 操作建议,推荐放置跳转链接,按钮组等 | ReactNode | - |
21 |
--------------------------------------------------------------------------------
/src/components/SiderMenu/SilderMenu.test.js:
--------------------------------------------------------------------------------
1 | import { getMeunMatcheys } from './SiderMenu';
2 |
3 | const meun = ['/dashboard', '/userinfo', '/dashboard/name', '/userinfo/:id', '/userinfo/:id/info'];
4 |
5 | describe('test meun match', () => {
6 | it('simple path', () => {
7 | expect(getMeunMatcheys(meun, '/dashboard')).toEqual(['/dashboard']);
8 | });
9 | it('error path', () => {
10 | expect(getMeunMatcheys(meun, '/dashboardname')).toEqual([]);
11 | });
12 |
13 | it('Secondary path', () => {
14 | expect(getMeunMatcheys(meun, '/dashboard/name')).toEqual(['/dashboard/name']);
15 | });
16 |
17 | it('Parameter path', () => {
18 | expect(getMeunMatcheys(meun, '/userinfo/2144')).toEqual(['/userinfo/:id']);
19 | });
20 |
21 | it('three parameter path', () => {
22 | expect(getMeunMatcheys(meun, '/userinfo/2144/info')).toEqual(['/userinfo/:id/info']);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/SiderMenu/index.js:
--------------------------------------------------------------------------------
1 | import 'rc-drawer-menu/assets/index.css';
2 | import React from 'react';
3 | import DrawerMenu from 'rc-drawer-menu';
4 | import SiderMenu from './SiderMenu';
5 |
6 | export default props =>
7 | props.isMobile ? (
8 | {
14 | props.onCollapse(true);
15 | }}
16 | width="256px"
17 | >
18 |
19 |
20 | ) : (
21 |
22 | );
23 |
--------------------------------------------------------------------------------
/src/components/SiderMenu/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86);
3 | .logo {
4 | height: 64px;
5 | position: relative;
6 | line-height: 64px;
7 | padding-left: (@menu-collapsed-width - 32px) / 2;
8 | transition: all 0.3s;
9 | background: #002140;
10 | overflow: hidden;
11 | img {
12 | display: inline-block;
13 | vertical-align: middle;
14 | height: 32px;
15 | }
16 | h1 {
17 | color: white;
18 | display: inline-block;
19 | vertical-align: middle;
20 | font-size: 20px;
21 | margin: 0 0 0 12px;
22 | font-family: 'Myriad Pro', 'Helvetica Neue', Arial, Helvetica, sans-serif;
23 | font-weight: 600;
24 | }
25 | }
26 |
27 | .sider {
28 | min-height: 100vh;
29 | box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
30 | position: relative;
31 | z-index: 10;
32 | &.ligth {
33 | background-color: white;
34 | .logo {
35 | background: white;
36 | h1 {
37 | color: #002140;
38 | }
39 | }
40 | }
41 | }
42 |
43 | .icon {
44 | width: 14px;
45 | margin-right: 10px;
46 | }
47 |
48 | :global {
49 | .drawer .drawer-content {
50 | background: #001529;
51 | }
52 | .ant-menu-inline-collapsed {
53 | & > .ant-menu-item .sider-menu-item-img + span,
54 | &
55 | > .ant-menu-item-group
56 | > .ant-menu-item-group-list
57 | > .ant-menu-item
58 | .sider-menu-item-img
59 | + span,
60 | & > .ant-menu-submenu > .ant-menu-submenu-title .sider-menu-item-img + span {
61 | max-width: 0;
62 | display: inline-block;
63 | opacity: 0;
64 | }
65 | }
66 | .ant-menu-item .sider-menu-item-img + span,
67 | .ant-menu-submenu-title .sider-menu-item-img + span {
68 | transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
69 | opacity: 1;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/StandardFormRow/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import styles from './index.less';
4 |
5 | export default ({ title, children, last, block, grid, ...rest }) => {
6 | const cls = classNames(styles.standardFormRow, {
7 | [styles.standardFormRowBlock]: block,
8 | [styles.standardFormRowLast]: last,
9 | [styles.standardFormRowGrid]: grid,
10 | });
11 |
12 | return (
13 |
14 | {title && (
15 |
16 | {title}
17 |
18 | )}
19 |
{children}
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/StandardFormRow/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .standardFormRow {
4 | border-bottom: 1px dashed @border-color-split;
5 | padding-bottom: 16px;
6 | margin-bottom: 16px;
7 | display: flex;
8 | :global {
9 | .ant-form-item {
10 | margin-right: 24px;
11 | }
12 | .ant-form-item-label label {
13 | color: @text-color;
14 | margin-right: 0;
15 | }
16 | .ant-form-item-label,
17 | .ant-form-item-control {
18 | padding: 0;
19 | line-height: 32px;
20 | }
21 | }
22 | .label {
23 | color: @heading-color;
24 | font-size: @font-size-base;
25 | margin-right: 24px;
26 | flex: 0 0 auto;
27 | text-align: right;
28 | & > span {
29 | display: inline-block;
30 | height: 32px;
31 | line-height: 32px;
32 | &:after {
33 | content: ':';
34 | }
35 | }
36 | }
37 | .content {
38 | flex: 1 1 0;
39 | :global {
40 | .ant-form-item:last-child {
41 | margin-right: 0;
42 | }
43 | }
44 | }
45 | }
46 |
47 | .standardFormRowLast {
48 | border: none;
49 | padding-bottom: 0;
50 | margin-bottom: 0;
51 | }
52 |
53 | .standardFormRowBlock {
54 | :global {
55 | .ant-form-item,
56 | div.ant-form-item-control-wrapper {
57 | display: block;
58 | }
59 | }
60 | }
61 |
62 | .standardFormRowGrid {
63 | :global {
64 | .ant-form-item,
65 | div.ant-form-item-control-wrapper {
66 | display: block;
67 | }
68 | .ant-form-item-label {
69 | float: left;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/StandardTable/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .standardTable {
4 | :global {
5 | .ant-table-pagination {
6 | margin-top: 24px;
7 | }
8 | }
9 |
10 | .tableAlert {
11 | margin-bottom: 16px;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/TagSelect/TagSelectOption.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export interface ITagSelectOptionProps {
4 | value: string | number;
5 | style?: React.CSSProperties;
6 | }
7 |
8 | export default class TagSelectOption extends React.Component {}
9 |
--------------------------------------------------------------------------------
/src/components/TagSelect/demo/expandable.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title: 可展开和收起
4 | ---
5 |
6 | 使用 `expandable` 属性,让标签组可以收起,避免过高。
7 |
8 | ````jsx
9 | import TagSelect from 'ant-design-pro/lib/TagSelect';
10 |
11 | function handleFormSubmit(checkedValue) {
12 | console.log(checkedValue);
13 | }
14 |
15 | ReactDOM.render(
16 |
17 | 类目一
18 | 类目二
19 | 类目三
20 | 类目四
21 | 类目五
22 | 类目六
23 | 类目七
24 | 类目八
25 | 类目九
26 | 类目十
27 | 类目十一
28 | 类目十二
29 |
30 | , mountNode);
31 | ````
32 |
--------------------------------------------------------------------------------
/src/components/TagSelect/demo/simple.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 基础样例
4 | ---
5 |
6 | 结合 `Tag` 的 `TagSelect` 组件,方便的应用于筛选类目的业务场景中。
7 |
8 | ````jsx
9 | import TagSelect from 'ant-design-pro/lib/TagSelect';
10 |
11 | function handleFormSubmit(checkedValue) {
12 | console.log(checkedValue);
13 | }
14 |
15 | ReactDOM.render(
16 |
17 | 类目一
18 | 类目二
19 | 类目三
20 | 类目四
21 | 类目五
22 | 类目六
23 |
24 | , mountNode);
25 | ````
26 |
--------------------------------------------------------------------------------
/src/components/TagSelect/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TagSelectOption from './TagSelectOption';
3 |
4 | export interface ITagSelectProps {
5 | onChange?: (value: string[]) => void;
6 | expandable?: boolean;
7 | value?: string[] | number[];
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export default class TagSelect extends React.Component {
12 | public static Option: typeof TagSelectOption;
13 | private children:
14 | | React.ReactElement
15 | | Array>;
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/TagSelect/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .tagSelect {
4 | user-select: none;
5 | margin-left: -8px;
6 | position: relative;
7 | overflow: hidden;
8 | max-height: 32px;
9 | line-height: 32px;
10 | transition: all 0.3s;
11 | :global {
12 | .ant-tag {
13 | padding: 0 8px;
14 | margin-right: 24px;
15 | font-size: @font-size-base;
16 | }
17 | }
18 | &.expanded {
19 | transition: all 0.3s;
20 | max-height: 200px;
21 | }
22 | .trigger {
23 | position: absolute;
24 | top: 0;
25 | right: 0;
26 | i {
27 | font-size: 12px;
28 | }
29 | }
30 | &.hasExpandTag {
31 | padding-right: 50px;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/TagSelect/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: TagSelect
4 | zh-CN: TagSelect
5 | subtitle: 标签选择器
6 | cols: 1
7 | order: 13
8 | ---
9 |
10 | 可进行多选,带折叠收起和展开更多功能,常用于对列表进行筛选。
11 |
12 | ## API
13 |
14 | ### TagSelect
15 |
16 | | 参数 | 说明 | 类型 | 默认值 |
17 | |----------|------------------------------------------|-------------|-------|
18 | | value |选中的项 |string[] \| number[] | |
19 | | defaultValue |默认选中的项 |string[] \| number[] | |
20 | | onChange | 标签选择的回调函数 | Function(checkedTags) | |
21 | | expandable | 是否展示 `展开/收起` 按钮 | Boolean | false |
22 |
23 |
24 | ### TagSelectOption
25 |
26 | | 参数 | 说明 | 类型 | 默认值 |
27 | |----------|------------------------------------------|-------------|-------|
28 | | value | TagSelect的值 | string\| number | - |
29 | | children | tag的内容 | string \| ReactNode | - |
30 |
--------------------------------------------------------------------------------
/src/components/Trend/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 演示
4 | ---
5 |
6 | 在数值背后添加一个小图标来标识涨跌情况。
7 |
8 | ````jsx
9 | import Trend from 'ant-design-pro/lib/Trend';
10 |
11 | ReactDOM.render(
12 |
13 | 12%
14 | 11%
15 |
16 | , mountNode);
17 | ````
18 |
--------------------------------------------------------------------------------
/src/components/Trend/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export interface ITrendProps {
4 | colorful?: boolean;
5 | flag: 'up' | 'down';
6 | style?: React.CSSProperties;
7 | }
8 |
9 | export default class Trend extends React.Component {}
10 |
--------------------------------------------------------------------------------
/src/components/Trend/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Icon } from 'antd';
3 | import classNames from 'classnames';
4 | import styles from './index.less';
5 |
6 | const Trend = ({ colorful = true, flag, children, className, ...rest }) => {
7 | const classString = classNames(
8 | styles.trendItem,
9 | {
10 | [styles.trendItemGrey]: !colorful,
11 | },
12 | className
13 | );
14 | return (
15 |
16 | {children}
17 | {flag && (
18 |
19 |
20 |
21 | )}
22 |
23 | );
24 | };
25 |
26 | export default Trend;
27 |
--------------------------------------------------------------------------------
/src/components/Trend/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .trendItem {
4 | display: inline-block;
5 | font-size: @font-size-base;
6 | line-height: 22px;
7 |
8 | .up,
9 | .down {
10 | margin-left: 4px;
11 | position: relative;
12 | top: 1px;
13 | i {
14 | font-size: 12px;
15 | transform: scale(0.83);
16 | }
17 | }
18 | .up {
19 | color: @red-6;
20 | }
21 | .down {
22 | color: @green-6;
23 | top: -1px;
24 | }
25 |
26 | &.trendItemGrey .up,
27 | &.trendItemGrey .down {
28 | color: @text-color;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Trend/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: Trend
4 | zh-CN: Trend
5 | subtitle: 趋势标记
6 | cols: 1
7 | order: 14
8 | ---
9 |
10 | 趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。
11 |
12 | ## API
13 |
14 | ```html
15 | 50%
16 | ```
17 |
18 | | 参数 | 说明 | 类型 | 默认值 |
19 | |----------|------------------------------------------|-------------|-------|
20 | | colorful | 是否彩色标记 | Boolean | true |
21 | | flag | 上升下降标识:`up|down` | string | - |
22 |
--------------------------------------------------------------------------------
/src/components/_utils/pathTools.js:
--------------------------------------------------------------------------------
1 | // /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
2 | export function urlToList(url) {
3 | const urllist = url.split('/').filter(i => i);
4 | return urllist.map((urlItem, index) => {
5 | return `/${urllist.slice(0, index + 1).join('/')}`;
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/_utils/pathTools.test.js:
--------------------------------------------------------------------------------
1 | import { urlToList } from './pathTools';
2 |
3 | describe('test urlToList', () => {
4 | it('A path', () => {
5 | expect(urlToList('/userinfo')).toEqual(['/userinfo']);
6 | });
7 | it('Secondary path', () => {
8 | expect(urlToList('/userinfo/2144')).toEqual(['/userinfo', '/userinfo/2144']);
9 | });
10 | it('Three paths', () => {
11 | expect(urlToList('/userinfo/2144/addr')).toEqual([
12 | '/userinfo',
13 | '/userinfo/2144',
14 | '/userinfo/2144/addr',
15 | ]);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/e2e/home.e2e.js:
--------------------------------------------------------------------------------
1 | import puppeteer from 'puppeteer';
2 |
3 | describe('Homepage', () => {
4 | it('it should have logo text', async () => {
5 | const browser = await puppeteer.launch();
6 | const page = await browser.newPage();
7 | await page.goto('http://localhost:8000');
8 | await page.waitForSelector('h1');
9 | const text = await page.evaluate(() => document.body.innerHTML);
10 | expect(text).toContain('Ant Tabs ');
11 | await page.close();
12 | browser.close();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/e2e/login.e2e.js:
--------------------------------------------------------------------------------
1 | import puppeteer from 'puppeteer';
2 |
3 | describe('Login', () => {
4 | let browser;
5 | let page;
6 |
7 | beforeAll(async () => {
8 | browser = await puppeteer.launch();
9 | });
10 |
11 | beforeEach(async () => {
12 | page = await browser.newPage();
13 | await page.goto('http://localhost:8000/#/user/login');
14 | await page.evaluate(() => window.localStorage.setItem('antd-pro-authority', 'guest'));
15 | });
16 |
17 | afterEach(() => page.close());
18 |
19 | it('should login with failure', async () => {
20 | await page.type('#userName', 'mockuser');
21 | await page.type('#password', 'wrong_password');
22 | await page.click('button[type="submit"]');
23 | await page.waitForSelector('.ant-alert-error'); // should display error
24 | });
25 |
26 | it('should login successfully', async () => {
27 | await page.type('#userName', 'admin');
28 | await page.type('#password', '888888');
29 | await page.click('button[type="submit"]');
30 | await page.waitForSelector('.ant-layout-sider h1'); // should display error
31 | const text = await page.evaluate(() => document.body.innerHTML);
32 | expect(text).toContain('Ant Tabs ');
33 | });
34 |
35 | afterAll(() => browser.close());
36 | });
37 |
--------------------------------------------------------------------------------
/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Ant Tabs
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import '@babel/polyfill';
2 | import 'url-polyfill';
3 | import dva from 'dva';
4 |
5 | import createHistory from 'history/createHashHistory';
6 | // user BrowserHistory
7 | // import createHistory from 'history/createBrowserHistory';
8 | import createLoading from 'dva-loading';
9 | import 'moment/locale/zh-cn';
10 | import './rollbar';
11 |
12 | import './index.less';
13 | // 1. Initialize
14 | const app = dva({
15 | history: createHistory(),
16 | });
17 |
18 | // 2. Plugins
19 | app.use(createLoading());
20 |
21 | // 3. Register global model
22 | app.model(require('./models/global').default);
23 |
24 | // 4. Router
25 | app.router(require('./router').default);
26 |
27 | // 5. Start
28 | app.start('#root');
29 |
30 | export default app._store; // eslint-disable-line
31 |
--------------------------------------------------------------------------------
/src/layouts/BlankLayout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default props =>
;
4 |
--------------------------------------------------------------------------------
/src/layouts/PageHeaderLayout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'dva/router';
3 | import PageHeader from '../components/PageHeader';
4 | import styles from './PageHeaderLayout.less';
5 |
6 | export default ({ children, wrapperClassName, top, ...restProps }) => (
7 |
8 | {top}
9 |
10 | {children ?
{children}
: null}
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/src/layouts/PageHeaderLayout.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .content {
4 | margin: 24px 24px 0;
5 | }
6 |
7 | @media screen and (max-width: @screen-sm) {
8 | .content {
9 | margin: 24px 0 0;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .container {
4 | display: flex;
5 | flex-direction: column;
6 | min-height: 100%;
7 | background: #f0f2f5;
8 | }
9 |
10 | .content {
11 | padding: 32px 0;
12 | flex: 1;
13 | }
14 |
15 | @media (min-width: @screen-md-min) {
16 | .container {
17 | background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
18 | background-repeat: no-repeat;
19 | background-position: center 110px;
20 | background-size: 100%;
21 | }
22 |
23 | .content {
24 | padding: 112px 0 24px 0;
25 | }
26 | }
27 |
28 | .top {
29 | text-align: center;
30 | }
31 |
32 | .header {
33 | height: 44px;
34 | line-height: 44px;
35 | a {
36 | text-decoration: none;
37 | }
38 | }
39 |
40 | .logo {
41 | height: 44px;
42 | vertical-align: top;
43 | margin-right: 16px;
44 | }
45 |
46 | .title {
47 | font-size: 33px;
48 | color: @heading-color;
49 | font-family: 'Myriad Pro', 'Helvetica Neue', Arial, Helvetica, sans-serif;
50 | font-weight: 600;
51 | position: relative;
52 | top: 2px;
53 | }
54 |
55 | .desc {
56 | font-size: @font-size-base;
57 | color: @text-color-secondary;
58 | margin-top: 12px;
59 | margin-bottom: 40px;
60 | }
61 |
--------------------------------------------------------------------------------
/src/models/activities.js:
--------------------------------------------------------------------------------
1 | import { queryActivities } from '../services/api';
2 |
3 | export default {
4 | namespace: 'activities',
5 |
6 | state: {
7 | list: [],
8 | },
9 |
10 | effects: {
11 | *fetchList(_, { call, put }) {
12 | const response = yield call(queryActivities);
13 | yield put({
14 | type: 'saveList',
15 | payload: Array.isArray(response) ? response : [],
16 | });
17 | },
18 | },
19 |
20 | reducers: {
21 | saveList(state, action) {
22 | return {
23 | ...state,
24 | list: action.payload,
25 | };
26 | },
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/models/chart.js:
--------------------------------------------------------------------------------
1 | import { fakeChartData } from '../services/api';
2 |
3 | export default {
4 | namespace: 'chart',
5 |
6 | state: {
7 | visitData: [],
8 | visitData2: [],
9 | salesData: [],
10 | searchData: [],
11 | offlineData: [],
12 | offlineChartData: [],
13 | salesTypeData: [],
14 | salesTypeDataOnline: [],
15 | salesTypeDataOffline: [],
16 | radarData: [],
17 | loading: false,
18 | },
19 |
20 | effects: {
21 | *fetch(_, { call, put }) {
22 | const response = yield call(fakeChartData);
23 | yield put({
24 | type: 'save',
25 | payload: response,
26 | });
27 | },
28 | *fetchSalesData(_, { call, put }) {
29 | const response = yield call(fakeChartData);
30 | yield put({
31 | type: 'save',
32 | payload: {
33 | salesData: response.salesData,
34 | },
35 | });
36 | },
37 | },
38 |
39 | reducers: {
40 | save(state, { payload }) {
41 | return {
42 | ...state,
43 | ...payload,
44 | };
45 | },
46 | clear() {
47 | return {
48 | visitData: [],
49 | visitData2: [],
50 | salesData: [],
51 | searchData: [],
52 | offlineData: [],
53 | offlineChartData: [],
54 | salesTypeData: [],
55 | salesTypeDataOnline: [],
56 | salesTypeDataOffline: [],
57 | radarData: [],
58 | };
59 | },
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/src/models/error.js:
--------------------------------------------------------------------------------
1 | import { routerRedux } from 'dva/router';
2 | import { query } from '../services/error';
3 |
4 | export default {
5 | namespace: 'error',
6 |
7 | state: {
8 | error: '',
9 | isloading: false,
10 | },
11 |
12 | effects: {
13 | *query({ payload }, { call, put }) {
14 | yield call(query, payload.code);
15 | // redirect on client when network broken
16 | yield put(routerRedux.push(`/exception/${payload.code}`));
17 | yield put({
18 | type: 'trigger',
19 | payload: payload.code,
20 | });
21 | },
22 | },
23 |
24 | reducers: {
25 | trigger(state, action) {
26 | return {
27 | error: action.payload,
28 | };
29 | },
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/src/models/form.js:
--------------------------------------------------------------------------------
1 | import { routerRedux } from 'dva/router';
2 | import { message } from 'antd';
3 | import { fakeSubmitForm } from '../services/api';
4 |
5 | export default {
6 | namespace: 'form',
7 |
8 | state: {
9 | step: {
10 | payAccount: 'ant-design@alipay.com',
11 | receiverAccount: 'test@example.com',
12 | receiverName: 'Alex',
13 | amount: '500',
14 | },
15 | },
16 |
17 | effects: {
18 | *submitRegularForm({ payload }, { call }) {
19 | yield call(fakeSubmitForm, payload);
20 | message.success('提交成功');
21 | },
22 | *submitStepForm({ payload }, { call, put }) {
23 | yield call(fakeSubmitForm, payload);
24 | yield put({
25 | type: 'saveStepFormData',
26 | payload,
27 | });
28 | yield put(routerRedux.push('/form/step-form/result'));
29 | },
30 | *submitAdvancedForm({ payload }, { call }) {
31 | yield call(fakeSubmitForm, payload);
32 | message.success('提交成功');
33 | },
34 | },
35 |
36 | reducers: {
37 | saveStepFormData(state, { payload }) {
38 | return {
39 | ...state,
40 | step: {
41 | ...state.step,
42 | ...payload,
43 | },
44 | };
45 | },
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/src/models/global.js:
--------------------------------------------------------------------------------
1 | import { queryNotices } from '../services/api';
2 |
3 | export default {
4 | namespace: 'global',
5 |
6 | state: {
7 | collapsed: false,
8 | notices: [],
9 | },
10 |
11 | effects: {
12 | *fetchNotices(_, { call, put }) {
13 | const data = yield call(queryNotices);
14 | yield put({
15 | type: 'saveNotices',
16 | payload: data,
17 | });
18 | yield put({
19 | type: 'user/changeNotifyCount',
20 | payload: data.length,
21 | });
22 | },
23 | *clearNotices({ payload }, { put, select }) {
24 | yield put({
25 | type: 'saveClearedNotices',
26 | payload,
27 | });
28 | const count = yield select(state => state.global.notices.length);
29 | yield put({
30 | type: 'user/changeNotifyCount',
31 | payload: count,
32 | });
33 | },
34 | },
35 |
36 | reducers: {
37 | changeLayoutCollapsed(state, { payload }) {
38 | return {
39 | ...state,
40 | collapsed: payload,
41 | };
42 | },
43 | saveNotices(state, { payload }) {
44 | return {
45 | ...state,
46 | notices: payload,
47 | };
48 | },
49 | saveClearedNotices(state, { payload }) {
50 | return {
51 | ...state,
52 | notices: state.notices.filter(item => item.type !== payload),
53 | };
54 | },
55 | },
56 |
57 | subscriptions: {
58 | setup({ history }) {
59 | // Subscribe history(url) change, trigger `load` action if pathname is `/`
60 | return history.listen(({ pathname, search }) => {
61 | if (typeof window.ga !== 'undefined') {
62 | window.ga('send', 'pageview', pathname + search);
63 | }
64 | });
65 | },
66 | },
67 | };
68 |
--------------------------------------------------------------------------------
/src/models/index.js:
--------------------------------------------------------------------------------
1 | // Use require.context to require reducers automatically
2 | // Ref: https://webpack.js.org/guides/dependency-management/#require-context
3 | const context = require.context('./', false, /\.js$/);
4 | export default context
5 | .keys()
6 | .filter(item => item !== './index.js')
7 | .map(key => context(key));
8 |
--------------------------------------------------------------------------------
/src/models/list.js:
--------------------------------------------------------------------------------
1 | import { queryFakeList } from '../services/api';
2 |
3 | export default {
4 | namespace: 'list',
5 |
6 | state: {
7 | list: [],
8 | },
9 |
10 | effects: {
11 | *fetch({ payload }, { call, put }) {
12 | const response = yield call(queryFakeList, payload);
13 | yield put({
14 | type: 'queryList',
15 | payload: Array.isArray(response) ? response : [],
16 | });
17 | },
18 | *appendFetch({ payload }, { call, put }) {
19 | const response = yield call(queryFakeList, payload);
20 | yield put({
21 | type: 'appendList',
22 | payload: Array.isArray(response) ? response : [],
23 | });
24 | },
25 | },
26 |
27 | reducers: {
28 | queryList(state, action) {
29 | return {
30 | ...state,
31 | list: action.payload,
32 | };
33 | },
34 | appendList(state, action) {
35 | return {
36 | ...state,
37 | list: state.list.concat(action.payload),
38 | };
39 | },
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/src/models/login.js:
--------------------------------------------------------------------------------
1 | import { routerRedux } from 'dva/router';
2 | import { fakeAccountLogin } from '../services/api';
3 | import { setAuthority } from '../utils/authority';
4 | import { reloadAuthorized } from '../utils/Authorized';
5 |
6 | export default {
7 | namespace: 'login',
8 |
9 | state: {
10 | status: undefined,
11 | },
12 |
13 | effects: {
14 | *login({ payload }, { call, put }) {
15 | const response = yield call(fakeAccountLogin, payload);
16 | yield put({
17 | type: 'changeLoginStatus',
18 | payload: response,
19 | });
20 | // Login successfully
21 | if (response.status === 'ok') {
22 | reloadAuthorized();
23 | yield put(routerRedux.push('/'));
24 | }
25 | },
26 | *logout(_, { put, select }) {
27 | try {
28 | // get location pathname
29 | const urlParams = new URL(window.location.href);
30 | const pathname = yield select(state => state.routing.location.pathname);
31 | // add the parameters in the url
32 | urlParams.searchParams.set('redirect', pathname);
33 | window.history.replaceState(null, 'login', urlParams.href);
34 | } finally {
35 | yield put({
36 | type: 'changeLoginStatus',
37 | payload: {
38 | status: false,
39 | currentAuthority: 'guest',
40 | },
41 | });
42 | reloadAuthorized();
43 | yield put(routerRedux.push('/user/login'));
44 | }
45 | },
46 | },
47 |
48 | reducers: {
49 | changeLoginStatus(state, { payload }) {
50 | setAuthority(payload.currentAuthority);
51 | return {
52 | ...state,
53 | status: payload.status,
54 | type: payload.type,
55 | };
56 | },
57 | },
58 | };
59 |
--------------------------------------------------------------------------------
/src/models/monitor.js:
--------------------------------------------------------------------------------
1 | import { queryTags } from '../services/api';
2 |
3 | export default {
4 | namespace: 'monitor',
5 |
6 | state: {
7 | tags: [],
8 | },
9 |
10 | effects: {
11 | *fetchTags(_, { call, put }) {
12 | const response = yield call(queryTags);
13 | yield put({
14 | type: 'saveTags',
15 | payload: response.list,
16 | });
17 | },
18 | },
19 |
20 | reducers: {
21 | saveTags(state, action) {
22 | return {
23 | ...state,
24 | tags: action.payload,
25 | };
26 | },
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/models/profile.js:
--------------------------------------------------------------------------------
1 | import { queryBasicProfile, queryAdvancedProfile } from '../services/api';
2 |
3 | export default {
4 | namespace: 'profile',
5 |
6 | state: {
7 | basicGoods: [],
8 | advancedOperation1: [],
9 | advancedOperation2: [],
10 | advancedOperation3: [],
11 | },
12 |
13 | effects: {
14 | *fetchBasic(_, { call, put }) {
15 | const response = yield call(queryBasicProfile);
16 | yield put({
17 | type: 'show',
18 | payload: response,
19 | });
20 | },
21 | *fetchAdvanced(_, { call, put }) {
22 | const response = yield call(queryAdvancedProfile);
23 | yield put({
24 | type: 'show',
25 | payload: response,
26 | });
27 | },
28 | },
29 |
30 | reducers: {
31 | show(state, { payload }) {
32 | return {
33 | ...state,
34 | ...payload,
35 | };
36 | },
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/src/models/project.js:
--------------------------------------------------------------------------------
1 | import { queryProjectNotice } from '../services/api';
2 |
3 | export default {
4 | namespace: 'project',
5 |
6 | state: {
7 | notice: [],
8 | },
9 |
10 | effects: {
11 | *fetchNotice(_, { call, put }) {
12 | const response = yield call(queryProjectNotice);
13 | yield put({
14 | type: 'saveNotice',
15 | payload: Array.isArray(response) ? response : [],
16 | });
17 | },
18 | },
19 |
20 | reducers: {
21 | saveNotice(state, action) {
22 | return {
23 | ...state,
24 | notice: action.payload,
25 | };
26 | },
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/models/register.js:
--------------------------------------------------------------------------------
1 | import { fakeRegister } from '../services/api';
2 | import { setAuthority } from '../utils/authority';
3 | import { reloadAuthorized } from '../utils/Authorized';
4 |
5 | export default {
6 | namespace: 'register',
7 |
8 | state: {
9 | status: undefined,
10 | },
11 |
12 | effects: {
13 | *submit(_, { call, put }) {
14 | const response = yield call(fakeRegister);
15 | yield put({
16 | type: 'registerHandle',
17 | payload: response,
18 | });
19 | },
20 | },
21 |
22 | reducers: {
23 | registerHandle(state, { payload }) {
24 | setAuthority('user');
25 | reloadAuthorized();
26 | return {
27 | ...state,
28 | status: payload.status,
29 | };
30 | },
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/src/models/rule.js:
--------------------------------------------------------------------------------
1 | import { queryRule, removeRule, addRule } from '../services/api';
2 |
3 | export default {
4 | namespace: 'rule',
5 |
6 | state: {
7 | data: {
8 | list: [],
9 | pagination: {},
10 | },
11 | },
12 |
13 | effects: {
14 | *fetch({ payload }, { call, put }) {
15 | const response = yield call(queryRule, payload);
16 | yield put({
17 | type: 'save',
18 | payload: response,
19 | });
20 | },
21 | *add({ payload, callback }, { call, put }) {
22 | const response = yield call(addRule, payload);
23 | yield put({
24 | type: 'save',
25 | payload: response,
26 | });
27 | if (callback) callback();
28 | },
29 | *remove({ payload, callback }, { call, put }) {
30 | const response = yield call(removeRule, payload);
31 | yield put({
32 | type: 'save',
33 | payload: response,
34 | });
35 | if (callback) callback();
36 | },
37 | },
38 |
39 | reducers: {
40 | save(state, action) {
41 | return {
42 | ...state,
43 | data: action.payload,
44 | };
45 | },
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/src/models/user.js:
--------------------------------------------------------------------------------
1 | import { query as queryUsers, queryCurrent } from '../services/user';
2 |
3 | export default {
4 | namespace: 'user',
5 |
6 | state: {
7 | list: [],
8 | currentUser: {},
9 | },
10 |
11 | effects: {
12 | *fetch(_, { call, put }) {
13 | const response = yield call(queryUsers);
14 | yield put({
15 | type: 'save',
16 | payload: response,
17 | });
18 | },
19 | *fetchCurrent(_, { call, put }) {
20 | const response = yield call(queryCurrent);
21 | yield put({
22 | type: 'saveCurrentUser',
23 | payload: response,
24 | });
25 | },
26 | },
27 |
28 | reducers: {
29 | save(state, action) {
30 | return {
31 | ...state,
32 | list: action.payload,
33 | };
34 | },
35 | saveCurrentUser(state, action) {
36 | return {
37 | ...state,
38 | currentUser: action.payload,
39 | };
40 | },
41 | changeNotifyCount(state, action) {
42 | return {
43 | ...state,
44 | currentUser: {
45 | ...state.currentUser,
46 | notifyCount: action.payload,
47 | },
48 | };
49 | },
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/src/rollbar.js:
--------------------------------------------------------------------------------
1 | import Rollbar from 'rollbar';
2 |
3 | // Track error by rollbar.com
4 | if (location.host === 'preview.pro.ant.design') {
5 | Rollbar.init({
6 | accessToken: '033ca6d7c0eb4cc1831cf470c2649971',
7 | captureUncaught: true,
8 | captureUnhandledRejections: true,
9 | payload: {
10 | environment: 'production',
11 | },
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { routerRedux, Route, Switch } from 'dva/router';
3 | import { LocaleProvider, Spin } from 'antd';
4 | import zhCN from 'antd/lib/locale-provider/zh_CN';
5 | import dynamic from 'dva/dynamic';
6 | import { getRouterData } from './common/router';
7 | import Authorized from './utils/Authorized';
8 | import styles from './index.less';
9 |
10 | const { ConnectedRouter } = routerRedux;
11 | const { AuthorizedRoute } = Authorized;
12 | dynamic.setDefaultLoadingComponent(() => {
13 | return ;
14 | });
15 |
16 | function RouterConfig({ history, app }) {
17 | const routerData = getRouterData(app);
18 | const UserLayout = routerData['/user'].component;
19 | const BasicLayout = routerData['/'].component;
20 | return (
21 |
22 |
23 |
24 |
25 | }
28 | authority={['admin', 'user']}
29 | redirectPath="/user/login"
30 | />
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default RouterConfig;
38 |
--------------------------------------------------------------------------------
/src/routes/Component/Hooks.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import {Card, Button } from 'antd';
3 | function Hooks(props) {
4 | // Declare a new state variable, which we'll call "count"
5 | // console.log(props);
6 | const [count, setCount] = useState(0)
7 | const [width, setWithd] = useState(window.innerWidth);
8 | // Similar to componentDidMount and componentDidUpdate:
9 | useEffect(() => {
10 | // Update the document title using the browser API
11 | document.title = `You clicked ${count} times`;
12 | const handleResize = ()=>{
13 | setWithd(window.innerWidth);
14 | }
15 | window.addEventListener('resize', handleResize);
16 |
17 | // return () => (document.title = "前端精读");
18 |
19 | },[width]);
20 |
21 | return (
22 |
23 |
24 |
25 |
窗口宽度{ width }px
26 |
You clicked {count} times
27 |
setCount(count + 1)}>
28 | Click me
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | export default Hooks;
36 |
37 |
--------------------------------------------------------------------------------
/src/routes/Component/Test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Row, Col } from 'antd';
3 | import ImageWrapper from 'components/ImageWrapper'; // aware of the relative path
4 |
5 | export default () => (
6 |
7 |
8 |
9 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
33 |
34 |
35 |
36 | );
37 |
--------------------------------------------------------------------------------
/src/routes/Component/Test.less:
--------------------------------------------------------------------------------
1 | .registerResult {
2 | :global {
3 | .anticon {
4 | font-size: 64px;
5 | }
6 | }
7 | .title {
8 | margin-top: 32px;
9 | font-size: 20px;
10 | line-height: 28px;
11 | }
12 | .actions {
13 | margin-top: 40px;
14 | a + a {
15 | margin-left: 8px;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/routes/Dashboard/Monitor.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @import '../../utils/utils.less';
3 |
4 | .mapChart {
5 | padding-top: 24px;
6 | height: 457px;
7 | text-align: center;
8 | img {
9 | display: inline-block;
10 | max-width: 100%;
11 | max-height: 437px;
12 | }
13 | }
14 |
15 | .pieCard :global(.pie-stat) {
16 | font-size: 24px !important;
17 | }
18 |
19 | @media screen and (max-width: @screen-lg) {
20 | .mapChart {
21 | height: auto;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/routes/Exception/403.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'dva/router';
3 | import Exception from 'components/Exception';
4 |
5 | export default () => (
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/routes/Exception/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'dva/router';
3 | import Exception from 'components/Exception';
4 |
5 | export default () => (
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/routes/Exception/500.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'dva/router';
3 | import Exception from 'components/Exception';
4 |
5 | export default () => (
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/routes/Exception/style.less:
--------------------------------------------------------------------------------
1 | .trigger {
2 | background: 'red';
3 | :global(.ant-btn) {
4 | margin-right: 8px;
5 | margin-bottom: 12px;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/routes/Exception/triggerException.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Button, Spin, Card } from 'antd';
3 | import { connect } from 'dva';
4 | import styles from './style.less';
5 |
6 | @connect(state => ({
7 | isloading: state.error.isloading,
8 | }))
9 | export default class TriggerException extends PureComponent {
10 | state = {
11 | isloading: false,
12 | };
13 | triggerError = code => {
14 | this.setState({
15 | isloading: true,
16 | });
17 | this.props.dispatch({
18 | type: 'error/query',
19 | payload: {
20 | code,
21 | },
22 | });
23 | };
24 | render() {
25 | return (
26 |
27 |
28 | this.triggerError(401)}>
29 | 触发401
30 |
31 | this.triggerError(403)}>
32 | 触发403
33 |
34 | this.triggerError(500)}>
35 | 触发500
36 |
37 | this.triggerError(404)}>
38 | 触发404
39 |
40 |
41 |
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/routes/Forms/StepForm/Step3.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { connect } from 'dva';
3 | import { Button, Row, Col } from 'antd';
4 | import { routerRedux } from 'dva/router';
5 | import Result from 'components/Result';
6 | import styles from './style.less';
7 |
8 | class Step3 extends React.PureComponent {
9 | render() {
10 | const { dispatch, data } = this.props;
11 | const onFinish = () => {
12 | dispatch(routerRedux.push('/form/step-form'));
13 | };
14 | const information = (
15 |
16 |
17 |
18 | 付款账户:
19 |
20 | {data.payAccount}
21 |
22 |
23 |
24 | 收款账户:
25 |
26 | {data.receiverAccount}
27 |
28 |
29 |
30 | 收款人姓名:
31 |
32 | {data.receiverName}
33 |
34 |
35 |
36 | 转账金额:
37 |
38 |
39 | {data.amount} 元
40 |
41 |
42 |
43 | );
44 | const actions = (
45 |
46 |
47 | 再转一笔
48 |
49 | 查看账单
50 |
51 | );
52 | return (
53 |
61 | );
62 | }
63 | }
64 |
65 | export default connect(({ form }) => ({
66 | data: form.step,
67 | }))(Step3);
68 |
--------------------------------------------------------------------------------
/src/routes/Forms/StepForm/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent, Fragment } from 'react';
2 | import { Route, Redirect, Switch } from 'dva/router';
3 | import { Card, Steps } from 'antd';
4 | import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
5 | import NotFound from '../../Exception/404';
6 | import { getRoutes } from '../../../utils/utils';
7 | import styles from '../style.less';
8 |
9 | const { Step } = Steps;
10 |
11 | export default class StepForm extends PureComponent {
12 | getCurrentStep() {
13 | const { location } = this.props;
14 | const { pathname } = location;
15 | const pathList = pathname.split('/');
16 | switch (pathList[pathList.length - 1]) {
17 | case 'info':
18 | return 0;
19 | case 'confirm':
20 | return 1;
21 | case 'result':
22 | return 2;
23 | default:
24 | return 0;
25 | }
26 | }
27 | render() {
28 | const { match, routerData } = this.props;
29 | return (
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {getRoutes(match.path, routerData).map(item => (
43 |
49 | ))}
50 |
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/routes/Forms/StepForm/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .stepForm {
4 | margin: 40px auto 0;
5 | max-width: 500px;
6 | }
7 |
8 | .stepFormText {
9 | margin-bottom: 24px;
10 | :global {
11 | .ant-form-item-label,
12 | .ant-form-item-control {
13 | line-height: 22px;
14 | }
15 | }
16 | }
17 |
18 | .result {
19 | margin: 0 auto;
20 | max-width: 560px;
21 | padding: 24px 0 8px;
22 | }
23 |
24 | .desc {
25 | padding: 0 56px;
26 | color: @text-color-secondary;
27 | h3 {
28 | font-size: 16px;
29 | margin: 0 0 12px 0;
30 | color: @text-color-secondary;
31 | line-height: 32px;
32 | }
33 | h4 {
34 | margin: 0 0 4px 0;
35 | color: @text-color-secondary;
36 | font-size: 14px;
37 | line-height: 22px;
38 | }
39 | p {
40 | margin-top: 0;
41 | margin-bottom: 12px;
42 | line-height: 22px;
43 | }
44 | }
45 |
46 | @media screen and (max-width: @screen-md) {
47 | .desc {
48 | padding: 0;
49 | }
50 | }
51 |
52 | .information {
53 | line-height: 22px;
54 | :global {
55 | .ant-row:not(:last-child) {
56 | margin-bottom: 24px;
57 | }
58 | }
59 | .label {
60 | color: @heading-color;
61 | text-align: right;
62 | padding-right: 8px;
63 | }
64 | }
65 |
66 | .money {
67 | font-family: 'Helvetica Neue', sans-serif;
68 | font-weight: 500;
69 | font-size: 20px;
70 | line-height: 14px;
71 | }
72 |
73 | .uppercase {
74 | font-size: 12px;
75 | }
76 |
--------------------------------------------------------------------------------
/src/routes/Forms/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .card {
4 | margin-bottom: 24px;
5 | }
6 |
7 | .heading {
8 | font-size: 14px;
9 | line-height: 22px;
10 | margin: 0 0 16px 0;
11 | }
12 |
13 | .steps:global(.ant-steps) {
14 | max-width: 750px;
15 | margin: 16px auto;
16 | }
17 |
18 | .errorIcon {
19 | cursor: pointer;
20 | color: @error-color;
21 | margin-right: 24px;
22 | i {
23 | margin-right: 4px;
24 | }
25 | }
26 |
27 | .errorPopover {
28 | :global {
29 | .ant-popover-inner-content {
30 | padding: 0;
31 | max-height: 290px;
32 | overflow: auto;
33 | min-width: 256px;
34 | }
35 | }
36 | }
37 |
38 | .errorListItem {
39 | list-style: none;
40 | border-bottom: 1px solid @border-color-split;
41 | padding: 8px 16px;
42 | cursor: pointer;
43 | transition: all 0.3s;
44 | &:hover {
45 | background: @primary-1;
46 | }
47 | &:last-child {
48 | border: 0;
49 | }
50 | .errorIcon {
51 | color: @error-color;
52 | float: left;
53 | margin-top: 4px;
54 | margin-right: 12px;
55 | padding-bottom: 22px;
56 | }
57 | .errorField {
58 | font-size: 12px;
59 | color: @text-color-secondary;
60 | margin-top: 2px;
61 | }
62 | }
63 |
64 | .editable {
65 | td {
66 | padding-top: 13px !important;
67 | padding-bottom: 12.5px !important;
68 | }
69 | }
70 |
71 | // custom footer for fixed footer toolbar
72 | .advancedForm + div {
73 | padding-bottom: 64px;
74 | }
75 |
76 | .advancedForm {
77 | :global {
78 | .ant-form .ant-row:last-child .ant-form-item {
79 | margin-bottom: 24px;
80 | }
81 | .ant-table td {
82 | transition: none !important;
83 | }
84 | }
85 | }
86 |
87 | .optional {
88 | color: @text-color-secondary;
89 | font-style: normal;
90 | }
91 |
--------------------------------------------------------------------------------
/src/routes/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 |
3 | export default class Home extends Component {
4 | constructor(props){
5 | super(props);
6 |
7 | }
8 | componentDidMount() {
9 |
10 | }
11 |
12 | render() {
13 |
14 | return (
15 |
16 |
欢迎使用多标签Ant-Tabs
17 |
18 | );
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/src/routes/Libraries/BraftEditor.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react'
2 | import BraftEditor from 'braft-editor'
3 | import 'braft-editor/dist/index.css'
4 | import { Card } from 'antd';
5 |
6 | export default class BasicDemo extends Component {
7 | constructor(props){
8 | super(props);
9 |
10 | this.state = {
11 | editorState: BraftEditor.createEditorState('Hello World! MY Github 欢迎 Start(https://github.com/kuhami/react-ant-pro) 😉
'), // 设置编辑器初始内容
12 | outputHTML: '
'
13 | }
14 | }
15 |
16 | componentDidMount () {
17 | this.isLivinig = true
18 | // 3秒后更改编辑器内容
19 | // setTimeout(this.setEditorContentAsync, 3000)
20 | }
21 |
22 | componentWillUnmount () {
23 | this.isLivinig = false
24 | }
25 |
26 | handleChange = (editorState) => {
27 | this.setState({
28 | editorState: editorState,
29 | outputHTML: editorState.toHTML()
30 | })
31 | }
32 |
33 | setEditorContentAsync = () => {
34 | this.isLivinig && this.setState({
35 | editorState: BraftEditor.createEditorState('你好,世界!
')
36 | })
37 | }
38 |
39 | render () {
40 |
41 | const { editorState, outputHTML } = this.state
42 | const title = 富文本编译器(braft-editor)
43 | return (
44 |
45 |
46 |
50 |
51 | 输出内容
52 | {outputHTML}
53 |
54 | )
55 |
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/src/routes/Libraries/Drag/Basic.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import {render} from 'react-dom';
3 | import { Card } from 'antd';
4 | import {SortableContainer, SortableElement} from 'react-sortable-hoc';
5 | import arrayMove from 'array-move';
6 |
7 | const SortableItem = SortableElement(({value}) =>
{value} );
8 | const SortableList = SortableContainer(({items}) => {
9 | return (
10 |
11 | {items.map((value, index) => (
12 |
13 | ))}
14 |
15 | );
16 | });
17 | export default class Basic extends Component {
18 | constructor(props){
19 | super(props);
20 | this.state = {
21 | items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
22 | }
23 | }
24 | componentDidMount() {
25 |
26 | }
27 | onSortEnd = ({oldIndex, newIndex}) => {
28 | this.setState(({items}) => ({
29 | items: arrayMove(items, oldIndex, newIndex),
30 | }));
31 | };
32 | render() {
33 | const title = 拖拽(react-sortable-hoc)
34 | return (
35 |
36 | );
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/src/routes/Libraries/Drag/Collections.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import {render} from 'react-dom';
3 | import { Card } from 'antd';
4 | import {sortableContainer, sortableElement} from 'react-sortable-hoc';
5 | import arrayMove from 'array-move';
6 |
7 | const SortableItem = sortableElement(({value}) => {value} );
8 |
9 | const SortableContainer = sortableContainer(({children}) => {
10 | return {children}
;
11 | });
12 | export default class Collections extends Component {
13 | constructor(props){
14 | super(props);
15 | this.state = {
16 | collections: [[0, 1, 2], [0, 1, 2, 3, 4], [0, 1, 2]],
17 | }
18 | }
19 |
20 | onSortEnd = ({oldIndex, newIndex, collection}) => {
21 | this.setState(({collections}) => {
22 | const newCollections = [...collections];
23 |
24 | newCollections[collection] = arrayMove(
25 | collections[collection],
26 | oldIndex,
27 | newIndex,
28 | );
29 |
30 | return {collections: newCollections};
31 | });
32 | };
33 | render() {
34 | const {collections} = this.state;
35 | return (
36 |
37 | {collections.map((items, index) => (
38 |
39 | LIST {index}
40 |
41 | {items.map((item, i) => (
42 |
48 | ))}
49 |
50 |
51 | ))}
52 |
53 | );
54 | }
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/src/routes/Libraries/Drag/DragHandle.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import {render} from 'react-dom';
3 | import { Card } from 'antd';
4 | import {
5 | sortableContainer,
6 | sortableElement,
7 | sortableHandle,} from 'react-sortable-hoc';
8 | import arrayMove from 'array-move';
9 |
10 | const DragHandles = sortableHandle(() => ::++:: );
11 |
12 | const SortableItem = sortableElement(({value}) => (
13 |
14 |
15 | {value}
16 |
17 | ));
18 |
19 | const SortableContainer = sortableContainer(({children}) => {
20 | return ;
21 | });
22 | export default class DragHandle extends Component {
23 | constructor(props){
24 | super(props);
25 | this.state = {
26 | items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
27 | }
28 | }
29 |
30 | onSortEnd = ({oldIndex, newIndex}) => {
31 | this.setState(({items}) => ({
32 | items: arrayMove(items, oldIndex, newIndex),
33 | }));
34 | };
35 | render() {
36 | const {items} = this.state;
37 | return (
38 |
39 | {items.map((value, index) => (
40 |
41 | ))}
42 |
43 | );
44 | }
45 | }
--------------------------------------------------------------------------------
/src/routes/Libraries/Drag/index.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import {render} from 'react-dom';
3 | import { Row,Card,Col } from 'antd';
4 | import Basic from './Basic.js'//DragHandle.js
5 | import Collections from './Collections.js'
6 | import DragHandle from './DragHandle.js'
7 |
8 | export default class Drag extends Component {
9 | constructor(props){
10 | super(props);
11 | this.state = {
12 | items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
13 | }
14 | }
15 | componentDidMount() {
16 |
17 | }
18 |
19 | render() {
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
);
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/src/routes/List/Applications.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @import '../../utils/utils.less';
3 |
4 | .filterCardList {
5 | margin-bottom: -24px;
6 | :global {
7 | .ant-card-meta-content {
8 | margin-top: 0;
9 | }
10 | // disabled white space
11 | .ant-card-meta-avatar {
12 | font-size: 0;
13 | }
14 | .ant-card-actions {
15 | background: #f7f9fa;
16 | }
17 | .ant-list .ant-list-item-content-single {
18 | max-width: 100%;
19 | }
20 | }
21 | .cardInfo {
22 | .clearfix();
23 | margin-top: 16px;
24 | margin-left: 40px;
25 | & > div {
26 | position: relative;
27 | text-align: left;
28 | float: left;
29 | width: 50%;
30 | p {
31 | line-height: 32px;
32 | font-size: 24px;
33 | margin: 0;
34 | }
35 | p:first-child {
36 | color: @text-color-secondary;
37 | font-size: 12px;
38 | line-height: 20px;
39 | margin-bottom: 4px;
40 | }
41 | }
42 | }
43 | }
44 |
45 | .wan {
46 | position: relative;
47 | top: -2px;
48 | font-size: @font-size-base;
49 | font-style: normal;
50 | line-height: 20px;
51 | margin-left: 2px;
52 | }
53 |
--------------------------------------------------------------------------------
/src/routes/List/Articles.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @import '../../utils/utils.less';
3 |
4 | .listContent {
5 | .description {
6 | line-height: 22px;
7 | max-width: 720px;
8 | }
9 | .extra {
10 | color: @text-color-secondary;
11 | margin-top: 16px;
12 | line-height: 22px;
13 | & > :global(.ant-avatar) {
14 | vertical-align: top;
15 | margin-right: 8px;
16 | width: 20px;
17 | height: 20px;
18 | position: relative;
19 | top: 1px;
20 | }
21 | & > em {
22 | color: @disabled-color;
23 | font-style: normal;
24 | margin-left: 16px;
25 | }
26 | }
27 | }
28 | a.listItemMetaTitle {
29 | color: @heading-color;
30 | }
31 | .listItemExtra {
32 | width: 272px;
33 | height: 1px;
34 | }
35 | .selfTrigger {
36 | margin-left: 12px;
37 | }
38 |
39 | @media screen and (max-width: @screen-xs) {
40 | .selfTrigger {
41 | display: block;
42 | margin-left: 0;
43 | }
44 | .listContent {
45 | .extra {
46 | & > em {
47 | display: block;
48 | margin-left: 0;
49 | margin-top: 8px;
50 | }
51 | }
52 | }
53 | }
54 | @media screen and (max-width: @screen-md) {
55 | .selfTrigger {
56 | display: block;
57 | margin-left: 0;
58 | }
59 | }
60 | @media screen and (max-width: @screen-lg) {
61 | .listItemExtra {
62 | width: 0;
63 | height: 1px;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/routes/List/List.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { routerRedux, Route, Switch } from 'dva/router';
3 | import { connect } from 'dva';
4 | import { Input } from 'antd';
5 | import PageHeaderLayout from '../../layouts/PageHeaderLayout';
6 | import { getRoutes } from '../../utils/utils';
7 |
8 | @connect()
9 | export default class SearchList extends Component {
10 | handleTabChange = key => {
11 | const { dispatch, match } = this.props;
12 | switch (key) {
13 | case 'articles':
14 | dispatch(routerRedux.push(`${match.url}/articles`));
15 | break;
16 | case 'applications':
17 | dispatch(routerRedux.push(`${match.url}/applications`));
18 | break;
19 | case 'projects':
20 | dispatch(routerRedux.push(`${match.url}/projects`));
21 | break;
22 | default:
23 | break;
24 | }
25 | };
26 |
27 | render() {
28 | const tabList = [
29 | {
30 | key: 'articles',
31 | tab: '文章',
32 | },
33 | {
34 | key: 'applications',
35 | tab: '应用',
36 | },
37 | {
38 | key: 'projects',
39 | tab: '项目',
40 | },
41 | ];
42 |
43 | const mainSearch = (
44 |
45 |
52 |
53 | );
54 |
55 | const { match, routerData, location } = this.props;
56 | const routes = getRoutes(match.path, routerData);
57 |
58 | return (
59 |
66 |
67 | {routes.map(item => (
68 |
69 | ))}
70 |
71 |
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/routes/List/Projects.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @import '../../utils/utils.less';
3 |
4 | .coverCardList {
5 | margin-bottom: -24px;
6 |
7 | .card {
8 | :global {
9 | .ant-card-meta-title {
10 | margin-bottom: 4px;
11 | & > a {
12 | color: @heading-color;
13 | display: inline-block;
14 | max-width: 100%;
15 | }
16 | }
17 | .ant-card-meta-description {
18 | height: 44px;
19 | line-height: 22px;
20 | overflow: hidden;
21 | }
22 | }
23 |
24 | &:hover {
25 | :global {
26 | .ant-card-meta-title > a {
27 | color: @primary-color;
28 | }
29 | }
30 | }
31 | }
32 |
33 | .cardItemContent {
34 | display: flex;
35 | margin-top: 16px;
36 | margin-bottom: -4px;
37 | line-height: 20px;
38 | height: 20px;
39 | & > span {
40 | color: @text-color-secondary;
41 | flex: 1;
42 | font-size: 12px;
43 | }
44 | .avatarList {
45 | flex: 0 1 auto;
46 | }
47 | }
48 | .cardList {
49 | margin-top: 24px;
50 | }
51 |
52 | :global {
53 | .ant-list .ant-list-item-content-single {
54 | max-width: 100%;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/routes/List/TableList.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @import '../../utils/utils.less';
3 |
4 | .tableList {
5 | .tableListOperator {
6 | margin-bottom: 16px;
7 | button {
8 | margin-right: 8px;
9 | }
10 | }
11 | }
12 |
13 | .tableListForm {
14 | :global {
15 | .ant-form-item {
16 | margin-bottom: 24px;
17 | margin-right: 0;
18 | display: flex;
19 | > .ant-form-item-label {
20 | width: auto;
21 | line-height: 32px;
22 | padding-right: 8px;
23 | }
24 | .ant-form-item-control {
25 | line-height: 32px;
26 | }
27 | }
28 | .ant-form-item-control-wrapper {
29 | flex: 1;
30 | }
31 | }
32 | .submitButtons {
33 | white-space: nowrap;
34 | margin-bottom: 24px;
35 | }
36 | }
37 |
38 | @media screen and (max-width: @screen-lg) {
39 | .tableListForm :global(.ant-form-item) {
40 | margin-right: 24px;
41 | }
42 | }
43 |
44 | @media screen and (max-width: @screen-md) {
45 | .tableListForm :global(.ant-form-item) {
46 | margin-right: 8px;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/routes/Profile/AdvancedProfile.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .headerList {
4 | margin-bottom: 4px;
5 | }
6 |
7 | .tabsCard {
8 | :global {
9 | .ant-card-head {
10 | padding: 0 16px;
11 | }
12 | }
13 | }
14 |
15 | .noData {
16 | color: @disabled-color;
17 | text-align: center;
18 | line-height: 64px;
19 | font-size: 16px;
20 | i {
21 | font-size: 24px;
22 | margin-right: 16px;
23 | position: relative;
24 | top: 3px;
25 | }
26 | }
27 |
28 | .heading {
29 | color: @heading-color;
30 | font-size: 20px;
31 | }
32 |
33 | .stepDescription {
34 | font-size: 14px;
35 | position: relative;
36 | left: 38px;
37 | & > div {
38 | margin-top: 8px;
39 | margin-bottom: 4px;
40 | }
41 | }
42 |
43 | .textSecondary {
44 | color: @text-color-secondary;
45 | }
46 |
47 | @media screen and (max-width: @screen-sm) {
48 | .stepDescription {
49 | left: 8px;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/routes/Profile/BasicProfile.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .title {
4 | color: @heading-color;
5 | font-size: 16px;
6 | font-weight: 500;
7 | margin-bottom: 16px;
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/Result/Error.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { Button, Icon, Card } from 'antd';
3 | import Result from 'components/Result';
4 | import PageHeaderLayout from '../../layouts/PageHeaderLayout';
5 |
6 | const extra = (
7 |
8 |
16 | 您提交的内容有如下错误:
17 |
18 |
24 |
30 |
31 | );
32 |
33 | const actions = 返回修改 ;
34 |
35 | export default () => (
36 |
37 |
38 |
46 |
47 |
48 | );
49 |
--------------------------------------------------------------------------------
/src/routes/Result/Success.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import Success from './Success';
4 |
5 | it('renders with Result', () => {
6 | const wrapper = shallow( );
7 | expect(wrapper.find('Result').length).toBe(1);
8 | expect(wrapper.find('Result').prop('type')).toBe('success');
9 | });
10 |
--------------------------------------------------------------------------------
/src/routes/User/Login.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .main {
4 | width: 368px;
5 | margin: 0 auto;
6 |
7 | .icon {
8 | font-size: 24px;
9 | color: rgba(0, 0, 0, 0.2);
10 | margin-left: 16px;
11 | vertical-align: middle;
12 | cursor: pointer;
13 | transition: color 0.3s;
14 |
15 | &:hover {
16 | color: @primary-color;
17 | }
18 | }
19 |
20 | .other {
21 | text-align: left;
22 | margin-top: 24px;
23 | line-height: 22px;
24 |
25 | .register {
26 | float: right;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/routes/User/Register.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .main {
4 | width: 368px;
5 | margin: 0 auto;
6 |
7 | :global {
8 | .ant-form-item {
9 | margin-bottom: 24px;
10 | }
11 | }
12 |
13 | h3 {
14 | font-size: 16px;
15 | margin-bottom: 20px;
16 | }
17 |
18 | .getCaptcha {
19 | display: block;
20 | width: 100%;
21 | }
22 |
23 | .submit {
24 | width: 50%;
25 | }
26 |
27 | .login {
28 | float: right;
29 | line-height: @btn-height-lg;
30 | }
31 | }
32 |
33 | .success,
34 | .warning,
35 | .error {
36 | transition: color 0.3s;
37 | }
38 |
39 | .success {
40 | color: @success-color;
41 | }
42 |
43 | .warning {
44 | color: @warning-color;
45 | }
46 |
47 | .error {
48 | color: @error-color;
49 | }
50 |
51 | .progress-pass > .progress {
52 | :global {
53 | .ant-progress-bg {
54 | background-color: @warning-color;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/routes/User/RegisterResult.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button } from 'antd';
3 | import { Link } from 'dva/router';
4 | import Result from 'components/Result';
5 | import styles from './RegisterResult.less';
6 | import ImageWrapper from 'components/ImageWrapper'; // aware of the relative path
7 |
8 | const actions = (
9 |
19 | );
20 |
21 | export default ({ location }) => (
22 |
23 |
28 | 你的账户:{location.state ? location.state.account : 'AntDesign@example.com'} 注册成功
29 |
30 | }
31 | description="激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。"
32 | actions={actions}
33 | style={{ marginTop: 56 }}
34 | />
35 |
40 |
41 | );
42 |
--------------------------------------------------------------------------------
/src/routes/User/RegisterResult.less:
--------------------------------------------------------------------------------
1 | .registerResult {
2 | :global {
3 | .anticon {
4 | font-size: 64px;
5 | }
6 | }
7 | .title {
8 | margin-top: 32px;
9 | font-size: 20px;
10 | line-height: 28px;
11 | }
12 | .actions {
13 | margin-top: 40px;
14 | a + a {
15 | margin-left: 8px;
16 | }
17 | }
18 | }
19 |
20 | .imgIndex{
21 | .desc{
22 | color: red;
23 | }
24 | }
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/services/api.js:
--------------------------------------------------------------------------------
1 | import { stringify } from 'qs';
2 | import request from '../utils/request';
3 |
4 | export async function queryProjectNotice() {
5 | return request('/api/project/notice');
6 | }
7 |
8 | export async function queryActivities() {
9 | return request('/api/activities');
10 | }
11 |
12 | export async function queryRule(params) {
13 | return request(`/api/rule?${stringify(params)}`);
14 | }
15 |
16 | export async function removeRule(params) {
17 | return request('/api/rule', {
18 | method: 'POST',
19 | body: {
20 | ...params,
21 | method: 'delete',
22 | },
23 | });
24 | }
25 |
26 | export async function addRule(params) {
27 | return request('/api/rule', {
28 | method: 'POST',
29 | body: {
30 | ...params,
31 | method: 'post',
32 | },
33 | });
34 | }
35 |
36 | export async function fakeSubmitForm(params) {
37 | return request('/api/forms', {
38 | method: 'POST',
39 | body: params,
40 | });
41 | }
42 |
43 | export async function fakeChartData() {
44 | return request('/api/fake_chart_data');
45 | }
46 |
47 | export async function queryTags() {
48 | return request('/api/tags');
49 | }
50 |
51 | export async function queryBasicProfile() {
52 | return request('/api/profile/basic');
53 | }
54 |
55 | export async function queryAdvancedProfile() {
56 | return request('/api/profile/advanced');
57 | }
58 |
59 | export async function queryFakeList(params) {
60 | return request(`/api/fake_list?${stringify(params)}`);
61 | }
62 |
63 | export async function fakeAccountLogin(params) {
64 | return request('/api/login/account', {
65 | method: 'POST',
66 | body: params,
67 | });
68 | }
69 |
70 | export async function fakeRegister(params) {
71 | return request('/api/register', {
72 | method: 'POST',
73 | body: params,
74 | });
75 | }
76 |
77 | export async function queryNotices() {
78 | return request('/api/notices');
79 | }
80 |
--------------------------------------------------------------------------------
/src/services/error.js:
--------------------------------------------------------------------------------
1 | import request from '../utils/request';
2 |
3 | export async function query(code) {
4 | return request(`/api/${code}`);
5 | }
6 |
--------------------------------------------------------------------------------
/src/services/user.js:
--------------------------------------------------------------------------------
1 | import request from '../utils/request';
2 |
3 | export async function query() {
4 | return request('/api/users');
5 | }
6 |
7 | export async function queryCurrent() {
8 | return request('/api/currentUser');
9 | }
10 |
--------------------------------------------------------------------------------
/src/theme.js:
--------------------------------------------------------------------------------
1 | // https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
2 | module.exports = {
3 | // 'primary-color': '#10e99b',
4 | 'card-actions-background': '#f5f8fa',
5 | };
6 |
--------------------------------------------------------------------------------
/src/utils/Authorized.js:
--------------------------------------------------------------------------------
1 | import RenderAuthorized from '../components/Authorized';
2 | import { getAuthority } from './authority';
3 |
4 | let Authorized = RenderAuthorized(getAuthority()); // eslint-disable-line
5 |
6 | // Reload the rights component
7 | const reloadAuthorized = () => {
8 | Authorized = RenderAuthorized(getAuthority());
9 | };
10 |
11 | export { reloadAuthorized };
12 | export default Authorized;
13 |
--------------------------------------------------------------------------------
/src/utils/authority.js:
--------------------------------------------------------------------------------
1 | // use localStorage to store the authority info, which might be sent from server in actual project.
2 | export function getAuthority() {
3 | return localStorage.getItem('antd-pro-authority') || 'admin';
4 | }
5 |
6 | export function setAuthority(authority) {
7 | return localStorage.setItem('antd-pro-authority', authority);
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | text-overflow: ellipsis;
4 | word-break: break-all;
5 | white-space: nowrap;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | overflow: hidden;
10 | position: relative;
11 | line-height: 1.5em;
12 | max-height: @line * 1.5em;
13 | text-align: justify;
14 | margin-right: -1em;
15 | padding-right: 1em;
16 | &:before {
17 | background: @bg;
18 | content: '...';
19 | padding: 0 1px;
20 | position: absolute;
21 | right: 14px;
22 | bottom: 0;
23 | }
24 | &:after {
25 | background: white;
26 | content: '';
27 | margin-top: 0.2em;
28 | position: absolute;
29 | right: 14px;
30 | width: 1em;
31 | height: 1em;
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &:before,
40 | &:after {
41 | content: ' ';
42 | display: table;
43 | }
44 | &:after {
45 | clear: both;
46 | visibility: hidden;
47 | font-size: 0;
48 | height: 0;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/run-tests.js:
--------------------------------------------------------------------------------
1 | const { spawn } = require('child_process');
2 | const { kill } = require('cross-port-killer');
3 |
4 | const env = Object.create(process.env);
5 | env.BROWSER = 'none';
6 | const startServer = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['start'], {
7 | env,
8 | });
9 |
10 | startServer.stderr.on('data', data => {
11 | // eslint-disable-next-line
12 | console.log(data);
13 | });
14 |
15 | startServer.on('exit', () => {
16 | kill(process.env.PORT || 8000);
17 | });
18 |
19 | // eslint-disable-next-line
20 | console.log('Starting development server for home.e2e.js tests...');
21 | startServer.stdout.on('data', data => {
22 | // eslint-disable-next-line
23 | console.log(data.toString());
24 | if (
25 | data.toString().indexOf('Compiled successfully') >= 0 ||
26 | data.toString().indexOf('Compiled with warnings') >= 0
27 | ) {
28 | // eslint-disable-next-line
29 | console.log('Development server is started, ready to run tests.');
30 | const testCmd = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['test'], {
31 | stdio: 'inherit',
32 | });
33 | testCmd.on('exit', () => {
34 | startServer.kill();
35 | });
36 | }
37 | });
38 |
--------------------------------------------------------------------------------