now.y + pre, 0))
45 | }}
46 | />
47 | )}
48 | data={salesPieData}
49 | valueFormat={val => }
50 | height={294}
51 | />,
52 | mountNode,
53 | );
54 | ```
55 |
--------------------------------------------------------------------------------
/src/pages/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 | padding-top: 8px;
38 | text-align: left;
39 |
40 | > div {
41 | margin-top: 8px;
42 | margin-bottom: 4px;
43 | }
44 | }
45 |
46 | .textSecondary {
47 | color: @text-color-secondary;
48 | }
49 |
50 | @media screen and (max-width: @screen-sm) {
51 | .stepDescription {
52 | left: 8px;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/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/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | import analysis from './zh-CN/analysis';
2 | import exception from './zh-CN/exception';
3 | import form from './zh-CN/form';
4 | import globalHeader from './zh-CN/globalHeader';
5 | import login from './zh-CN/login';
6 | import menu from './zh-CN/menu';
7 | import monitor from './zh-CN/monitor';
8 | import result from './zh-CN/result';
9 | import settingDrawer from './zh-CN/settingDrawer';
10 | import settings from './zh-CN/settings';
11 | import pwa from './zh-CN/pwa';
12 |
13 | export default {
14 | 'navBar.lang': '语言',
15 | 'layout.user.link.help': '帮助',
16 | 'layout.user.link.privacy': '隐私',
17 | 'layout.user.link.terms': '条款',
18 | 'app.home.introduce': '介绍',
19 | 'app.forms.basic.title': '基础表单',
20 | 'app.forms.basic.description':
21 | '表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。',
22 | ...analysis,
23 | ...exception,
24 | ...form,
25 | ...globalHeader,
26 | ...login,
27 | ...menu,
28 | ...monitor,
29 | ...result,
30 | ...settingDrawer,
31 | ...settings,
32 | ...pwa,
33 | };
34 |
--------------------------------------------------------------------------------
/src/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | import analysis from './zh-TW/analysis';
2 | import exception from './zh-TW/exception';
3 | import form from './zh-TW/form';
4 | import globalHeader from './zh-TW/globalHeader';
5 | import login from './zh-TW/login';
6 | import menu from './zh-TW/menu';
7 | import monitor from './zh-TW/monitor';
8 | import result from './zh-TW/result';
9 | import settingDrawer from './zh-TW/settingDrawer';
10 | import settings from './zh-TW/settings';
11 | import pwa from './zh-TW/pwa';
12 |
13 | export default {
14 | 'navBar.lang': '語言',
15 | 'layout.user.link.help': '幫助',
16 | 'layout.user.link.privacy': '隱私',
17 | 'layout.user.link.terms': '條款',
18 | 'app.home.introduce': '介紹',
19 | 'app.forms.basic.title': '基礎表單',
20 | 'app.forms.basic.description':
21 | '表單頁用於向用戶收集或驗證信息,基礎表單常見於數據項較少的表單場景。',
22 | ...analysis,
23 | ...exception,
24 | ...form,
25 | ...globalHeader,
26 | ...login,
27 | ...menu,
28 | ...monitor,
29 | ...result,
30 | ...settingDrawer,
31 | ...settings,
32 | ...pwa,
33 | };
34 |
--------------------------------------------------------------------------------
/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 | .ant-avatar-string {
45 | font-size: 12px;
46 | line-height: 18px;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/e2e/baseLayout.e2e.js:
--------------------------------------------------------------------------------
1 | import RouterConfig from '../../config/router.config';
2 |
3 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
4 |
5 | function formatter(data) {
6 | return data
7 | .reduce((pre, item) => {
8 | pre.push(item.path);
9 | return pre;
10 | }, [])
11 | .filter(item => item);
12 | }
13 |
14 | describe('Homepage', async () => {
15 | const testPage = path => async () => {
16 | await page.goto(`${BASE_URL}${path}`);
17 | await page.waitForSelector('footer', {
18 | timeout: 2000,
19 | });
20 | const haveFooter = await page.evaluate(
21 | () => document.getElementsByTagName('footer').length > 0
22 | );
23 | expect(haveFooter).toBeTruthy();
24 | };
25 |
26 | beforeAll(async () => {
27 | jest.setTimeout(1000000);
28 | await page.setCacheEnabled(false);
29 | });
30 | const routers = formatter(RouterConfig[1].routes);
31 | routers.forEach(route => {
32 | fit(`test pages ${route}`, testPage(route));
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/components/Login/LoginTab.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Tabs } from 'antd';
3 | import LoginContext from './loginContext';
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 | class LoginTab extends Component {
16 | constructor(props) {
17 | super(props);
18 | this.uniqueId = generateId('login-tab-');
19 | }
20 |
21 | componentDidMount() {
22 | const { tabUtil } = this.props;
23 | tabUtil.addTab(this.uniqueId);
24 | }
25 |
26 | render() {
27 | const { children } = this.props;
28 | return {children};
29 | }
30 | }
31 |
32 | const wrapContext = props => (
33 |
34 | {value => }
35 |
36 | );
37 |
38 | // 标志位 用来判断是不是自定义组件
39 | wrapContext.typeName = 'LoginTab';
40 |
41 | export default wrapContext;
42 |
--------------------------------------------------------------------------------
/src/components/Login/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .login {
4 | :global {
5 | .ant-tabs .ant-tabs-bar {
6 | border-bottom: 0;
7 | margin-bottom: 24px;
8 | text-align: center;
9 | }
10 |
11 | .ant-form-item {
12 | margin: 0 2px 24px;
13 | }
14 | }
15 |
16 | .getCaptcha {
17 | display: block;
18 | width: 100%;
19 | }
20 |
21 | .icon {
22 | font-size: 24px;
23 | color: rgba(0, 0, 0, 0.2);
24 | margin-left: 16px;
25 | vertical-align: middle;
26 | cursor: pointer;
27 | transition: color 0.3s;
28 |
29 | &:hover {
30 | color: @primary-color;
31 | }
32 | }
33 |
34 | .other {
35 | text-align: left;
36 | margin-top: 24px;
37 | line-height: 22px;
38 |
39 | .register {
40 | float: right;
41 | }
42 | }
43 |
44 | .prefixIcon {
45 | font-size: @font-size-base;
46 | color: @disabled-color;
47 | }
48 |
49 | .submit {
50 | width: 100%;
51 | margin-top: 24px;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/ActiveChart/index.less:
--------------------------------------------------------------------------------
1 | .activeChart {
2 | position: relative;
3 | }
4 | .activeChartGrid {
5 | p {
6 | position: absolute;
7 | top: 80px;
8 | }
9 | p:last-child {
10 | top: 115px;
11 | }
12 | }
13 | .activeChartLegend {
14 | position: relative;
15 | font-size: 0;
16 | margin-top: 8px;
17 | height: 20px;
18 | line-height: 20px;
19 | span {
20 | display: inline-block;
21 | font-size: 12px;
22 | text-align: center;
23 | width: 33.33%;
24 | }
25 | span:first-child {
26 | text-align: left;
27 | }
28 | span:last-child {
29 | text-align: right;
30 | }
31 | }
32 | .dashedLine {
33 | position: relative;
34 | height: 1px;
35 | top: -70px;
36 | left: -3px;
37 |
38 | .line {
39 | position: absolute;
40 | top: 0;
41 | left: 0;
42 | width: 100%;
43 | height: 100%;
44 | background-image: linear-gradient(to right, transparent 50%, #e9e9e9 50%);
45 | background-size: 6px;
46 | }
47 | }
48 |
49 | .dashedLine:last-child {
50 | top: -36px;
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/Authorized/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import AuthorizedRoute, { authority } from './AuthorizedRoute';
3 | export type IReactComponent =
4 | | React.StatelessComponent
5 | | React.ComponentClass
6 | | React.ClassicComponentClass
;
7 |
8 | type Secured = (
9 | authority: authority,
10 | error?: React.ReactNode
11 | ) => (target: T) => T;
12 |
13 | type check = (
14 | authority: authority,
15 | target: T,
16 | Exception: S
17 | ) => T | S;
18 |
19 | export interface IAuthorizedProps {
20 | authority: authority;
21 | noMatch?: React.ReactNode;
22 | }
23 |
24 | export class Authorized extends React.Component {
25 | public static Secured: Secured;
26 | public static AuthorizedRoute: typeof AuthorizedRoute;
27 | public static check: check;
28 | }
29 |
30 | declare function renderAuthorize(currentAuthority: string): typeof Authorized;
31 |
32 | export default renderAuthorize;
33 |
--------------------------------------------------------------------------------
/src/pages/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 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/monitor.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.monitor.trading-activity': 'Atividade de Trading Real-time',
3 | 'app.monitor.total-transactions': 'Total de transações hoje',
4 | 'app.monitor.sales-target': 'Taxa de conclusão da meta de vendas',
5 | 'app.monitor.remaining-time': 'Tempo restante da atividade',
6 | 'app.monitor.total-transactions-per-second': 'Total de transações por segundo',
7 | 'app.monitor.activity-forecast': 'Previsão atual',
8 | 'app.monitor.efficiency': 'Eficiência',
9 | 'app.monitor.ratio': 'Relação',
10 | 'app.monitor.proportion-per-category': 'Proporção por categoria',
11 | 'app.monitor.fast-food': 'Fast food',
12 | 'app.monitor.western-food': 'Comida Ocidental',
13 | 'app.monitor.hot-pot': 'Hot pot',
14 | 'app.monitor.waiting-for-implementation': 'Aguardando implementação',
15 | 'app.monitor.popular-searches': 'Buscas populares',
16 | 'app.monitor.resource-surplus': 'Excedente de recursos',
17 | 'app.monitor.fund-surplus': 'Excedente do fundo',
18 | 'app.exception.back': 'Voltar a home',
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/Exception/index.en-US.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Exception
3 | cols: 1
4 | order: 5
5 | ---
6 |
7 | Exceptions page is used to provide feedback on specific abnormal state. Usually, it contains an explanation of the error status, and provides users with suggestions or operations, to prevent users from feeling lost and confused.
8 |
9 | ## API
10 |
11 | Property | Description | Type | Default
12 | ---------|-------------|------|--------
13 | | backText | default return button text | ReactNode | back to home |
14 | type | type of exception, the corresponding default `title`, `desc`, `img` will be given if set, which can be overridden by explicit setting of `title`, `desc`, `img` | Enum {'403', '404', '500'} | -
15 | title | title | ReactNode | -
16 | desc | supplementary description | ReactNode | -
17 | img | the url of background image | string | -
18 | actions | suggested operations, a default 'Home' link will show if not set | ReactNode | -
19 | linkElement | to specify the element of link | string\|ReactElement | 'a'
20 | redirect | redirect path | string | '/'
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/Widget/MediaView.tsx:
--------------------------------------------------------------------------------
1 | import React,{ Component } from "react";
2 | const styles = require('./styles/MediaView.less')
3 | interface IProps {
4 | /**
5 | * 布局宽
6 | */
7 | width: number;
8 | /**
9 | * 布局高
10 | */
11 | height: number;
12 | /**
13 | * 是否显示边框
14 | */
15 | border?: boolean;
16 |
17 | /**
18 | * 媒体对象
19 | */
20 | item: Models.IMedia;
21 | /**
22 | * 类
23 | */
24 | className?: string;
25 | }
26 |
27 | interface IState {
28 |
29 | }
30 | class MediaView extends Component{
31 | render() {
32 | const { width, height, border, item, className} = this.props;
33 | return ;
37 | }
38 | }
39 | export default MediaView;
--------------------------------------------------------------------------------
/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 | const Charts = {
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 |
34 | export {
35 | Charts as default,
36 | yuan,
37 | Bar,
38 | Pie,
39 | Gauge,
40 | Radar,
41 | MiniBar,
42 | MiniArea,
43 | MiniProgress,
44 | ChartCard,
45 | Field,
46 | WaterWave,
47 | TagCloud,
48 | TimelineChart,
49 | };
50 |
--------------------------------------------------------------------------------
/src/layouts/Footer.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { Layout, Icon } from 'antd';
3 | import GlobalFooter from '@/components/GlobalFooter';
4 |
5 | const { Footer } = Layout;
6 | const FooterView = () => (
7 |
36 | );
37 | export default FooterView;
38 |
--------------------------------------------------------------------------------
/src/components/SiderMenu/SiderMenuUtils.js:
--------------------------------------------------------------------------------
1 | import pathToRegexp from 'path-to-regexp';
2 | import { urlToList } from '../_utils/pathTools';
3 |
4 | /**
5 | * Recursively flatten the data
6 | * [{path:string},{path:string}] => {path,path2}
7 | * @param menus
8 | */
9 | export const getFlatMenuKeys = menuData => {
10 | let keys = [];
11 | menuData.forEach(item => {
12 | keys.push(item.path);
13 | if (item.children) {
14 | keys = keys.concat(getFlatMenuKeys(item.children));
15 | }
16 | });
17 | return keys;
18 | };
19 |
20 | export const getMenuMatches = (flatMenuKeys, path) =>
21 | flatMenuKeys.filter(item => {
22 | if (item) {
23 | return pathToRegexp(item).test(path);
24 | }
25 | return false;
26 | });
27 | /**
28 | * 获得菜单子节点
29 | * @memberof SiderMenu
30 | */
31 | export const getDefaultCollapsedSubMenus = props => {
32 | const {
33 | location: { pathname },
34 | flatMenuKeys,
35 | } = props;
36 | return urlToList(pathname)
37 | .map(item => getMenuMatches(flatMenuKeys, item)[0])
38 | .filter(item => item);
39 | };
40 |
--------------------------------------------------------------------------------
/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/Ellipsis/demo/line.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title:
4 | zh-CN: 按照行数省略
5 | en-US: Truncate according to the number of rows
6 | ---
7 |
8 | ## zh-CN
9 |
10 | 通过设置 `lines` 属性指定最大行数,如果超过这个行数的文本会自动截取。但是在这种模式下所有 `children` 将会被转换成纯文本。
11 |
12 | 并且注意在这种模式下,外容器需要有指定的宽度(或设置自身宽度)。
13 |
14 | ## en-US
15 |
16 | `lines` attribute specifies the maximum number of rows where the text will automatically be truncated when exceeded. In this mode, all children will be converted to plain text.
17 |
18 | Also note that, in this mode, the outer container needs to have a specified width (or set its own width).
19 |
20 |
21 | ````jsx
22 | import Ellipsis from 'ant-design-pro/lib/Ellipsis';
23 |
24 | 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.
;
25 |
26 | ReactDOM.render(
27 |
28 | {article}
29 |
30 | , mountNode);
31 | ````
32 |
--------------------------------------------------------------------------------
/src/components/AvatarList/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import range from 'lodash/range';
3 | import { mount } from 'enzyme';
4 | import AvatarList from './index';
5 |
6 | const renderItems = numItems =>
7 | range(numItems).map(i => (
8 |
13 | ));
14 |
15 | describe('AvatarList', () => {
16 | it('renders all items', () => {
17 | const wrapper = mount({renderItems(4)});
18 | expect(wrapper.find('AvatarList').length).toBe(1);
19 | expect(wrapper.find('Item').length).toBe(4);
20 | expect(wrapper.findWhere(node => node.key() === 'exceed').length).toBe(0);
21 | });
22 |
23 | it('renders max of 3 items', () => {
24 | const wrapper = mount({renderItems(4)});
25 | expect(wrapper.find('AvatarList').length).toBe(1);
26 | expect(wrapper.find('Item').length).toBe(3);
27 | expect(wrapper.findWhere(node => node.key() === 'exceed').length).toBe(1);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/pages/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 | display: block;
34 | white-space: nowrap;
35 | margin-bottom: 24px;
36 | }
37 | }
38 |
39 | @media screen and (max-width: @screen-lg) {
40 | .tableListForm :global(.ant-form-item) {
41 | margin-right: 24px;
42 | }
43 | }
44 |
45 | @media screen and (max-width: @screen-md) {
46 | .tableListForm :global(.ant-form-item) {
47 | margin-right: 8px;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Alipay.inc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/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/DescriptionList/demo/basic.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 | Basic DescriptionList.
15 |
16 | ````jsx
17 | import DescriptionList from 'ant-design-pro/lib/DescriptionList';
18 |
19 | const { Description } = DescriptionList;
20 |
21 | ReactDOM.render(
22 |
23 |
24 | A free, open source, cross-platform,
25 | graphical web browser developed by the
26 | Mozilla Corporation and hundreds of
27 | volunteers.
28 |
29 |
30 | A free, open source, cross-platform,
31 | graphical web browser developed by the
32 | Mozilla Corporation and hundreds of
33 | volunteers.
34 |
35 |
36 | A free, open source, cross-platform,
37 | graphical web browser developed by the
38 | Mozilla Corporation and hundreds of
39 | volunteers.
40 |
41 |
42 | , mountNode);
43 | ````
44 |
--------------------------------------------------------------------------------
/src/locales/en-US.js:
--------------------------------------------------------------------------------
1 | import analysis from './en-US/analysis';
2 | import exception from './en-US/exception';
3 | import form from './en-US/form';
4 | import globalHeader from './en-US/globalHeader';
5 | import login from './en-US/login';
6 | import menu from './en-US/menu';
7 | import monitor from './en-US/monitor';
8 | import result from './en-US/result';
9 | import settingDrawer from './en-US/settingDrawer';
10 | import settings from './en-US/settings';
11 | import pwa from './en-US/pwa';
12 |
13 | export default {
14 | 'navBar.lang': 'Languages',
15 | 'layout.user.link.help': 'Help',
16 | 'layout.user.link.privacy': 'Privacy',
17 | 'layout.user.link.terms': 'Terms',
18 | 'app.home.introduce': 'introduce',
19 | 'app.forms.basic.title': 'Basic form',
20 | 'app.forms.basic.description':
21 | 'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
22 | ...analysis,
23 | ...exception,
24 | ...form,
25 | ...globalHeader,
26 | ...login,
27 | ...menu,
28 | ...monitor,
29 | ...result,
30 | ...settingDrawer,
31 | ...settings,
32 | ...pwa,
33 | };
34 |
--------------------------------------------------------------------------------
/src/pages/Account/Settings/BaseView.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .baseView {
4 | display: flex;
5 | padding-top: 12px;
6 |
7 | .left {
8 | max-width: 448px;
9 | min-width: 224px;
10 | }
11 | .right {
12 | flex: 1;
13 | padding-left: 104px;
14 | .avatar_title {
15 | height: 22px;
16 | font-size: @font-size-base;
17 | color: @heading-color;
18 | line-height: 22px;
19 | margin-bottom: 8px;
20 | }
21 | .avatar {
22 | width: 144px;
23 | height: 144px;
24 | margin-bottom: 12px;
25 | overflow: hidden;
26 | img {
27 | width: 100%;
28 | }
29 | }
30 | .button_view {
31 | width: 144px;
32 | text-align: center;
33 | }
34 | }
35 | }
36 |
37 | @media screen and (max-width: @screen-xl) {
38 | .baseView {
39 | flex-direction: column-reverse;
40 |
41 | .right {
42 | padding: 20px;
43 | display: flex;
44 | flex-direction: column;
45 | align-items: center;
46 | max-width: 448px;
47 | .avatar_title {
48 | display: none;
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/demo/vertical.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 1
3 | title:
4 | zh-CN: 垂直型
5 | en-US: Vertical
6 | ---
7 |
8 | ## zh-CN
9 |
10 | 垂直布局。
11 |
12 | ## en-US
13 |
14 | Vertical layout.
15 |
16 | ````jsx
17 | import DescriptionList from 'ant-design-pro/lib/DescriptionList';
18 |
19 | const { Description } = DescriptionList;
20 |
21 | ReactDOM.render(
22 |
23 |
24 | A free, open source, cross-platform,
25 | graphical web browser developed by the
26 | Mozilla Corporation and hundreds of
27 | volunteers.
28 |
29 |
30 | A free, open source, cross-platform,
31 | graphical web browser developed by the
32 | Mozilla Corporation and hundreds of
33 | volunteers.
34 |
35 |
36 | A free, open source, cross-platform,
37 | graphical web browser developed by the
38 | Mozilla Corporation and hundreds of
39 | volunteers.
40 |
41 |
42 | , mountNode);
43 | ````
44 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: 'babel-eslint',
3 | extends: ['airbnb', 'prettier', 'plugin:compat/recommended'],
4 | env: {
5 | browser: true,
6 | node: true,
7 | es6: true,
8 | mocha: true,
9 | jest: true,
10 | jasmine: true,
11 | },
12 | globals: {
13 | APP_TYPE: true,
14 | page: true,
15 | },
16 | rules: {
17 | 'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
18 | 'react/jsx-wrap-multilines': 0,
19 | 'react/prop-types': 0,
20 | 'react/forbid-prop-types': 0,
21 | 'react/jsx-one-expression-per-line': 0,
22 | 'import/no-unresolved': [2, { ignore: ['^@/', '^umi/'] }],
23 | 'import/no-extraneous-dependencies': [
24 | 2,
25 | {
26 | optionalDependencies: true,
27 | devDependencies: ['**/tests/**.js', '/mock/**.js', '**/**.test.js'],
28 | },
29 | ],
30 | 'jsx-a11y/no-noninteractive-element-interactions': 0,
31 | 'jsx-a11y/click-events-have-key-events': 0,
32 | 'jsx-a11y/no-static-element-interactions': 0,
33 | 'jsx-a11y/anchor-is-valid': 0,
34 | 'linebreak-style': 0,
35 | },
36 | settings: {
37 | polyfills: ['fetch', 'promises', 'url'],
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/src/components/AvatarList/demo/maxLength.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title:
4 | zh-CN: 要显示的最大项目
5 | en-US: Max Items to Show
6 | ---
7 |
8 | `maxLength` attribute specifies the maximum number of items to show while `excessItemsStyle` style the excess
9 | item component.
10 |
11 | ````jsx
12 | import AvatarList from 'ant-design-pro/lib/AvatarList';
13 |
14 | ReactDOM.render(
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | , mountNode);
24 | ````
25 |
--------------------------------------------------------------------------------
/config/plugin.config.js:
--------------------------------------------------------------------------------
1 | // Change theme plugin
2 |
3 | import MergeLessPlugin from 'antd-pro-merge-less';
4 | import AntDesignThemePlugin from 'antd-theme-webpack-plugin';
5 | import path from 'path';
6 |
7 | export default config => {
8 | // pro 和 开发环境再添加这个插件
9 | if (process.env.APP_TYPE === 'site' || process.env.NODE_ENV !== 'production') {
10 | // 将所有 less 合并为一个供 themePlugin使用
11 | const outFile = path.join(__dirname, '../.temp/ant-design-pro.less');
12 | const stylesDir = path.join(__dirname, '../src/');
13 |
14 | config.plugin('merge-less').use(MergeLessPlugin, [
15 | {
16 | stylesDir,
17 | outFile,
18 | },
19 | ]);
20 |
21 | config.plugin('ant-design-theme').use(AntDesignThemePlugin, [
22 | {
23 | antDir: path.join(__dirname, '../node_modules/antd'),
24 | stylesDir,
25 | varFile: path.join(__dirname, '../node_modules/antd/lib/style/themes/default.less'),
26 | mainLessFile: outFile, // themeVariables: ['@primary-color'],
27 | indexFileName: 'index.html',
28 | generateOne: true,
29 | lessUrl: 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js',
30 | },
31 | ]);
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/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 | @media screen and (max-width: @screen-xs) {
8 | width: 100%;
9 | }
10 |
11 | .icon {
12 | font-size: 72px;
13 | line-height: 72px;
14 | margin-bottom: 24px;
15 |
16 | & > .success {
17 | color: @success-color;
18 | }
19 |
20 | & > .error {
21 | color: @error-color;
22 | }
23 | }
24 |
25 | .title {
26 | font-size: 24px;
27 | color: @heading-color;
28 | font-weight: 500;
29 | line-height: 32px;
30 | margin-bottom: 16px;
31 | }
32 |
33 | .description {
34 | font-size: 14px;
35 | line-height: 22px;
36 | color: @text-color-secondary;
37 | margin-bottom: 24px;
38 | }
39 |
40 | .extra {
41 | background: #fafafa;
42 | padding: 24px 40px;
43 | border-radius: @border-radius-sm;
44 | text-align: left;
45 |
46 | @media screen and (max-width: @screen-xs) {
47 | padding: 18px 20px;
48 | }
49 | }
50 |
51 | .actions {
52 | margin-top: 32px;
53 |
54 | button:not(:last-child) {
55 | margin-right: 8px;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/locales/pt-BR.js:
--------------------------------------------------------------------------------
1 | import analysis from './pt-BR/analysis';
2 | import exception from './pt-BR/exception';
3 | import form from './pt-BR/form';
4 | import globalHeader from './pt-BR/globalHeader';
5 | import login from './pt-BR/login';
6 | import menu from './pt-BR/menu';
7 | import monitor from './pt-BR/monitor';
8 | import result from './pt-BR/result';
9 | import settingDrawer from './pt-BR/settingDrawer';
10 | import settings from './pt-BR/settings';
11 | import pwa from './pt-BR/pwa';
12 |
13 | export default {
14 | 'navBar.lang': 'Idiomas',
15 | 'layout.user.link.help': 'ajuda',
16 | 'layout.user.link.privacy': 'política de privacidade',
17 | 'layout.user.link.terms': 'termos de serviços',
18 | 'app.home.introduce': 'introduzir',
19 | 'app.forms.basic.title': 'Basic form',
20 | 'app.forms.basic.description':
21 | 'Páginas de formulário são usadas para coletar e verificar as informações dos usuários e formulários básicos são comuns nos cenários onde existem alguns formatos de informações.',
22 | ...analysis,
23 | ...exception,
24 | ...form,
25 | ...globalHeader,
26 | ...login,
27 | ...menu,
28 | ...monitor,
29 | ...result,
30 | ...settingDrawer,
31 | ...settings,
32 | ...pwa,
33 | };
34 |
--------------------------------------------------------------------------------
/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/pages/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/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 | | maxLength | max items to show | number | - |
17 | | excessItemsStyle | the excess item style | CSSProperties | - |
18 |
19 | ### AvatarList.Item
20 |
21 | | Property | Description | Type | Default |
22 | | -------- | -------------------------------------------- | --------- | ------- |
23 | | tips | title tips for avatar item | ReactNode | - |
24 | | src | the address of the image for an image avatar | string | - |
25 |
--------------------------------------------------------------------------------
/src/pages/Forms/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/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.totalCount,
47 | unreadCount: action.payload.unreadCount,
48 | },
49 | };
50 | },
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/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 propTypes = {
10 | links: PropTypes.array,
11 | onAdd: PropTypes.func,
12 | linkElement: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
13 | };
14 |
15 | static defaultProps = {
16 | links: [],
17 | onAdd: () => {},
18 | linkElement: 'a',
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 |
40 | }
41 |
42 | );
43 | }
44 | }
45 |
46 | export default EditableLinkGroup;
47 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/result.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.result.error.title': '提交失败',
3 | 'app.result.error.description': '请核对并修改以下信息后,再重新提交。',
4 | 'app.result.error.hint-title': '您提交的内容有如下错误:',
5 | 'app.result.error.hint-text1': '您的账户已被冻结',
6 | 'app.result.error.hint-btn1': '立即解冻',
7 | 'app.result.error.hint-text2': '您的账户还不具备申请资格',
8 | 'app.result.error.hint-btn2': '立即升级',
9 | 'app.result.error.btn-text': '返回修改',
10 | 'app.result.success.title': '提交成功',
11 | 'app.result.success.description':
12 | '提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,使用 Message 全局提示反馈即可。 本文字区域可以展示简单的补充说明,如果有类似展示 “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。',
13 | 'app.result.success.operate-title': '项目名称',
14 | 'app.result.success.operate-id': '项目 ID:',
15 | 'app.result.success.principal': '负责人:',
16 | 'app.result.success.operate-time': '生效时间:',
17 | 'app.result.success.step1-title': '创建项目',
18 | 'app.result.success.step1-operator': '曲丽丽',
19 | 'app.result.success.step2-title': '部门初审',
20 | 'app.result.success.step2-operator': '周毛毛',
21 | 'app.result.success.step2-extra': '催一下',
22 | 'app.result.success.step3-title': '财务复核',
23 | 'app.result.success.step4-title': '完成',
24 | 'app.result.success.btn-return': '返回列表',
25 | 'app.result.success.btn-project': '查看项目',
26 | 'app.result.success.btn-print': '打印',
27 | };
28 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/result.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.result.error.title': '提交失敗',
3 | 'app.result.error.description': '請核對並修改以下信息後,再重新提交。',
4 | 'app.result.error.hint-title': '您提交的內容有如下錯誤:',
5 | 'app.result.error.hint-text1': '您的賬戶已被凍結',
6 | 'app.result.error.hint-btn1': '立即解凍',
7 | 'app.result.error.hint-text2': '您的賬戶還不具備申請資格',
8 | 'app.result.error.hint-btn2': '立即升級',
9 | 'app.result.error.btn-text': '返回修改',
10 | 'app.result.success.title': '提交成功',
11 | 'app.result.success.description':
12 | '提交結果頁用於反饋壹系列操作任務的處理結果, 如果僅是簡單操作,使用 Message 全局提示反饋即可。 本文字區域可以展示簡單的補充說明,如果有類似展示 “單據”的需求,下面這個灰色區域可以呈現比較復雜的內容。',
13 | 'app.result.success.operate-title': '項目名稱',
14 | 'app.result.success.operate-id': '項目 ID:',
15 | 'app.result.success.principal': '負責人:',
16 | 'app.result.success.operate-time': '生效時間:',
17 | 'app.result.success.step1-title': '創建項目',
18 | 'app.result.success.step1-operator': '曲麗麗',
19 | 'app.result.success.step2-title': '部門初審',
20 | 'app.result.success.step2-operator': '周毛毛',
21 | 'app.result.success.step2-extra': '催壹下',
22 | 'app.result.success.step3-title': '財務復核',
23 | 'app.result.success.step4-title': '完成',
24 | 'app.result.success.btn-return': '返回列表',
25 | 'app.result.success.btn-project': '查看項目',
26 | 'app.result.success.btn-print': '打印',
27 | };
28 |
--------------------------------------------------------------------------------
/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 | const NumberInfo = ({ theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest }) => (
7 |
13 | {title && (
14 |
15 | {title}
16 |
17 | )}
18 | {subTitle && (
19 |
23 | {subTitle}
24 |
25 | )}
26 |
27 |
28 | {total}
29 | {suffix && {suffix}}
30 |
31 | {(status || subTotal) && (
32 |
33 | {subTotal}
34 | {status && }
35 |
36 | )}
37 |
38 |
39 | );
40 |
41 | export default NumberInfo;
42 |
--------------------------------------------------------------------------------
/src/pages/User/RegisterResult.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { formatMessage, FormattedMessage } from 'umi/locale';
3 | import { Button } from 'antd';
4 | import Link from 'umi/link';
5 | import Result from '@/components/Result';
6 | import styles from './RegisterResult.less';
7 |
8 | const actions = (
9 |
21 | );
22 |
23 | const RegisterResult = ({ location }) => (
24 |
29 |
33 |
34 | }
35 | description={formatMessage({ id: 'app.register-result.activation-email' })}
36 | actions={actions}
37 | style={{ marginTop: 56 }}
38 | />
39 | );
40 |
41 | export default RegisterResult;
42 |
--------------------------------------------------------------------------------
/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 |
40 |
41 |
42 |
43 | , mountNode);
44 | ````
--------------------------------------------------------------------------------
/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 | ## Login.Password、Login.Mobile 同 Login.UserName
36 |
37 | ### Login.Captcha
38 |
39 | 参数 | 说明 | 类型 | 默认值
40 | ----|------|-----|------
41 | onGetCaptcha | 点击获取校验码的回调 | () => (void \| false \| Promise) | -
42 | countDown | 倒计时 | number |-
43 | buttonText | 点击获取校验码的说明文字 | ReactNode | '获取验证码'
44 |
45 | 除上述属性以外,Login.Captcha 支持的属性与 Login.UserName 相同。
46 |
47 | ### Login.Submit
48 |
49 | 支持 antd.Button 的所有属性。
50 |
--------------------------------------------------------------------------------
/src/e2e/login.e2e.js:
--------------------------------------------------------------------------------
1 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
2 |
3 | describe('Login', () => {
4 | beforeAll(async () => {
5 | jest.setTimeout(1000000);
6 | });
7 |
8 | beforeEach(async () => {
9 | await page.goto(`${BASE_URL}/user/login`, { waitUntil: 'networkidle2' });
10 | await page.evaluate(() => window.localStorage.setItem('antd-pro-authority', 'guest'));
11 | });
12 |
13 | it('should login with failure', async () => {
14 | await page.waitForSelector('#userName', {
15 | timeout: 2000,
16 | });
17 | await page.type('#userName', 'mockuser');
18 | await page.type('#password', 'wrong_password');
19 | await page.click('button[type="submit"]');
20 | await page.waitForSelector('.ant-alert-error'); // should display error
21 | });
22 |
23 | it('should login successfully', async () => {
24 | await page.waitForSelector('#userName', {
25 | timeout: 2000,
26 | });
27 | await page.type('#userName', 'admin');
28 | await page.type('#password', 'ant.design');
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 Design Pro
');
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tests/run-tests.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const { spawn } = require('child_process');
3 | const { kill } = require('cross-port-killer');
4 |
5 | const env = Object.create(process.env);
6 | env.BROWSER = 'none';
7 | env.TEST = true;
8 | // flag to prevent multiple test
9 | let once = false;
10 |
11 | const startServer = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['start'], {
12 | env,
13 | });
14 |
15 | startServer.stderr.on('data', data => {
16 | // eslint-disable-next-line
17 | console.log(data.toString());
18 | });
19 |
20 | startServer.on('exit', () => {
21 | kill(process.env.PORT || 8000);
22 | });
23 |
24 | console.log('Starting development server for e2e tests...');
25 | startServer.stdout.on('data', data => {
26 | console.log(data.toString());
27 | if (!once && data.toString().indexOf('Compiled successfully') >= 0) {
28 | // eslint-disable-next-line
29 | once = true;
30 | console.log('Development server is started, ready to run tests.');
31 | const testCmd = spawn(
32 | /^win/.test(process.platform) ? 'npm.cmd' : 'npm',
33 | ['test', '--', '--maxWorkers=1', '--runInBand'],
34 | {
35 | stdio: 'inherit',
36 | }
37 | );
38 | testCmd.on('exit', code => {
39 | startServer.kill();
40 | process.exit(code);
41 | });
42 | }
43 | });
44 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/settingDrawer.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': '整体风格设置',
3 | 'app.setting.pagestyle.dark': '暗色菜单风格',
4 | 'app.setting.pagestyle.light': '亮色菜单风格',
5 | 'app.setting.content-width': '内容区域宽度',
6 | 'app.setting.content-width.fixed': '定宽',
7 | 'app.setting.content-width.fluid': '流式',
8 | 'app.setting.themecolor': '主题色',
9 | 'app.setting.themecolor.dust': '薄暮',
10 | 'app.setting.themecolor.volcano': '火山',
11 | 'app.setting.themecolor.sunset': '日暮',
12 | 'app.setting.themecolor.cyan': '明青',
13 | 'app.setting.themecolor.green': '极光绿',
14 | 'app.setting.themecolor.daybreak': '拂晓蓝(默认)',
15 | 'app.setting.themecolor.geekblue': '极客蓝',
16 | 'app.setting.themecolor.purple': '酱紫',
17 | 'app.setting.navigationmode': '导航模式',
18 | 'app.setting.sidemenu': '侧边菜单布局',
19 | 'app.setting.topmenu': '顶部菜单布局',
20 | 'app.setting.fixedheader': '固定 Header',
21 | 'app.setting.fixedsidebar': '固定侧边菜单',
22 | 'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置',
23 | 'app.setting.hideheader': '下滑时隐藏 Header',
24 | 'app.setting.hideheader.hint': '固定 Header 时可配置',
25 | 'app.setting.othersettings': '其他设置',
26 | 'app.setting.weakmode': '色弱模式',
27 | 'app.setting.copy': '拷贝设置',
28 | 'app.setting.copyinfo': '拷贝成功,请到 src/defaultSettings.js 中替换默认配置',
29 | 'app.setting.production.hint':
30 | '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件',
31 | };
32 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/settingDrawer.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': '整體風格設置',
3 | 'app.setting.pagestyle.dark': '暗色菜單風格',
4 | 'app.setting.pagestyle.light': '亮色菜單風格',
5 | 'app.setting.content-width': '內容區域寬度',
6 | 'app.setting.content-width.fixed': '定寬',
7 | 'app.setting.content-width.fluid': '流式',
8 | 'app.setting.themecolor': '主題色',
9 | 'app.setting.themecolor.dust': '薄暮',
10 | 'app.setting.themecolor.volcano': '火山',
11 | 'app.setting.themecolor.sunset': '日暮',
12 | 'app.setting.themecolor.cyan': '明青',
13 | 'app.setting.themecolor.green': '極光綠',
14 | 'app.setting.themecolor.daybreak': '拂曉藍(默認)',
15 | 'app.setting.themecolor.geekblue': '極客藍',
16 | 'app.setting.themecolor.purple': '醬紫',
17 | 'app.setting.navigationmode': '導航模式',
18 | 'app.setting.sidemenu': '側邊菜單布局',
19 | 'app.setting.topmenu': '頂部菜單布局',
20 | 'app.setting.fixedheader': '固定 Header',
21 | 'app.setting.fixedsidebar': '固定側邊菜單',
22 | 'app.setting.fixedsidebar.hint': '側邊菜單布局時可配置',
23 | 'app.setting.hideheader': '下滑時隱藏 Header',
24 | 'app.setting.hideheader.hint': '固定 Header 時可配置',
25 | 'app.setting.othersettings': '其他設置',
26 | 'app.setting.weakmode': '色弱模式',
27 | 'app.setting.copy': '拷貝設置',
28 | 'app.setting.copyinfo': '拷貝成功,請到 src/defaultSettings.js 中替換默認配置',
29 | 'app.setting.production.hint':
30 | '配置欄只在開發環境用於預覽,生產環境不會展現,請拷貝後手動修改配置文件',
31 | };
32 |
--------------------------------------------------------------------------------
/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 | 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 | export default MiniBar;
52 |
--------------------------------------------------------------------------------
/scripts/prettier.js:
--------------------------------------------------------------------------------
1 | /**
2 | * copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
3 | * prettier api doc https://prettier.io/docs/en/api.html
4 | *----------*****--------------
5 | * prettier all js and all ts.
6 | *----------*****--------------
7 | */
8 |
9 | const glob = require('glob');
10 | const prettier = require('prettier');
11 | const fs = require('fs');
12 | const getPrettierFiles = require('./getPrettierFiles');
13 | const prettierConfigPath = require.resolve('../.prettierrc');
14 |
15 | let didError = false;
16 |
17 | const files = getPrettierFiles();
18 |
19 | files.forEach(file => {
20 | const options = prettier.resolveConfig.sync(file, {
21 | config: prettierConfigPath,
22 | });
23 | const fileInfo = prettier.getFileInfo.sync(file);
24 | if (fileInfo.ignored) {
25 | return;
26 | }
27 | try {
28 | const input = fs.readFileSync(file, 'utf8');
29 | const withParserOptions = {
30 | ...options,
31 | parser: fileInfo.inferredParser,
32 | };
33 | const output = prettier.format(input, withParserOptions);
34 | if (output !== input) {
35 | fs.writeFileSync(file, output, 'utf8');
36 | console.log(`\x1b[34m ${file} is prettier`);
37 | }
38 | } catch (e) {
39 | didError = true;
40 | }
41 | });
42 |
43 | if (didError) {
44 | process.exit(1);
45 | }
46 | console.log('\x1b[32m prettier success!');
47 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/analysis.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.analysis.test': '工专路 {no} 号店',
3 | 'app.analysis.introduce': '指标说明',
4 | 'app.analysis.total-sales': '总销售额',
5 | 'app.analysis.day-sales': '日销售额',
6 | 'app.analysis.visits': '访问量',
7 | 'app.analysis.visits-trend': '访问量趋势',
8 | 'app.analysis.visits-ranking': '门店访问量排名',
9 | 'app.analysis.day-visits': '日访问量',
10 | 'app.analysis.week': '周同比',
11 | 'app.analysis.day': '日同比',
12 | 'app.analysis.payments': '支付笔数',
13 | 'app.analysis.conversion-rate': '转化率',
14 | 'app.analysis.operational-effect': '运营活动效果',
15 | 'app.analysis.sales-trend': '销售趋势',
16 | 'app.analysis.sales-ranking': '门店销售额排名',
17 | 'app.analysis.all-year': '全年',
18 | 'app.analysis.all-month': '本月',
19 | 'app.analysis.all-week': '本周',
20 | 'app.analysis.all-day': '今日',
21 | 'app.analysis.search-users': '搜索用户数',
22 | 'app.analysis.per-capita-search': '人均搜索次数',
23 | 'app.analysis.online-top-search': '线上热门搜索',
24 | 'app.analysis.the-proportion-of-sales': '销售额类别占比',
25 | 'app.analysis.channel.all': '全部渠道',
26 | 'app.analysis.channel.online': '线上',
27 | 'app.analysis.channel.stores': '门店',
28 | 'app.analysis.sales': '销售额',
29 | 'app.analysis.traffic': '客流量',
30 | 'app.analysis.table.rank': '排名',
31 | 'app.analysis.table.search-keyword': '搜索关键词',
32 | 'app.analysis.table.users': '用户数',
33 | 'app.analysis.table.weekly-range': '周涨幅',
34 | };
35 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/analysis.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.analysis.test': '工專路 {no} 號店',
3 | 'app.analysis.introduce': '指標說明',
4 | 'app.analysis.total-sales': '總銷售額',
5 | 'app.analysis.day-sales': '日銷售額',
6 | 'app.analysis.visits': '訪問量',
7 | 'app.analysis.visits-trend': '訪問量趨勢',
8 | 'app.analysis.visits-ranking': '門店訪問量排名',
9 | 'app.analysis.day-visits': '日訪問量',
10 | 'app.analysis.week': '周同比',
11 | 'app.analysis.day': '日同比',
12 | 'app.analysis.payments': '支付筆數',
13 | 'app.analysis.conversion-rate': '轉化率',
14 | 'app.analysis.operational-effect': '運營活動效果',
15 | 'app.analysis.sales-trend': '銷售趨勢',
16 | 'app.analysis.sales-ranking': '門店銷售額排名',
17 | 'app.analysis.all-year': '全年',
18 | 'app.analysis.all-month': '本月',
19 | 'app.analysis.all-week': '本周',
20 | 'app.analysis.all-day': '今日',
21 | 'app.analysis.search-users': '搜索用戶數',
22 | 'app.analysis.per-capita-search': '人均搜索次數',
23 | 'app.analysis.online-top-search': '線上熱門搜索',
24 | 'app.analysis.the-proportion-of-sales': '銷售額類別占比',
25 | 'app.analysis.channel.all': '全部渠道',
26 | 'app.analysis.channel.online': '線上',
27 | 'app.analysis.channel.stores': '門店',
28 | 'app.analysis.sales': '銷售額',
29 | 'app.analysis.traffic': '客流量',
30 | 'app.analysis.table.rank': '排名',
31 | 'app.analysis.table.search-keyword': '搜索關鍵詞',
32 | 'app.analysis.table.users': '用戶數',
33 | 'app.analysis.table.weekly-range': '周漲幅',
34 | };
35 |
--------------------------------------------------------------------------------
/src/pages/Forms/StepForm/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent, Fragment } from 'react';
2 | import { Card, Steps } from 'antd';
3 | import PageHeaderWrapper from '@/components/PageHeaderWrapper';
4 | import styles from '../style.less';
5 |
6 | const { Step } = Steps;
7 |
8 | export default class StepForm extends PureComponent {
9 | getCurrentStep() {
10 | const { location } = this.props;
11 | const { pathname } = location;
12 | const pathList = pathname.split('/');
13 | switch (pathList[pathList.length - 1]) {
14 | case 'info':
15 | return 0;
16 | case 'confirm':
17 | return 1;
18 | case 'result':
19 | return 2;
20 | default:
21 | return 0;
22 | }
23 | }
24 |
25 | render() {
26 | const { location, children } = this.props;
27 | return (
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {children}
41 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: NoticeIcon
3 | subtitle: 通知菜单
4 | cols: 1
5 | order: 9
6 | ---
7 |
8 | 用在导航工具栏上,作为整个产品统一的通知中心。
9 |
10 | ## API
11 |
12 | 参数 | 说明 | 类型 | 默认值
13 | ----|------|-----|------
14 | count | 图标上的消息总数 | number | -
15 | bell | translate this please -> Change the bell Icon | ReactNode | ``
16 | loading | 弹出卡片加载状态 | boolean | false
17 | onClear | 点击清空按钮的回调 | function(tabName) | -
18 | onItemClick | 点击列表项的回调 | function(item, tabProps) | -
19 | onTabChange | 切换页签的回调 | function(tabTitle) | -
20 | onPopupVisibleChange | 弹出卡片显隐的回调 | function(visible) | -
21 | popupVisible | 控制弹层显隐 | boolean | -
22 | locale | 默认文案 | Object | `{ emptyText: '暂无数据', clear: '清空' }`
23 | clearClose | 点击清空按钮后关闭通知菜单 | boolean | false
24 |
25 | ### NoticeIcon.Tab
26 |
27 | 参数 | 说明 | 类型 | 默认值
28 | ----|------|-----|------
29 | title | 消息分类的页签标题 | string | -
30 | name | 消息分类的标识符 | string | -
31 | list | 列表数据,格式参照下表 | Array | `[]`
32 | showClear | 是否显示清空按钮 | boolean | true
33 | emptyText | 针对每个 Tab 定制空数据文案 | ReactNode | -
34 | emptyImage | 针对每个 Tab 定制空数据图片 | string | -
35 |
36 |
37 | ### Tab data
38 |
39 | 参数 | 说明 | 类型 | 默认值
40 | ----|------|-----|------
41 | avatar | 头像图片链接 | string \| ReactNode | -
42 | title | 标题 | ReactNode | -
43 | description | 描述信息 | ReactNode | -
44 | datetime | 时间戳 | ReactNode | -
45 | extra | 额外信息,在列表项右上角 | ReactNode | -
46 | clickClose | 点击列表项关闭通知菜单 | boolean | false
47 |
--------------------------------------------------------------------------------
/src/pages/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 | class TriggerException extends PureComponent {
10 | state = {
11 | isloading: false,
12 | };
13 |
14 | triggerError = code => {
15 | this.setState({
16 | isloading: true,
17 | });
18 | const { dispatch } = this.props;
19 | dispatch({
20 | type: 'error/query',
21 | payload: {
22 | code,
23 | },
24 | });
25 | };
26 |
27 | render() {
28 | const { isloading } = this.state;
29 | return (
30 |
31 |
32 |
35 |
38 |
41 |
44 |
45 |
46 | );
47 | }
48 | }
49 |
50 | export default TriggerException;
51 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/menu.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.home': '首頁',
3 | 'menu.dashboard': 'Dashboard',
4 | 'menu.dashboard.analysis': '分析頁',
5 | 'menu.dashboard.monitor': '監控頁',
6 | 'menu.dashboard.workplace': '工作臺',
7 | 'menu.form': '表單頁',
8 | 'menu.form.basicform': '基礎表單',
9 | 'menu.form.stepform': '分步表單',
10 | 'menu.form.stepform.info': '分步表單(填寫轉賬信息)',
11 | 'menu.form.stepform.confirm': '分步表單(確認轉賬信息)',
12 | 'menu.form.stepform.result': '分步表單(完成)',
13 | 'menu.form.advancedform': '高級表單',
14 | 'menu.list': '列表頁',
15 | 'menu.list.searchtable': '查詢表格',
16 | 'menu.list.basiclist': '標淮列表',
17 | 'menu.list.cardlist': '卡片列表',
18 | 'menu.list.searchlist': '搜索列表',
19 | 'menu.list.searchlist.articles': '搜索列表(文章)',
20 | 'menu.list.searchlist.projects': '搜索列表(項目)',
21 | 'menu.list.searchlist.applications': '搜索列表(應用)',
22 | 'menu.profile': '詳情頁',
23 | 'menu.profile.basic': '基礎詳情頁',
24 | 'menu.profile.advanced': '高級詳情頁',
25 | 'menu.result': '結果頁',
26 | 'menu.result.success': '成功頁',
27 | 'menu.result.fail': '失敗頁',
28 | 'menu.account': '個人頁',
29 | 'menu.account.center': '個人中心',
30 | 'menu.account.settings': '個人設置',
31 | 'menu.account.trigger': '觸發報錯',
32 | 'menu.account.logout': '退出登錄',
33 | 'menu.exception': '异常页',
34 | 'menu.exception.not-permission': '403',
35 | 'menu.exception.not-find': '404',
36 | 'menu.exception.server-error': '500',
37 | 'menu.exception.trigger': '触发错误',
38 | };
39 |
--------------------------------------------------------------------------------
/src/pages/List/models/rule.js:
--------------------------------------------------------------------------------
1 | import { queryRule, removeRule, addRule, updateRule } 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 | *update({ payload, callback }, { call, put }) {
38 | const response = yield call(updateRule, payload);
39 | yield put({
40 | type: 'save',
41 | payload: response,
42 | });
43 | if (callback) callback();
44 | },
45 | },
46 |
47 | reducers: {
48 | save(state, action) {
49 | return {
50 | ...state,
51 | data: action.payload,
52 | };
53 | },
54 | },
55 | };
56 |
--------------------------------------------------------------------------------
/src/components/Media/styles.less:
--------------------------------------------------------------------------------
1 | .media_list {
2 | overflow: hidden;
3 |
4 | .page_view {
5 | display: flex;
6 | justify-content: center;
7 | }
8 |
9 | .image_item {
10 | background-size: cover;
11 | background-position: center;
12 | }
13 |
14 | .ant-list-pagination {
15 | margin-top: 0;
16 | }
17 | }
18 |
19 |
20 |
21 |
22 | .media_item {
23 | position: relative;
24 |
25 | .item_check {
26 | position: absolute;
27 | top: 10px;
28 | left: 10px;
29 | }
30 |
31 | .attachment_selected {
32 | position: absolute;
33 | top: 0;
34 | left: 0;
35 | width: 100%;
36 | height: 100%;
37 | border: 2px solid @primary-color;
38 | -webkit-box-sizing: border-box;
39 | -moz-box-sizing: border-box;
40 | box-sizing: border-box;
41 |
42 | .selected_index {
43 | position: absolute;
44 | right: 0;
45 | top: 0;
46 | z-index: 2;
47 | font-size: 12px;
48 | color: #fff;
49 | font-style: normal;
50 | line-height: 15px;
51 | min-width: 15px;
52 | text-align: center;
53 | height: 15px;
54 | }
55 | }
56 |
57 | .attachment_selected:after {
58 | position: absolute;
59 | display: block;
60 | content: " ";
61 | right: 0;
62 | top: 0;
63 | border: 15px solid @primary-color;
64 | border-left-color: transparent;
65 | border-bottom-color: transparent;
66 | z-index: 1;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/TagSelect/demo/controlled.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 3
3 | title: 受控模式
4 | ---
5 |
6 | 结合 `Tag` 的 `TagSelect` 组件,方便的应用于筛选类目的业务场景中。
7 |
8 | ```jsx
9 | import { Button } from 'antd';
10 | import TagSelect from 'ant-design-pro/lib/TagSelect';
11 |
12 | class Demo extends React.Component {
13 | state = {
14 | value: ['cat1'],
15 | };
16 | handleFormSubmit = value => {
17 | this.setState({
18 | value,
19 | });
20 | };
21 | checkAll = () => {
22 | this.setState({
23 | value: ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6'],
24 | });
25 | };
26 | render() {
27 | return (
28 |
29 |
30 |
35 |
36 | 类目一
37 | 类目二
38 | 类目三
39 | 类目四
40 | 类目五
41 | 类目六
42 |
43 |
44 |
45 | );
46 | }
47 | }
48 |
49 | ReactDOM.render(, mountNode);
50 | ```
51 |
--------------------------------------------------------------------------------