提交 a00307523ad9891010a1af098cf9d864fab5bedd

作者 chencheng (云谦)
提交者 Yu
1 个父辈 be62a6da

feat: improve last 4 pages (#8)

正在显示 88 个修改的文件 包含 1430 行增加2951 行删除

要显示太多修改。

为保证性能只显示 88 of 88+ 个文件。

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 updateRule(params) {  
37 - return request('/api/rule', {  
38 - method: 'POST',  
39 - body: {  
40 - ...params,  
41 - method: 'update',  
42 - },  
43 - });  
44 -}  
45 -  
46 -export async function fakeSubmitForm(params) {  
47 - return request('/api/forms', {  
48 - method: 'POST',  
49 - body: params,  
50 - });  
51 -}  
52 -  
53 -export async function fakeChartData() {  
54 - return request('/api/fake_chart_data');  
55 -}  
56 -  
57 -export async function queryTags() {  
58 - return request('/api/tags');  
59 -}  
60 -  
61 -export async function queryBasicProfile() {  
62 - return request('/api/profile/basic');  
63 -}  
64 -  
65 -export async function queryAdvancedProfile() {  
66 - return request('/api/profile/advanced');  
67 -}  
68 -  
69 -export async function queryFakeList(params) {  
70 - return request(`/api/fake_list?${stringify(params)}`);  
71 -}  
72 -  
73 -export async function removeFakeList(params) {  
74 - const { count = 5, ...restParams } = params;  
75 - return request(`/api/fake_list?count=${count}`, {  
76 - method: 'POST',  
77 - body: {  
78 - ...restParams,  
79 - method: 'delete',  
80 - },  
81 - });  
82 -}  
83 -  
84 -export async function addFakeList(params) {  
85 - const { count = 5, ...restParams } = params;  
86 - return request(`/api/fake_list?count=${count}`, {  
87 - method: 'POST',  
88 - body: {  
89 - ...restParams,  
90 - method: 'post',  
91 - },  
92 - });  
93 -}  
94 -  
95 -export async function updateFakeList(params) {  
96 - const { count = 5, ...restParams } = params;  
97 - return request(`/api/fake_list?count=${count}`, {  
98 - method: 'POST',  
99 - body: {  
100 - ...restParams,  
101 - method: 'update',  
102 - },  
103 - });  
104 -}  
105 -  
106 -export async function fakeAccountLogin(params) {  
107 - return request('/api/login/account', {  
108 - method: 'POST',  
109 - body: params,  
110 - });  
111 -}  
112 -  
113 -export async function fakeRegister(params) {  
114 - return request('/api/register', {  
115 - method: 'POST',  
116 - body: params,  
117 - });  
118 -}  
119 -  
120 -export async function queryNotices() {  
121 - return request('/api/notices');  
122 -}  
123 -  
124 -export async function getFakeCaptcha(mobile) {  
125 - return request(`/api/captcha?mobile=${mobile}`);  
126 -}  
1 -import fetch from 'dva/fetch';  
2 -import { notification } from 'antd';  
3 -import router from 'umi/router';  
4 -import hash from 'hash.js';  
5 -import { isAntdPro } from './utils';  
6 -  
7 -const codeMessage = {  
8 - 200: '服务器成功返回请求的数据。',  
9 - 201: '新建或修改数据成功。',  
10 - 202: '一个请求已经进入后台排队(异步任务)。',  
11 - 204: '删除数据成功。',  
12 - 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',  
13 - 401: '用户没有权限(令牌、用户名、密码错误)。',  
14 - 403: '用户得到授权,但是访问是被禁止的。',  
15 - 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',  
16 - 406: '请求的格式不可得。',  
17 - 410: '请求的资源被永久删除,且不会再得到的。',  
18 - 422: '当创建一个对象时,发生一个验证错误。',  
19 - 500: '服务器发生错误,请检查服务器。',  
20 - 502: '网关错误。',  
21 - 503: '服务不可用,服务器暂时过载或维护。',  
22 - 504: '网关超时。',  
23 -};  
24 -  
25 -const checkStatus = response => {  
26 - if (response.status >= 200 && response.status < 300) {  
27 - return response;  
28 - }  
29 - const errortext = codeMessage[response.status] || response.statusText;  
30 - notification.error({  
31 - message: `请求错误 ${response.status}: ${response.url}`,  
32 - description: errortext,  
33 - });  
34 - const error = new Error(errortext);  
35 - error.name = response.status;  
36 - error.response = response;  
37 - throw error;  
38 -};  
39 -  
40 -const cachedSave = (response, hashcode) => {  
41 - /**  
42 - * Clone a response data and store it in sessionStorage  
43 - * Does not support data other than json, Cache only json  
44 - */  
45 - const contentType = response.headers.get('Content-Type');  
46 - if (contentType && contentType.match(/application\/json/i)) {  
47 - // All data is saved as text  
48 - response  
49 - .clone()  
50 - .text()  
51 - .then(content => {  
52 - sessionStorage.setItem(hashcode, content);  
53 - sessionStorage.setItem(`${hashcode}:timestamp`, Date.now());  
54 - });  
55 - }  
56 - return response;  
57 -};  
58 -  
59 -/**  
60 - * Requests a URL, returning a promise.  
61 - *  
62 - * @param {string} url The URL we want to request  
63 - * @param {object} [option] The options we want to pass to "fetch"  
64 - * @return {object} An object containing either "data" or "err"  
65 - */  
66 -export default function request(url, option) {  
67 - const options = {  
68 - expirys: isAntdPro(),  
69 - ...option,  
70 - };  
71 - /**  
72 - * Produce fingerprints based on url and parameters  
73 - * Maybe url has the same parameters  
74 - */  
75 - const fingerprint = url + (options.body ? JSON.stringify(options.body) : '');  
76 - const hashcode = hash  
77 - .sha256()  
78 - .update(fingerprint)  
79 - .digest('hex');  
80 -  
81 - const defaultOptions = {  
82 - credentials: 'include',  
83 - };  
84 - const newOptions = { ...defaultOptions, ...options };  
85 - if (  
86 - newOptions.method === 'POST' ||  
87 - newOptions.method === 'PUT' ||  
88 - newOptions.method === 'DELETE'  
89 - ) {  
90 - if (!(newOptions.body instanceof FormData)) {  
91 - newOptions.headers = {  
92 - Accept: 'application/json',  
93 - 'Content-Type': 'application/json; charset=utf-8',  
94 - ...newOptions.headers,  
95 - };  
96 - newOptions.body = JSON.stringify(newOptions.body);  
97 - } else {  
98 - // newOptions.body is FormData  
99 - newOptions.headers = {  
100 - Accept: 'application/json',  
101 - ...newOptions.headers,  
102 - };  
103 - }  
104 - }  
105 -  
106 - const expirys = options.expirys && 60;  
107 - // options.expirys !== false, return the cache,  
108 - if (options.expirys !== false) {  
109 - const cached = sessionStorage.getItem(hashcode);  
110 - const whenCached = sessionStorage.getItem(`${hashcode}:timestamp`);  
111 - if (cached !== null && whenCached !== null) {  
112 - const age = (Date.now() - whenCached) / 1000;  
113 - if (age < expirys) {  
114 - const response = new Response(new Blob([cached]));  
115 - return response.json();  
116 - }  
117 - sessionStorage.removeItem(hashcode);  
118 - sessionStorage.removeItem(`${hashcode}:timestamp`);  
119 - }  
120 - }  
121 - return fetch(url, newOptions)  
122 - .then(checkStatus)  
123 - .then(response => cachedSave(response, hashcode))  
124 - .then(response => {  
125 - // DELETE and 204 do not return data by default  
126 - // using .json will report an error.  
127 - if (newOptions.method === 'DELETE' || response.status === 204) {  
128 - return response.text();  
129 - }  
130 - return response.json();  
131 - })  
132 - .catch(e => {  
133 - const status = e.name;  
134 - if (status === 401) {  
135 - // @HACK  
136 - /* eslint-disable no-underscore-dangle */  
137 - window.g_app._store.dispatch({  
138 - type: 'login/logout',  
139 - });  
140 - return;  
141 - }  
142 - // environment should not be used  
143 - if (status === 403) {  
144 - router.push('/exception/403');  
145 - return;  
146 - }  
147 - if (status <= 504 && status >= 500) {  
148 - router.push('/exception/500');  
149 - return;  
150 - }  
151 - if (status >= 404 && status < 422) {  
152 - router.push('/exception/404');  
153 - }  
154 - });  
155 -}  
@@ -11,19 +11,17 @@ @@ -11,19 +11,17 @@
11 "url": "https://github.com/umijs/umi-blocks/userlogin" 11 "url": "https://github.com/umijs/umi-blocks/userlogin"
12 }, 12 },
13 "dependencies": { 13 "dependencies": {
14 - "react": "^16.6.3",  
15 - "dva": "^2.4.0",  
16 - "antd": "^3.10.9",  
17 "ant-design-pro": "^2.1.1", 14 "ant-design-pro": "^2.1.1",
18 - "qs": "^6.6.0",  
19 - "hash.js": "^1.1.5", 15 + "antd": "^3.10.9",
  16 + "dva": "^2.4.0",
20 "moment": "^2.22.2", 17 "moment": "^2.22.2",
21 - "nzh": "^1.0.3" 18 + "react": "^16.6.3",
  19 + "umi-request": "^1.0.0"
22 }, 20 },
23 "devDependencies": { 21 "devDependencies": {
24 "umi": "^2.3.0-beta.1", 22 "umi": "^2.3.0-beta.1",
25 - "umi-plugin-react": "^1.3.0-beta.1",  
26 - "umi-plugin-block-dev": "^1.0.0" 23 + "umi-plugin-block-dev": "^1.0.0",
  24 + "umi-plugin-react": "^1.3.0-beta.1"
27 }, 25 },
28 - "license": "ISC" 26 + "license": "MIT"
29 } 27 }
  1 +import React, { PureComponent } from 'react';
  2 +import { Dropdown } from 'antd';
  3 +import classNames from 'classnames';
  4 +import styles from './index.less';
  5 +
  6 +export default class HeaderDropdown extends PureComponent {
  7 + render() {
  8 + const { overlayClassName, ...props } = this.props;
  9 + return (
  10 + <Dropdown overlayClassName={classNames(styles.container, overlayClassName)} {...props} />
  11 + );
  12 + }
  13 +}
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.container > *:global(:not(.ant-dropdown-menu)) {
  4 + background-color: #fff;
  5 + box-shadow: @shadow-1-down;
  6 + border-radius: 4px;
  7 +}
  8 +
  9 +@media screen and (max-width: @screen-xs) {
  10 + .container {
  11 + width: 100% !important;
  12 + }
  13 + .container > * {
  14 + border-radius: 0 !important;
  15 + }
  16 +}
  1 +import React, { PureComponent } from 'react';
  2 +import { formatMessage, setLocale, getLocale } from 'umi/locale';
  3 +import { Menu, Icon } from 'antd';
  4 +import classNames from 'classnames';
  5 +import HeaderDropdown from '../HeaderDropdown';
  6 +import styles from './index.less';
  7 +
  8 +export default class SelectLang extends PureComponent {
  9 + changeLang = ({ key }) => {
  10 + setLocale(key);
  11 + };
  12 +
  13 + render() {
  14 + const { className } = this.props;
  15 + const selectedLang = getLocale();
  16 + const langMenu = (
  17 + <Menu className={styles.menu} selectedKeys={[selectedLang]} onClick={this.changeLang}>
  18 + <Menu.Item key="zh-CN">
  19 + <span role="img" aria-label="简体中文">
  20 + 🇨🇳
  21 + </span>{' '}
  22 + 简体中文
  23 + </Menu.Item>
  24 + <Menu.Item key="zh-TW">
  25 + <span role="img" aria-label="繁体中文">
  26 + 🇭🇰
  27 + </span>{' '}
  28 + 繁体中文
  29 + </Menu.Item>
  30 + <Menu.Item key="en-US">
  31 + <span role="img" aria-label="English">
  32 + 🇬🇧
  33 + </span>{' '}
  34 + English
  35 + </Menu.Item>
  36 + <Menu.Item key="pt-BR">
  37 + <span role="img" aria-label="Português">
  38 + 🇵🇹
  39 + </span>{' '}
  40 + Português
  41 + </Menu.Item>
  42 + </Menu>
  43 + );
  44 + return (
  45 + <HeaderDropdown overlay={langMenu} placement="bottomRight">
  46 + <span className={classNames(styles.dropDown, className)}>
  47 + <Icon type="global" title={formatMessage({ id: 'navBar.lang' })} />
  48 + </span>
  49 + </HeaderDropdown>
  50 + );
  51 + }
  52 +}
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.menu {
  4 + :global(.anticon) {
  5 + margin-right: 8px;
  6 + }
  7 + :global(.ant-dropdown-menu-item) {
  8 + min-width: 160px;
  9 + }
  10 +}
  11 +
  12 +.dropDown {
  13 + cursor: pointer;
  14 + vertical-align: top;
  15 + line-height: @layout-header-height;
  16 + > i {
  17 + font-size: 14px !important;
  18 + transform: none !important;
  19 + svg {
  20 + position: relative;
  21 + top: -1px;
  22 + }
  23 + }
  24 +}
  1 +import React, { Fragment } from 'react';
  2 +import { formatMessage } from 'umi/locale';
  3 +import Link from 'umi/link';
  4 +import { Icon } from 'antd';
  5 +import { GlobalFooter } from 'ant-design-pro';
  6 +import SelectLang from '../SelectLang';
  7 +import styles from './index.less';
  8 +import logo from './logo.svg';
  9 +
  10 +const links = [
  11 + {
  12 + key: 'help',
  13 + title: formatMessage({ id: 'layout.user.link.help' }),
  14 + href: '',
  15 + },
  16 + {
  17 + key: 'privacy',
  18 + title: formatMessage({ id: 'layout.user.link.privacy' }),
  19 + href: '',
  20 + },
  21 + {
  22 + key: 'terms',
  23 + title: formatMessage({ id: 'layout.user.link.terms' }),
  24 + href: '',
  25 + },
  26 +];
  27 +
  28 +const copyright = (
  29 + <Fragment>
  30 + Copyright <Icon type="copyright" /> 2018 蚂蚁金服体验技术部出品
  31 + </Fragment>
  32 +);
  33 +
  34 +class UserLayout extends React.PureComponent {
  35 + // @TODO title
  36 + // getPageTitle() {
  37 + // const { routerData, location } = this.props;
  38 + // const { pathname } = location;
  39 + // let title = 'Ant Design Pro';
  40 + // if (routerData[pathname] && routerData[pathname].name) {
  41 + // title = `${routerData[pathname].name} - Ant Design Pro`;
  42 + // }
  43 + // return title;
  44 + // }
  45 +
  46 + render() {
  47 + const { children } = this.props;
  48 + return (
  49 + // @TODO <DocumentTitle title={this.getPageTitle()}>
  50 + <div className={styles.container}>
  51 + <div className={styles.lang}>
  52 + <SelectLang />
  53 + </div>
  54 + <div className={styles.content}>
  55 + <div className={styles.top}>
  56 + <div className={styles.header}>
  57 + <Link to="/">
  58 + <img alt="logo" className={styles.logo} src={logo} />
  59 + <span className={styles.title}>Ant Design</span>
  60 + </Link>
  61 + </div>
  62 + <div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
  63 + </div>
  64 + {children}
  65 + </div>
  66 + <GlobalFooter links={links} copyright={copyright} />
  67 + </div>
  68 + );
  69 + }
  70 +}
  71 +
  72 +export default UserLayout;
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.container {
  4 + display: flex;
  5 + flex-direction: column;
  6 + height: 100vh;
  7 + overflow: auto;
  8 + background: @layout-body-background;
  9 +}
  10 +
  11 +.lang {
  12 + text-align: right;
  13 + width: 100%;
  14 + height: 40px;
  15 + line-height: 44px;
  16 + :global(.ant-dropdown-trigger) {
  17 + margin-right: 24px;
  18 + }
  19 +}
  20 +
  21 +.content {
  22 + padding: 32px 0;
  23 + flex: 1;
  24 +}
  25 +
  26 +@media (min-width: @screen-md-min) {
  27 + .container {
  28 + background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
  29 + background-repeat: no-repeat;
  30 + background-position: center 110px;
  31 + background-size: 100%;
  32 + }
  33 +
  34 + .content {
  35 + padding: 32px 0 24px 0;
  36 + }
  37 +}
  38 +
  39 +.top {
  40 + text-align: center;
  41 +}
  42 +
  43 +.header {
  44 + height: 44px;
  45 + line-height: 44px;
  46 + a {
  47 + text-decoration: none;
  48 + }
  49 +}
  50 +
  51 +.logo {
  52 + height: 44px;
  53 + vertical-align: top;
  54 + margin-right: 16px;
  55 +}
  56 +
  57 +.title {
  58 + font-size: 33px;
  59 + color: @heading-color;
  60 + font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
  61 + font-weight: 600;
  62 + position: relative;
  63 + top: 2px;
  64 +}
  65 +
  66 +.desc {
  67 + font-size: @font-size-base;
  68 + color: @text-color-secondary;
  69 + margin-top: 12px;
  70 + margin-bottom: 40px;
  71 +}
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  3 + <!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
  4 + <title>Group 28 Copy 5</title>
  5 + <desc>Created with Sketch.</desc>
  6 + <defs>
  7 + <linearGradient x1="62.1023273%" y1="0%" x2="108.19718%" y2="37.8635764%" id="linearGradient-1">
  8 + <stop stop-color="#4285EB" offset="0%"></stop>
  9 + <stop stop-color="#2EC7FF" offset="100%"></stop>
  10 + </linearGradient>
  11 + <linearGradient x1="69.644116%" y1="0%" x2="54.0428975%" y2="108.456714%" id="linearGradient-2">
  12 + <stop stop-color="#29CDFF" offset="0%"></stop>
  13 + <stop stop-color="#148EFF" offset="37.8600687%"></stop>
  14 + <stop stop-color="#0A60FF" offset="100%"></stop>
  15 + </linearGradient>
  16 + <linearGradient x1="69.6908165%" y1="-12.9743587%" x2="16.7228981%" y2="117.391248%" id="linearGradient-3">
  17 + <stop stop-color="#FA816E" offset="0%"></stop>
  18 + <stop stop-color="#F74A5C" offset="41.472606%"></stop>
  19 + <stop stop-color="#F51D2C" offset="100%"></stop>
  20 + </linearGradient>
  21 + <linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-4">
  22 + <stop stop-color="#FA8E7D" offset="0%"></stop>
  23 + <stop stop-color="#F74A5C" offset="51.2635191%"></stop>
  24 + <stop stop-color="#F51D2C" offset="100%"></stop>
  25 + </linearGradient>
  26 + </defs>
  27 + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  28 + <g id="logo" transform="translate(-20.000000, -20.000000)">
  29 + <g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)">
  30 + <g id="Group-27-Copy-3">
  31 + <g id="Group-25" fill-rule="nonzero">
  32 + <g id="2">
  33 + <path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-1)"></path>
  34 + <path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-2)"></path>
  35 + </g>
  36 + <path d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z" id="Shape" fill="url(#linearGradient-3)"></path>
  37 + </g>
  38 + <ellipse id="Combined-Shape" fill="url(#linearGradient-4)" cx="100.519339" cy="100.436681" rx="23.6001926" ry="23.580786"></ellipse>
  39 + </g>
  40 + </g>
  41 + </g>
  42 + </g>
  43 +</svg>
1 -module.exports = {  
2 - navTheme: 'dark', // theme for nav menu  
3 - primaryColor: '#1890FF', // primary color of ant design  
4 - layout: 'sidemenu', // nav menu position: sidemenu or topmenu  
5 - contentWidth: 'Fluid', // layout of content: Fluid or Fixed, only works when layout is topmenu  
6 - fixedHeader: false, // sticky header  
7 - autoHideHeader: false, // auto hide header  
8 - fixSiderbar: false, // sticky siderbar  
9 -};  
@@ -3,8 +3,9 @@ import { connect } from 'dva'; @@ -3,8 +3,9 @@ import { connect } from 'dva';
3 import { formatMessage, FormattedMessage } from 'umi/locale'; 3 import { formatMessage, FormattedMessage } from 'umi/locale';
4 import Link from 'umi/link'; 4 import Link from 'umi/link';
5 import { Checkbox, Alert, Icon } from 'antd'; 5 import { Checkbox, Alert, Icon } from 'antd';
6 -import Login from 'ant-design-pro/lib/Login';  
7 -import styles from './Login.less'; 6 +import { Login } from 'ant-design-pro';
  7 +import UserLayout from './components/UserLayout';
  8 +import styles from './style.less';
8 9
9 const { Tab, UserName, Password, Mobile, Captcha, Submit } = Login; 10 const { Tab, UserName, Password, Mobile, Captcha, Submit } = Login;
10 11
@@ -67,6 +68,7 @@ class LoginPage extends Component { @@ -67,6 +68,7 @@ class LoginPage extends Component {
67 const { login, submitting } = this.props; 68 const { login, submitting } = this.props;
68 const { type, autoLogin } = this.state; 69 const { type, autoLogin } = this.state;
69 return ( 70 return (
  71 + <UserLayout>
70 <div className={styles.main}> 72 <div className={styles.main}>
71 <Login 73 <Login
72 defaultActiveKey={type} 74 defaultActiveKey={type}
@@ -161,6 +163,7 @@ class LoginPage extends Component { @@ -161,6 +163,7 @@ class LoginPage extends Component {
161 </div> 163 </div>
162 </Login> 164 </Login>
163 </div> 165 </div>
  166 + </UserLayout>
164 ); 167 );
165 } 168 }
166 } 169 }
@@ -36,4 +36,41 @@ export default { @@ -36,4 +36,41 @@ export default {
36 'validation.date.required': 'Please select the start and end date', 36 'validation.date.required': 'Please select the start and end date',
37 'validation.goal.required': 'Please enter a description of the goal', 37 'validation.goal.required': 'Please enter a description of the goal',
38 'validation.standard.required': 'Please enter a metric', 38 'validation.standard.required': 'Please enter a metric',
  39 + 'form.get-captcha': 'Get Captcha',
  40 + 'form.captcha.second': 'sec',
  41 + 'form.optional': ' (optional) ',
  42 + 'form.submit': 'Submit',
  43 + 'form.save': 'Save',
  44 + 'form.email.placeholder': 'Email',
  45 + 'form.password.placeholder': 'Password',
  46 + 'form.confirm-password.placeholder': 'Confirm password',
  47 + 'form.phone-number.placeholder': 'Phone number',
  48 + 'form.verification-code.placeholder': 'Verification code',
  49 + 'form.title.label': 'Title',
  50 + 'form.title.placeholder': 'Give the target a name',
  51 + 'form.date.label': 'Start and end date',
  52 + 'form.date.placeholder.start': 'Start date',
  53 + 'form.date.placeholder.end': 'End date',
  54 + 'form.goal.label': 'Goal description',
  55 + 'form.goal.placeholder': 'Please enter your work goals',
  56 + 'form.standard.label': 'Metrics',
  57 + 'form.standard.placeholder': 'Please enter a metric',
  58 + 'form.client.label': 'Client',
  59 + 'form.client.label.tooltip': 'Target service object',
  60 + 'form.client.placeholder':
  61 + 'Please describe your customer service, internal customers directly @ Name / job number',
  62 + 'form.invites.label': 'Inviting critics',
  63 + 'form.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
  64 + 'form.weight.label': 'Weight',
  65 + 'form.weight.placeholder': 'Please enter weight',
  66 + 'form.public.label': 'Target disclosure',
  67 + 'form.public.label.help': 'Customers and invitees are shared by default',
  68 + 'form.public.radio.public': 'Public',
  69 + 'form.public.radio.partially-public': 'Partially public',
  70 + 'form.public.radio.private': 'Private',
  71 + 'form.publicUsers.placeholder': 'Open to',
  72 + 'form.publicUsers.option.A': 'Colleague A',
  73 + 'form.publicUsers.option.B': 'Colleague B',
  74 + 'form.publicUsers.option.C': 'Colleague C',
  75 + 'navBar.lang': 'Languages',
39 }; 76 };
@@ -35,4 +35,40 @@ export default { @@ -35,4 +35,40 @@ export default {
35 'validation.date.required': '请选择起止日期', 35 'validation.date.required': '请选择起止日期',
36 'validation.goal.required': '请输入目标描述', 36 'validation.goal.required': '请输入目标描述',
37 'validation.standard.required': '请输入衡量标准', 37 'validation.standard.required': '请输入衡量标准',
  38 + 'form.get-captcha': '获取验证码',
  39 + 'form.captcha.second': '秒',
  40 + 'form.optional': '(选填)',
  41 + 'form.submit': '提交',
  42 + 'form.save': '保存',
  43 + 'form.email.placeholder': '邮箱',
  44 + 'form.password.placeholder': '至少6位密码,区分大小写',
  45 + 'form.confirm-password.placeholder': '确认密码',
  46 + 'form.phone-number.placeholder': '手机号',
  47 + 'form.verification-code.placeholder': '验证码',
  48 + 'form.title.label': '标题',
  49 + 'form.title.placeholder': '给目标起个名字',
  50 + 'form.date.label': '起止日期',
  51 + 'form.date.placeholder.start': '开始日期',
  52 + 'form.date.placeholder.end': '结束日期',
  53 + 'form.goal.label': '目标描述',
  54 + 'form.goal.placeholder': '请输入你的阶段性工作目标',
  55 + 'form.standard.label': '衡量标准',
  56 + 'form.standard.placeholder': '请输入衡量标准',
  57 + 'form.client.label': '客户',
  58 + 'form.client.label.tooltip': '目标的服务对象',
  59 + 'form.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
  60 + 'form.invites.label': '邀评人',
  61 + 'form.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
  62 + 'form.weight.label': '权重',
  63 + 'form.weight.placeholder': '请输入',
  64 + 'form.public.label': '目标公开',
  65 + 'form.public.label.help': '客户、邀评人默认被分享',
  66 + 'form.public.radio.public': '公开',
  67 + 'form.public.radio.partially-public': '部分公开',
  68 + 'form.public.radio.private': '不公开',
  69 + 'form.publicUsers.placeholder': '公开给',
  70 + 'form.publicUsers.option.A': '同事甲',
  71 + 'form.publicUsers.option.B': '同事乙',
  72 + 'form.publicUsers.option.C': '同事丙',
  73 + 'navBar.lang': '语言',
38 }; 74 };
@@ -35,4 +35,40 @@ export default { @@ -35,4 +35,40 @@ export default {
35 'validation.date.required': '請選擇起止日期', 35 'validation.date.required': '請選擇起止日期',
36 'validation.goal.required': '請輸入目標描述', 36 'validation.goal.required': '請輸入目標描述',
37 'validation.standard.required': '請輸入衡量標淮', 37 'validation.standard.required': '請輸入衡量標淮',
  38 + 'form.get-captcha': '獲取驗證碼',
  39 + 'form.captcha.second': '秒',
  40 + 'form.optional': '(選填)',
  41 + 'form.submit': '提交',
  42 + 'form.save': '保存',
  43 + 'form.email.placeholder': '郵箱',
  44 + 'form.password.placeholder': '至少6位密碼,區分大小寫',
  45 + 'form.confirm-password.placeholder': '確認密碼',
  46 + 'form.phone-number.placeholder': '手機號',
  47 + 'form.verification-code.placeholder': '驗證碼',
  48 + 'form.title.label': '標題',
  49 + 'form.title.placeholder': '給目標起個名字',
  50 + 'form.date.label': '起止日期',
  51 + 'form.date.placeholder.start': '開始日期',
  52 + 'form.date.placeholder.end': '結束日期',
  53 + 'form.goal.label': '目標描述',
  54 + 'form.goal.placeholder': '請輸入妳的階段性工作目標',
  55 + 'form.standard.label': '衡量標淮',
  56 + 'form.standard.placeholder': '請輸入衡量標淮',
  57 + 'form.client.label': '客戶',
  58 + 'form.client.label.tooltip': '目標的服務對象',
  59 + 'form.client.placeholder': '請描述妳服務的客戶,內部客戶直接 @姓名/工號',
  60 + 'form.invites.label': '邀評人',
  61 + 'form.invites.placeholder': '請直接 @姓名/工號,最多可邀請 5 人',
  62 + 'form.weight.label': '權重',
  63 + 'form.weight.placeholder': '請輸入',
  64 + 'form.public.label': '目標公開',
  65 + 'form.public.label.help': '客戶、邀評人默認被分享',
  66 + 'form.public.radio.public': '公開',
  67 + 'form.public.radio.partially-public': '部分公開',
  68 + 'form.public.radio.private': '不公開',
  69 + 'form.publicUsers.placeholder': '公開給',
  70 + 'form.publicUsers.option.A': '同事甲',
  71 + 'form.publicUsers.option.B': '同事乙',
  72 + 'form.publicUsers.option.C': '同事丙',
  73 + 'navBar.lang': '語言',
38 }; 74 };
1 import { routerRedux } from 'dva/router'; 1 import { routerRedux } from 'dva/router';
2 import { stringify } from 'qs'; 2 import { stringify } from 'qs';
3 -import { fakeAccountLogin, getFakeCaptcha } from '@/services/api';  
4 -import { setAuthority } from '@/utils/authority';  
5 -import { getPageQuery } from '@/utils/utils';  
6 -import { reloadAuthorized } from '@/utils/Authorized'; 3 +import { getPageQuery } from './utils/utils';
  4 +import { setAuthority } from './utils/authority';
  5 +import { reloadAuthorized } from './utils/Authorized';
  6 +import { fakeAccountLogin, getFakeCaptcha } from './service';
7 7
8 export default { 8 export default {
9 namespace: 'login', 9 namespace: 'login',
1 -import { message } from 'antd';  
2 -import defaultSettings from '../defaultSettings';  
3 -  
4 -let lessNodesAppended;  
5 -const updateTheme = primaryColor => {  
6 - // Don't compile less in production!  
7 - if (APP_TYPE !== 'site') {  
8 - return;  
9 - }  
10 - // Determine if the component is remounted  
11 - if (!primaryColor) {  
12 - return;  
13 - }  
14 - const hideMessage = message.loading('正在编译主题!', 0);  
15 - function buildIt() {  
16 - if (!window.less) {  
17 - return;  
18 - }  
19 - setTimeout(() => {  
20 - window.less  
21 - .modifyVars({  
22 - '@primary-color': primaryColor,  
23 - })  
24 - .then(() => {  
25 - hideMessage();  
26 - })  
27 - .catch(() => {  
28 - message.error('Failed to update theme');  
29 - hideMessage();  
30 - });  
31 - }, 200);  
32 - }  
33 - if (!lessNodesAppended) {  
34 - // insert less.js and color.less  
35 - const lessStyleNode = document.createElement('link');  
36 - const lessConfigNode = document.createElement('script');  
37 - const lessScriptNode = document.createElement('script');  
38 - lessStyleNode.setAttribute('rel', 'stylesheet/less');  
39 - lessStyleNode.setAttribute('href', '/color.less');  
40 - lessConfigNode.innerHTML = `  
41 - window.less = {  
42 - async: true,  
43 - env: 'production',  
44 - javascriptEnabled: true  
45 - };  
46 - `;  
47 - lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js';  
48 - lessScriptNode.async = true;  
49 - lessScriptNode.onload = () => {  
50 - buildIt();  
51 - lessScriptNode.onload = null;  
52 - };  
53 - document.body.appendChild(lessStyleNode);  
54 - document.body.appendChild(lessConfigNode);  
55 - document.body.appendChild(lessScriptNode);  
56 - lessNodesAppended = true;  
57 - } else {  
58 - buildIt();  
59 - }  
60 -};  
61 -  
62 -const updateColorWeak = colorWeak => {  
63 - document.body.className = colorWeak ? 'colorWeak' : '';  
64 -};  
65 -  
66 -export default {  
67 - namespace: 'setting',  
68 - state: defaultSettings,  
69 - reducers: {  
70 - getSetting(state) {  
71 - const setting = {};  
72 - const urlParams = new URL(window.location.href);  
73 - Object.keys(state).forEach(key => {  
74 - if (urlParams.searchParams.has(key)) {  
75 - const value = urlParams.searchParams.get(key);  
76 - setting[key] = value === '1' ? true : value;  
77 - }  
78 - });  
79 - const { primaryColor, colorWeak } = setting;  
80 - if (state.primaryColor !== primaryColor) {  
81 - updateTheme(primaryColor);  
82 - }  
83 - updateColorWeak(colorWeak);  
84 - return {  
85 - ...state,  
86 - ...setting,  
87 - };  
88 - },  
89 - changeSetting(state, { payload }) {  
90 - const urlParams = new URL(window.location.href);  
91 - Object.keys(defaultSettings).forEach(key => {  
92 - if (urlParams.searchParams.has(key)) {  
93 - urlParams.searchParams.delete(key);  
94 - }  
95 - });  
96 - Object.keys(payload).forEach(key => {  
97 - if (key === 'collapse') {  
98 - return;  
99 - }  
100 - let value = payload[key];  
101 - if (value === true) {  
102 - value = 1;  
103 - }  
104 - if (defaultSettings[key] !== value) {  
105 - urlParams.searchParams.set(key, value);  
106 - }  
107 - });  
108 - const { primaryColor, colorWeak, contentWidth } = payload;  
109 - if (state.primaryColor !== primaryColor) {  
110 - updateTheme(primaryColor);  
111 - }  
112 - if (state.contentWidth !== contentWidth && window.dispatchEvent) {  
113 - window.dispatchEvent(new Event('resize'));  
114 - }  
115 - updateColorWeak(colorWeak);  
116 - window.history.replaceState(null, 'setting', urlParams.href);  
117 - return {  
118 - ...state,  
119 - ...payload,  
120 - };  
121 - },  
122 - },  
123 -};  
  1 +import request from 'umi-request';
  2 +
  3 +export async function fakeAccountLogin(params) {
  4 + return request('/api/login/account', {
  5 + method: 'POST',
  6 + body: params,
  7 + });
  8 +}
  9 +
  10 +export async function getFakeCaptcha(mobile) {
  11 + return request(`/api/captcha?mobile=${mobile}`);
  12 +}
@@ -29,4 +29,11 @@ @@ -29,4 +29,11 @@
29 float: right; 29 float: right;
30 } 30 }
31 } 31 }
  32 +
  33 + :global {
  34 + .antd-pro-login-submit {
  35 + width: 100%;
  36 + margin-top: 24px;
  37 + }
  38 + }
32 } 39 }
  1 +
  2 +export function getPageQuery() {
  3 + return parse(window.location.href.split('?')[1]);
  4 +}
  5 +
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 updateRule(params) {  
37 - return request('/api/rule', {  
38 - method: 'POST',  
39 - body: {  
40 - ...params,  
41 - method: 'update',  
42 - },  
43 - });  
44 -}  
45 -  
46 -export async function fakeSubmitForm(params) {  
47 - return request('/api/forms', {  
48 - method: 'POST',  
49 - body: params,  
50 - });  
51 -}  
52 -  
53 -export async function fakeChartData() {  
54 - return request('/api/fake_chart_data');  
55 -}  
56 -  
57 -export async function queryTags() {  
58 - return request('/api/tags');  
59 -}  
60 -  
61 -export async function queryBasicProfile() {  
62 - return request('/api/profile/basic');  
63 -}  
64 -  
65 -export async function queryAdvancedProfile() {  
66 - return request('/api/profile/advanced');  
67 -}  
68 -  
69 -export async function queryFakeList(params) {  
70 - return request(`/api/fake_list?${stringify(params)}`);  
71 -}  
72 -  
73 -export async function removeFakeList(params) {  
74 - const { count = 5, ...restParams } = params;  
75 - return request(`/api/fake_list?count=${count}`, {  
76 - method: 'POST',  
77 - body: {  
78 - ...restParams,  
79 - method: 'delete',  
80 - },  
81 - });  
82 -}  
83 -  
84 -export async function addFakeList(params) {  
85 - const { count = 5, ...restParams } = params;  
86 - return request(`/api/fake_list?count=${count}`, {  
87 - method: 'POST',  
88 - body: {  
89 - ...restParams,  
90 - method: 'post',  
91 - },  
92 - });  
93 -}  
94 -  
95 -export async function updateFakeList(params) {  
96 - const { count = 5, ...restParams } = params;  
97 - return request(`/api/fake_list?count=${count}`, {  
98 - method: 'POST',  
99 - body: {  
100 - ...restParams,  
101 - method: 'update',  
102 - },  
103 - });  
104 -}  
105 -  
106 -export async function fakeAccountLogin(params) {  
107 - return request('/api/login/account', {  
108 - method: 'POST',  
109 - body: params,  
110 - });  
111 -}  
112 -  
113 -export async function fakeRegister(params) {  
114 - return request('/api/register', {  
115 - method: 'POST',  
116 - body: params,  
117 - });  
118 -}  
119 -  
120 -export async function queryNotices() {  
121 - return request('/api/notices');  
122 -}  
123 -  
124 -export async function getFakeCaptcha(mobile) {  
125 - return request(`/api/captcha?mobile=${mobile}`);  
126 -}  
1 -import fetch from 'dva/fetch';  
2 -import { notification } from 'antd';  
3 -import router from 'umi/router';  
4 -import hash from 'hash.js';  
5 -import { isAntdPro } from './utils';  
6 -  
7 -const codeMessage = {  
8 - 200: '服务器成功返回请求的数据。',  
9 - 201: '新建或修改数据成功。',  
10 - 202: '一个请求已经进入后台排队(异步任务)。',  
11 - 204: '删除数据成功。',  
12 - 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',  
13 - 401: '用户没有权限(令牌、用户名、密码错误)。',  
14 - 403: '用户得到授权,但是访问是被禁止的。',  
15 - 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',  
16 - 406: '请求的格式不可得。',  
17 - 410: '请求的资源被永久删除,且不会再得到的。',  
18 - 422: '当创建一个对象时,发生一个验证错误。',  
19 - 500: '服务器发生错误,请检查服务器。',  
20 - 502: '网关错误。',  
21 - 503: '服务不可用,服务器暂时过载或维护。',  
22 - 504: '网关超时。',  
23 -};  
24 -  
25 -const checkStatus = response => {  
26 - if (response.status >= 200 && response.status < 300) {  
27 - return response;  
28 - }  
29 - const errortext = codeMessage[response.status] || response.statusText;  
30 - notification.error({  
31 - message: `请求错误 ${response.status}: ${response.url}`,  
32 - description: errortext,  
33 - });  
34 - const error = new Error(errortext);  
35 - error.name = response.status;  
36 - error.response = response;  
37 - throw error;  
38 -};  
39 -  
40 -const cachedSave = (response, hashcode) => {  
41 - /**  
42 - * Clone a response data and store it in sessionStorage  
43 - * Does not support data other than json, Cache only json  
44 - */  
45 - const contentType = response.headers.get('Content-Type');  
46 - if (contentType && contentType.match(/application\/json/i)) {  
47 - // All data is saved as text  
48 - response  
49 - .clone()  
50 - .text()  
51 - .then(content => {  
52 - sessionStorage.setItem(hashcode, content);  
53 - sessionStorage.setItem(`${hashcode}:timestamp`, Date.now());  
54 - });  
55 - }  
56 - return response;  
57 -};  
58 -  
59 -/**  
60 - * Requests a URL, returning a promise.  
61 - *  
62 - * @param {string} url The URL we want to request  
63 - * @param {object} [option] The options we want to pass to "fetch"  
64 - * @return {object} An object containing either "data" or "err"  
65 - */  
66 -export default function request(url, option) {  
67 - const options = {  
68 - expirys: isAntdPro(),  
69 - ...option,  
70 - };  
71 - /**  
72 - * Produce fingerprints based on url and parameters  
73 - * Maybe url has the same parameters  
74 - */  
75 - const fingerprint = url + (options.body ? JSON.stringify(options.body) : '');  
76 - const hashcode = hash  
77 - .sha256()  
78 - .update(fingerprint)  
79 - .digest('hex');  
80 -  
81 - const defaultOptions = {  
82 - credentials: 'include',  
83 - };  
84 - const newOptions = { ...defaultOptions, ...options };  
85 - if (  
86 - newOptions.method === 'POST' ||  
87 - newOptions.method === 'PUT' ||  
88 - newOptions.method === 'DELETE'  
89 - ) {  
90 - if (!(newOptions.body instanceof FormData)) {  
91 - newOptions.headers = {  
92 - Accept: 'application/json',  
93 - 'Content-Type': 'application/json; charset=utf-8',  
94 - ...newOptions.headers,  
95 - };  
96 - newOptions.body = JSON.stringify(newOptions.body);  
97 - } else {  
98 - // newOptions.body is FormData  
99 - newOptions.headers = {  
100 - Accept: 'application/json',  
101 - ...newOptions.headers,  
102 - };  
103 - }  
104 - }  
105 -  
106 - const expirys = options.expirys && 60;  
107 - // options.expirys !== false, return the cache,  
108 - if (options.expirys !== false) {  
109 - const cached = sessionStorage.getItem(hashcode);  
110 - const whenCached = sessionStorage.getItem(`${hashcode}:timestamp`);  
111 - if (cached !== null && whenCached !== null) {  
112 - const age = (Date.now() - whenCached) / 1000;  
113 - if (age < expirys) {  
114 - const response = new Response(new Blob([cached]));  
115 - return response.json();  
116 - }  
117 - sessionStorage.removeItem(hashcode);  
118 - sessionStorage.removeItem(`${hashcode}:timestamp`);  
119 - }  
120 - }  
121 - return fetch(url, newOptions)  
122 - .then(checkStatus)  
123 - .then(response => cachedSave(response, hashcode))  
124 - .then(response => {  
125 - // DELETE and 204 do not return data by default  
126 - // using .json will report an error.  
127 - if (newOptions.method === 'DELETE' || response.status === 204) {  
128 - return response.text();  
129 - }  
130 - return response.json();  
131 - })  
132 - .catch(e => {  
133 - const status = e.name;  
134 - if (status === 401) {  
135 - // @HACK  
136 - /* eslint-disable no-underscore-dangle */  
137 - window.g_app._store.dispatch({  
138 - type: 'login/logout',  
139 - });  
140 - return;  
141 - }  
142 - // environment should not be used  
143 - if (status === 403) {  
144 - router.push('/exception/403');  
145 - return;  
146 - }  
147 - if (status <= 504 && status >= 500) {  
148 - router.push('/exception/500');  
149 - return;  
150 - }  
151 - if (status >= 404 && status < 422) {  
152 - router.push('/exception/404');  
153 - }  
154 - });  
155 -}  
1 -import moment from 'moment';  
2 -import React from 'react';  
3 -import nzh from 'nzh/cn';  
4 -import { parse, stringify } from 'qs';  
5 -  
6 -export function fixedZero(val) {  
7 - return val * 1 < 10 ? `0${val}` : val;  
8 -}  
9 -  
10 -export function getTimeDistance(type) {  
11 - const now = new Date();  
12 - const oneDay = 1000 * 60 * 60 * 24;  
13 -  
14 - if (type === 'today') {  
15 - now.setHours(0);  
16 - now.setMinutes(0);  
17 - now.setSeconds(0);  
18 - return [moment(now), moment(now.getTime() + (oneDay - 1000))];  
19 - }  
20 -  
21 - if (type === 'week') {  
22 - let day = now.getDay();  
23 - now.setHours(0);  
24 - now.setMinutes(0);  
25 - now.setSeconds(0);  
26 -  
27 - if (day === 0) {  
28 - day = 6;  
29 - } else {  
30 - day -= 1;  
31 - }  
32 -  
33 - const beginTime = now.getTime() - day * oneDay;  
34 -  
35 - return [moment(beginTime), moment(beginTime + (7 * oneDay - 1000))];  
36 - }  
37 -  
38 - if (type === 'month') {  
39 - const year = now.getFullYear();  
40 - const month = now.getMonth();  
41 - const nextDate = moment(now).add(1, 'months');  
42 - const nextYear = nextDate.year();  
43 - const nextMonth = nextDate.month();  
44 -  
45 - return [  
46 - moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`),  
47 - moment(moment(`${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`).valueOf() - 1000),  
48 - ];  
49 - }  
50 -  
51 - const year = now.getFullYear();  
52 - return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)];  
53 -}  
54 -  
55 -export function getPlainNode(nodeList, parentPath = '') {  
56 - const arr = [];  
57 - nodeList.forEach(node => {  
58 - const item = node;  
59 - item.path = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/');  
60 - item.exact = true;  
61 - if (item.children && !item.component) {  
62 - arr.push(...getPlainNode(item.children, item.path));  
63 - } else {  
64 - if (item.children && item.component) {  
65 - item.exact = false;  
66 - }  
67 - arr.push(item);  
68 - }  
69 - });  
70 - return arr;  
71 -}  
72 -  
73 -export function digitUppercase(n) {  
74 - return nzh.toMoney(n);  
75 -}  
76 -  
77 -function getRelation(str1, str2) {  
78 - if (str1 === str2) {  
79 - console.warn('Two path are equal!'); // eslint-disable-line  
80 - }  
81 - const arr1 = str1.split('/');  
82 - const arr2 = str2.split('/');  
83 - if (arr2.every((item, index) => item === arr1[index])) {  
84 - return 1;  
85 - }  
86 - if (arr1.every((item, index) => item === arr2[index])) {  
87 - return 2;  
88 - }  
89 - return 3;  
90 -}  
91 -  
92 -function getRenderArr(routes) {  
93 - let renderArr = [];  
94 - renderArr.push(routes[0]);  
95 - for (let i = 1; i < routes.length; i += 1) {  
96 - // 去重  
97 - renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1);  
98 - // 是否包含  
99 - const isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);  
100 - if (isAdd) {  
101 - renderArr.push(routes[i]);  
102 - }  
103 - }  
104 - return renderArr;  
105 -}  
106 -  
107 -/**  
108 - * Get router routing configuration  
109 - * { path:{name,...param}}=>Array<{name,path ...param}>  
110 - * @param {string} path  
111 - * @param {routerData} routerData  
112 - */  
113 -export function getRoutes(path, routerData) {  
114 - let routes = Object.keys(routerData).filter(  
115 - routePath => routePath.indexOf(path) === 0 && routePath !== path  
116 - );  
117 - // Replace path to '' eg. path='user' /user/name => name  
118 - routes = routes.map(item => item.replace(path, ''));  
119 - // Get the route to be rendered to remove the deep rendering  
120 - const renderArr = getRenderArr(routes);  
121 - // Conversion and stitching parameters  
122 - const renderRoutes = renderArr.map(item => {  
123 - const exact = !routes.some(route => route !== item && getRelation(route, item) === 1);  
124 - return {  
125 - exact,  
126 - ...routerData[`${path}${item}`],  
127 - key: `${path}${item}`,  
128 - path: `${path}${item}`,  
129 - };  
130 - });  
131 - return renderRoutes;  
132 -}  
133 -  
134 -export function getPageQuery() {  
135 - return parse(window.location.href.split('?')[1]);  
136 -}  
137 -  
138 -export function getQueryPath(path = '', query = {}) {  
139 - const search = stringify(query);  
140 - if (search.length) {  
141 - return `${path}?${search}`;  
142 - }  
143 - return path;  
144 -}  
145 -  
146 -/* eslint no-useless-escape:0 */  
147 -const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;  
148 -  
149 -export function isUrl(path) {  
150 - return reg.test(path);  
151 -}  
152 -  
153 -export function formatWan(val) {  
154 - const v = val * 1;  
155 - if (!v || Number.isNaN(v)) return '';  
156 -  
157 - let result = val;  
158 - if (val > 10000) {  
159 - result = Math.floor(val / 10000);  
160 - result = (  
161 - <span>  
162 - {result}  
163 - <span  
164 - style={{  
165 - position: 'relative',  
166 - top: -2,  
167 - fontSize: 14,  
168 - fontStyle: 'normal',  
169 - marginLeft: 2,  
170 - }}  
171 - >  
172 -  
173 - </span>  
174 - </span>  
175 - );  
176 - }  
177 - return result;  
178 -}  
179 -  
180 -// 给官方演示站点用,用于关闭真实开发环境不需要使用的特性  
181 -export function isAntdPro() {  
182 - return window.location.hostname === 'preview.pro.ant.design';  
183 -}  
@@ -11,19 +11,20 @@ @@ -11,19 +11,20 @@
11 "url": "https://github.com/umijs/umi-blocks/userregister" 11 "url": "https://github.com/umijs/umi-blocks/userregister"
12 }, 12 },
13 "dependencies": { 13 "dependencies": {
14 - "react": "^16.6.3",  
15 - "dva": "^2.4.0", 14 + "ant-design-pro": "^2.1.1",
16 "antd": "^3.10.9", 15 "antd": "^3.10.9",
17 - "qs": "^6.6.0", 16 + "dva": "^2.4.0",
18 "hash.js": "^1.1.5", 17 "hash.js": "^1.1.5",
19 "moment": "^2.22.2", 18 "moment": "^2.22.2",
20 "nzh": "^1.0.3", 19 "nzh": "^1.0.3",
21 - "ant-design-pro": "^2.1.1" 20 + "qs": "^6.6.0",
  21 + "react": "^16.6.3",
  22 + "umi-request": "^1.0.0"
22 }, 23 },
23 "devDependencies": { 24 "devDependencies": {
24 "umi": "^2.3.0-beta.1", 25 "umi": "^2.3.0-beta.1",
25 "umi-plugin-react": "^1.3.0-beta.1", 26 "umi-plugin-react": "^1.3.0-beta.1",
26 "umi-plugin-block-dev": "^1.0.0" 27 "umi-plugin-block-dev": "^1.0.0"
27 }, 28 },
28 - "license": "ISC" 29 + "license": "MIT"
29 } 30 }
1 -// 代码中会兼容本地 service mock 以及部署站点的静态数据 1 +
2 export default { 2 export default {
3 - // 支持值为 Object 和 Array  
4 - 'GET /api/currentUser': {  
5 - name: 'Serati Ma',  
6 - avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',  
7 - userid: '00000001',  
8 - email: 'antdesign@alipay.com',  
9 - signature: '海纳百川,有容乃大',  
10 - title: '交互专家',  
11 - group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',  
12 - tags: [  
13 - {  
14 - key: '0',  
15 - label: '很有想法的',  
16 - },  
17 - {  
18 - key: '1',  
19 - label: '专注设计',  
20 - },  
21 - {  
22 - key: '2',  
23 - label: '辣~',  
24 - },  
25 - {  
26 - key: '3',  
27 - label: '大长腿',  
28 - },  
29 - {  
30 - key: '4',  
31 - label: '川妹子',  
32 - },  
33 - {  
34 - key: '5',  
35 - label: '海纳百川',  
36 - },  
37 - ],  
38 - notifyCount: 12,  
39 - unreadCount: 11,  
40 - country: 'China',  
41 - geographic: {  
42 - province: {  
43 - label: '浙江省',  
44 - key: '330000',  
45 - },  
46 - city: {  
47 - label: '杭州市',  
48 - key: '330100',  
49 - },  
50 - },  
51 - address: '西湖区工专路 77 号',  
52 - phone: '0752-268888888',  
53 - },  
54 - // GET POST 可省略  
55 - 'GET /api/users': [  
56 - {  
57 - key: '1',  
58 - name: 'John Brown',  
59 - age: 32,  
60 - address: 'New York No. 1 Lake Park',  
61 - },  
62 - {  
63 - key: '2',  
64 - name: 'Jim Green',  
65 - age: 42,  
66 - address: 'London No. 1 Lake Park',  
67 - },  
68 - {  
69 - key: '3',  
70 - name: 'Joe Black',  
71 - age: 32,  
72 - address: 'Sidney No. 1 Lake Park',  
73 - },  
74 - ],  
75 - 'POST /api/login/account': (req, res) => {  
76 - const { password, userName, type } = req.body;  
77 - if (password === 'ant.design' && userName === 'admin') {  
78 - res.send({  
79 - status: 'ok',  
80 - type,  
81 - currentAuthority: 'admin',  
82 - });  
83 - return;  
84 - }  
85 - if (password === 'ant.design' && userName === 'user') {  
86 - res.send({  
87 - status: 'ok',  
88 - type,  
89 - currentAuthority: 'user',  
90 - });  
91 - return;  
92 - }  
93 - res.send({  
94 - status: 'error',  
95 - type,  
96 - currentAuthority: 'guest',  
97 - });  
98 - },  
99 'POST /api/register': (req, res) => { 3 'POST /api/register': (req, res) => {
100 res.send({ status: 'ok', currentAuthority: 'user' }); 4 res.send({ status: 'ok', currentAuthority: 'user' });
101 }, 5 },
102 - 'GET /api/500': (req, res) => {  
103 - res.status(500).send({  
104 - timestamp: 1513932555104,  
105 - status: 500,  
106 - error: 'error',  
107 - message: 'error',  
108 - path: '/base/category/list',  
109 - });  
110 - },  
111 - 'GET /api/404': (req, res) => {  
112 - res.status(404).send({  
113 - timestamp: 1513932643431,  
114 - status: 404,  
115 - error: 'Not Found',  
116 - message: 'No message available',  
117 - path: '/base/category/list/2121212',  
118 - });  
119 - },  
120 - 'GET /api/403': (req, res) => {  
121 - res.status(403).send({  
122 - timestamp: 1513932555104,  
123 - status: 403,  
124 - error: 'Unauthorized',  
125 - message: 'Unauthorized',  
126 - path: '/base/category/list',  
127 - });  
128 - },  
129 - 'GET /api/401': (req, res) => {  
130 - res.status(401).send({  
131 - timestamp: 1513932555104,  
132 - status: 401,  
133 - error: 'Unauthorized',  
134 - message: 'Unauthorized',  
135 - path: '/base/category/list',  
136 - });  
137 - },  
138 }; 6 };
  1 +import React, { PureComponent } from 'react';
  2 +import { Dropdown } from 'antd';
  3 +import classNames from 'classnames';
  4 +import styles from './index.less';
  5 +
  6 +export default class HeaderDropdown extends PureComponent {
  7 + render() {
  8 + const { overlayClassName, ...props } = this.props;
  9 + return (
  10 + <Dropdown overlayClassName={classNames(styles.container, overlayClassName)} {...props} />
  11 + );
  12 + }
  13 +}
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.container > *:global(:not(.ant-dropdown-menu)) {
  4 + background-color: #fff;
  5 + box-shadow: @shadow-1-down;
  6 + border-radius: 4px;
  7 +}
  8 +
  9 +@media screen and (max-width: @screen-xs) {
  10 + .container {
  11 + width: 100% !important;
  12 + }
  13 + .container > * {
  14 + border-radius: 0 !important;
  15 + }
  16 +}
  1 +import React, { PureComponent } from 'react';
  2 +import { formatMessage, setLocale, getLocale } from 'umi/locale';
  3 +import { Menu, Icon } from 'antd';
  4 +import classNames from 'classnames';
  5 +import HeaderDropdown from '../HeaderDropdown';
  6 +import styles from './index.less';
  7 +
  8 +export default class SelectLang extends PureComponent {
  9 + changeLang = ({ key }) => {
  10 + setLocale(key);
  11 + };
  12 +
  13 + render() {
  14 + const { className } = this.props;
  15 + const selectedLang = getLocale();
  16 + const langMenu = (
  17 + <Menu className={styles.menu} selectedKeys={[selectedLang]} onClick={this.changeLang}>
  18 + <Menu.Item key="zh-CN">
  19 + <span role="img" aria-label="简体中文">
  20 + 🇨🇳
  21 + </span>{' '}
  22 + 简体中文
  23 + </Menu.Item>
  24 + <Menu.Item key="zh-TW">
  25 + <span role="img" aria-label="繁体中文">
  26 + 🇭🇰
  27 + </span>{' '}
  28 + 繁体中文
  29 + </Menu.Item>
  30 + <Menu.Item key="en-US">
  31 + <span role="img" aria-label="English">
  32 + 🇬🇧
  33 + </span>{' '}
  34 + English
  35 + </Menu.Item>
  36 + <Menu.Item key="pt-BR">
  37 + <span role="img" aria-label="Português">
  38 + 🇵🇹
  39 + </span>{' '}
  40 + Português
  41 + </Menu.Item>
  42 + </Menu>
  43 + );
  44 + return (
  45 + <HeaderDropdown overlay={langMenu} placement="bottomRight">
  46 + <span className={classNames(styles.dropDown, className)}>
  47 + <Icon type="global" title={formatMessage({ id: 'navBar.lang' })} />
  48 + </span>
  49 + </HeaderDropdown>
  50 + );
  51 + }
  52 +}
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.menu {
  4 + :global(.anticon) {
  5 + margin-right: 8px;
  6 + }
  7 + :global(.ant-dropdown-menu-item) {
  8 + min-width: 160px;
  9 + }
  10 +}
  11 +
  12 +.dropDown {
  13 + cursor: pointer;
  14 + vertical-align: top;
  15 + line-height: @layout-header-height;
  16 + > i {
  17 + font-size: 14px !important;
  18 + transform: none !important;
  19 + svg {
  20 + position: relative;
  21 + top: -1px;
  22 + }
  23 + }
  24 +}
  1 +import React, { Fragment } from 'react';
  2 +import { formatMessage } from 'umi/locale';
  3 +import Link from 'umi/link';
  4 +import { Icon } from 'antd';
  5 +import { GlobalFooter } from 'ant-design-pro';
  6 +import SelectLang from '../SelectLang';
  7 +import styles from './index.less';
  8 +import logo from './logo.svg';
  9 +
  10 +const links = [
  11 + {
  12 + key: 'help',
  13 + title: formatMessage({ id: 'layout.user.link.help' }),
  14 + href: '',
  15 + },
  16 + {
  17 + key: 'privacy',
  18 + title: formatMessage({ id: 'layout.user.link.privacy' }),
  19 + href: '',
  20 + },
  21 + {
  22 + key: 'terms',
  23 + title: formatMessage({ id: 'layout.user.link.terms' }),
  24 + href: '',
  25 + },
  26 +];
  27 +
  28 +const copyright = (
  29 + <Fragment>
  30 + Copyright <Icon type="copyright" /> 2018 蚂蚁金服体验技术部出品
  31 + </Fragment>
  32 +);
  33 +
  34 +class UserLayout extends React.PureComponent {
  35 + // @TODO title
  36 + // getPageTitle() {
  37 + // const { routerData, location } = this.props;
  38 + // const { pathname } = location;
  39 + // let title = 'Ant Design Pro';
  40 + // if (routerData[pathname] && routerData[pathname].name) {
  41 + // title = `${routerData[pathname].name} - Ant Design Pro`;
  42 + // }
  43 + // return title;
  44 + // }
  45 +
  46 + render() {
  47 + const { children } = this.props;
  48 + return (
  49 + // @TODO <DocumentTitle title={this.getPageTitle()}>
  50 + <div className={styles.container}>
  51 + <div className={styles.lang}>
  52 + <SelectLang />
  53 + </div>
  54 + <div className={styles.content}>
  55 + <div className={styles.top}>
  56 + <div className={styles.header}>
  57 + <Link to="/">
  58 + <img alt="logo" className={styles.logo} src={logo} />
  59 + <span className={styles.title}>Ant Design</span>
  60 + </Link>
  61 + </div>
  62 + <div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
  63 + </div>
  64 + {children}
  65 + </div>
  66 + <GlobalFooter links={links} copyright={copyright} />
  67 + </div>
  68 + );
  69 + }
  70 +}
  71 +
  72 +export default UserLayout;
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.container {
  4 + display: flex;
  5 + flex-direction: column;
  6 + height: 100vh;
  7 + overflow: auto;
  8 + background: @layout-body-background;
  9 +}
  10 +
  11 +.lang {
  12 + text-align: right;
  13 + width: 100%;
  14 + height: 40px;
  15 + line-height: 44px;
  16 + :global(.ant-dropdown-trigger) {
  17 + margin-right: 24px;
  18 + }
  19 +}
  20 +
  21 +.content {
  22 + padding: 32px 0;
  23 + flex: 1;
  24 +}
  25 +
  26 +@media (min-width: @screen-md-min) {
  27 + .container {
  28 + background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
  29 + background-repeat: no-repeat;
  30 + background-position: center 110px;
  31 + background-size: 100%;
  32 + }
  33 +
  34 + .content {
  35 + padding: 32px 0 24px 0;
  36 + }
  37 +}
  38 +
  39 +.top {
  40 + text-align: center;
  41 +}
  42 +
  43 +.header {
  44 + height: 44px;
  45 + line-height: 44px;
  46 + a {
  47 + text-decoration: none;
  48 + }
  49 +}
  50 +
  51 +.logo {
  52 + height: 44px;
  53 + vertical-align: top;
  54 + margin-right: 16px;
  55 +}
  56 +
  57 +.title {
  58 + font-size: 33px;
  59 + color: @heading-color;
  60 + font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
  61 + font-weight: 600;
  62 + position: relative;
  63 + top: 2px;
  64 +}
  65 +
  66 +.desc {
  67 + font-size: @font-size-base;
  68 + color: @text-color-secondary;
  69 + margin-top: 12px;
  70 + margin-bottom: 40px;
  71 +}
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  3 + <!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
  4 + <title>Group 28 Copy 5</title>
  5 + <desc>Created with Sketch.</desc>
  6 + <defs>
  7 + <linearGradient x1="62.1023273%" y1="0%" x2="108.19718%" y2="37.8635764%" id="linearGradient-1">
  8 + <stop stop-color="#4285EB" offset="0%"></stop>
  9 + <stop stop-color="#2EC7FF" offset="100%"></stop>
  10 + </linearGradient>
  11 + <linearGradient x1="69.644116%" y1="0%" x2="54.0428975%" y2="108.456714%" id="linearGradient-2">
  12 + <stop stop-color="#29CDFF" offset="0%"></stop>
  13 + <stop stop-color="#148EFF" offset="37.8600687%"></stop>
  14 + <stop stop-color="#0A60FF" offset="100%"></stop>
  15 + </linearGradient>
  16 + <linearGradient x1="69.6908165%" y1="-12.9743587%" x2="16.7228981%" y2="117.391248%" id="linearGradient-3">
  17 + <stop stop-color="#FA816E" offset="0%"></stop>
  18 + <stop stop-color="#F74A5C" offset="41.472606%"></stop>
  19 + <stop stop-color="#F51D2C" offset="100%"></stop>
  20 + </linearGradient>
  21 + <linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-4">
  22 + <stop stop-color="#FA8E7D" offset="0%"></stop>
  23 + <stop stop-color="#F74A5C" offset="51.2635191%"></stop>
  24 + <stop stop-color="#F51D2C" offset="100%"></stop>
  25 + </linearGradient>
  26 + </defs>
  27 + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  28 + <g id="logo" transform="translate(-20.000000, -20.000000)">
  29 + <g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)">
  30 + <g id="Group-27-Copy-3">
  31 + <g id="Group-25" fill-rule="nonzero">
  32 + <g id="2">
  33 + <path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-1)"></path>
  34 + <path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-2)"></path>
  35 + </g>
  36 + <path d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z" id="Shape" fill="url(#linearGradient-3)"></path>
  37 + </g>
  38 + <ellipse id="Combined-Shape" fill="url(#linearGradient-4)" cx="100.519339" cy="100.436681" rx="23.6001926" ry="23.580786"></ellipse>
  39 + </g>
  40 + </g>
  41 + </g>
  42 + </g>
  43 +</svg>
1 -module.exports = {  
2 - navTheme: 'dark', // theme for nav menu  
3 - primaryColor: '#1890FF', // primary color of ant design  
4 - layout: 'sidemenu', // nav menu position: sidemenu or topmenu  
5 - contentWidth: 'Fluid', // layout of content: Fluid or Fixed, only works when layout is topmenu  
6 - fixedHeader: false, // sticky header  
7 - autoHideHeader: false, // auto hide header  
8 - fixSiderbar: false, // sticky siderbar  
9 -};  
@@ -4,7 +4,8 @@ import { formatMessage, FormattedMessage } from 'umi/locale'; @@ -4,7 +4,8 @@ import { formatMessage, FormattedMessage } from 'umi/locale';
4 import Link from 'umi/link'; 4 import Link from 'umi/link';
5 import router from 'umi/router'; 5 import router from 'umi/router';
6 import { Form, Input, Button, Select, Row, Col, Popover, Progress } from 'antd'; 6 import { Form, Input, Button, Select, Row, Col, Popover, Progress } from 'antd';
7 -import styles from './Register.less'; 7 +import UserLayout from './components/UserLayout';
  8 +import styles from './style.less';
8 9
9 const FormItem = Form.Item; 10 const FormItem = Form.Item;
10 const { Option } = Select; 11 const { Option } = Select;
@@ -178,6 +179,7 @@ class Register extends Component { @@ -178,6 +179,7 @@ class Register extends Component {
178 const { getFieldDecorator } = form; 179 const { getFieldDecorator } = form;
179 const { count, prefix, help, visible } = this.state; 180 const { count, prefix, help, visible } = this.state;
180 return ( 181 return (
  182 + <UserLayout>
181 <div className={styles.main}> 183 <div className={styles.main}>
182 <h3> 184 <h3>
183 <FormattedMessage id="app.register.register" /> 185 <FormattedMessage id="app.register.register" />
@@ -327,6 +329,7 @@ class Register extends Component { @@ -327,6 +329,7 @@ class Register extends Component {
327 </FormItem> 329 </FormItem>
328 </Form> 330 </Form>
329 </div> 331 </div>
  332 + </UserLayout>
330 ); 333 );
331 } 334 }
332 } 335 }
@@ -36,4 +36,41 @@ export default { @@ -36,4 +36,41 @@ export default {
36 'validation.date.required': 'Please select the start and end date', 36 'validation.date.required': 'Please select the start and end date',
37 'validation.goal.required': 'Please enter a description of the goal', 37 'validation.goal.required': 'Please enter a description of the goal',
38 'validation.standard.required': 'Please enter a metric', 38 'validation.standard.required': 'Please enter a metric',
  39 + 'form.get-captcha': 'Get Captcha',
  40 + 'form.captcha.second': 'sec',
  41 + 'form.optional': ' (optional) ',
  42 + 'form.submit': 'Submit',
  43 + 'form.save': 'Save',
  44 + 'form.email.placeholder': 'Email',
  45 + 'form.password.placeholder': 'Password',
  46 + 'form.confirm-password.placeholder': 'Confirm password',
  47 + 'form.phone-number.placeholder': 'Phone number',
  48 + 'form.verification-code.placeholder': 'Verification code',
  49 + 'form.title.label': 'Title',
  50 + 'form.title.placeholder': 'Give the target a name',
  51 + 'form.date.label': 'Start and end date',
  52 + 'form.date.placeholder.start': 'Start date',
  53 + 'form.date.placeholder.end': 'End date',
  54 + 'form.goal.label': 'Goal description',
  55 + 'form.goal.placeholder': 'Please enter your work goals',
  56 + 'form.standard.label': 'Metrics',
  57 + 'form.standard.placeholder': 'Please enter a metric',
  58 + 'form.client.label': 'Client',
  59 + 'form.client.label.tooltip': 'Target service object',
  60 + 'form.client.placeholder':
  61 + 'Please describe your customer service, internal customers directly @ Name / job number',
  62 + 'form.invites.label': 'Inviting critics',
  63 + 'form.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
  64 + 'form.weight.label': 'Weight',
  65 + 'form.weight.placeholder': 'Please enter weight',
  66 + 'form.public.label': 'Target disclosure',
  67 + 'form.public.label.help': 'Customers and invitees are shared by default',
  68 + 'form.public.radio.public': 'Public',
  69 + 'form.public.radio.partially-public': 'Partially public',
  70 + 'form.public.radio.private': 'Private',
  71 + 'form.publicUsers.placeholder': 'Open to',
  72 + 'form.publicUsers.option.A': 'Colleague A',
  73 + 'form.publicUsers.option.B': 'Colleague B',
  74 + 'form.publicUsers.option.C': 'Colleague C',
  75 + 'navBar.lang': 'Languages',
39 }; 76 };
@@ -35,4 +35,40 @@ export default { @@ -35,4 +35,40 @@ export default {
35 'validation.date.required': '请选择起止日期', 35 'validation.date.required': '请选择起止日期',
36 'validation.goal.required': '请输入目标描述', 36 'validation.goal.required': '请输入目标描述',
37 'validation.standard.required': '请输入衡量标准', 37 'validation.standard.required': '请输入衡量标准',
  38 + 'form.get-captcha': '获取验证码',
  39 + 'form.captcha.second': '秒',
  40 + 'form.optional': '(选填)',
  41 + 'form.submit': '提交',
  42 + 'form.save': '保存',
  43 + 'form.email.placeholder': '邮箱',
  44 + 'form.password.placeholder': '至少6位密码,区分大小写',
  45 + 'form.confirm-password.placeholder': '确认密码',
  46 + 'form.phone-number.placeholder': '手机号',
  47 + 'form.verification-code.placeholder': '验证码',
  48 + 'form.title.label': '标题',
  49 + 'form.title.placeholder': '给目标起个名字',
  50 + 'form.date.label': '起止日期',
  51 + 'form.date.placeholder.start': '开始日期',
  52 + 'form.date.placeholder.end': '结束日期',
  53 + 'form.goal.label': '目标描述',
  54 + 'form.goal.placeholder': '请输入你的阶段性工作目标',
  55 + 'form.standard.label': '衡量标准',
  56 + 'form.standard.placeholder': '请输入衡量标准',
  57 + 'form.client.label': '客户',
  58 + 'form.client.label.tooltip': '目标的服务对象',
  59 + 'form.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
  60 + 'form.invites.label': '邀评人',
  61 + 'form.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
  62 + 'form.weight.label': '权重',
  63 + 'form.weight.placeholder': '请输入',
  64 + 'form.public.label': '目标公开',
  65 + 'form.public.label.help': '客户、邀评人默认被分享',
  66 + 'form.public.radio.public': '公开',
  67 + 'form.public.radio.partially-public': '部分公开',
  68 + 'form.public.radio.private': '不公开',
  69 + 'form.publicUsers.placeholder': '公开给',
  70 + 'form.publicUsers.option.A': '同事甲',
  71 + 'form.publicUsers.option.B': '同事乙',
  72 + 'form.publicUsers.option.C': '同事丙',
  73 + 'navBar.lang': '语言',
38 }; 74 };
@@ -35,4 +35,40 @@ export default { @@ -35,4 +35,40 @@ export default {
35 'validation.date.required': '請選擇起止日期', 35 'validation.date.required': '請選擇起止日期',
36 'validation.goal.required': '請輸入目標描述', 36 'validation.goal.required': '請輸入目標描述',
37 'validation.standard.required': '請輸入衡量標淮', 37 'validation.standard.required': '請輸入衡量標淮',
  38 + 'form.get-captcha': '獲取驗證碼',
  39 + 'form.captcha.second': '秒',
  40 + 'form.optional': '(選填)',
  41 + 'form.submit': '提交',
  42 + 'form.save': '保存',
  43 + 'form.email.placeholder': '郵箱',
  44 + 'form.password.placeholder': '至少6位密碼,區分大小寫',
  45 + 'form.confirm-password.placeholder': '確認密碼',
  46 + 'form.phone-number.placeholder': '手機號',
  47 + 'form.verification-code.placeholder': '驗證碼',
  48 + 'form.title.label': '標題',
  49 + 'form.title.placeholder': '給目標起個名字',
  50 + 'form.date.label': '起止日期',
  51 + 'form.date.placeholder.start': '開始日期',
  52 + 'form.date.placeholder.end': '結束日期',
  53 + 'form.goal.label': '目標描述',
  54 + 'form.goal.placeholder': '請輸入妳的階段性工作目標',
  55 + 'form.standard.label': '衡量標淮',
  56 + 'form.standard.placeholder': '請輸入衡量標淮',
  57 + 'form.client.label': '客戶',
  58 + 'form.client.label.tooltip': '目標的服務對象',
  59 + 'form.client.placeholder': '請描述妳服務的客戶,內部客戶直接 @姓名/工號',
  60 + 'form.invites.label': '邀評人',
  61 + 'form.invites.placeholder': '請直接 @姓名/工號,最多可邀請 5 人',
  62 + 'form.weight.label': '權重',
  63 + 'form.weight.placeholder': '請輸入',
  64 + 'form.public.label': '目標公開',
  65 + 'form.public.label.help': '客戶、邀評人默認被分享',
  66 + 'form.public.radio.public': '公開',
  67 + 'form.public.radio.partially-public': '部分公開',
  68 + 'form.public.radio.private': '不公開',
  69 + 'form.publicUsers.placeholder': '公開給',
  70 + 'form.publicUsers.option.A': '同事甲',
  71 + 'form.publicUsers.option.B': '同事乙',
  72 + 'form.publicUsers.option.C': '同事丙',
  73 + 'navBar.lang': '語言',
38 }; 74 };
1 -import { queryActivities } from '@/services/api'; 1 +import { fakeRegister } from './service';
  2 +import { setAuthority } from './utils/authority';
  3 +import { reloadAuthorized } from './utils/Authorized';
2 4
3 export default { 5 export default {
4 - namespace: 'activities', 6 + namespace: 'register',
5 7
6 state: { 8 state: {
7 - list: [], 9 + status: undefined,
8 }, 10 },
9 11
10 effects: { 12 effects: {
11 - *fetchList(_, { call, put }) {  
12 - const response = yield call(queryActivities); 13 + *submit({ payload }, { call, put }) {
  14 + const response = yield call(fakeRegister, payload);
13 yield put({ 15 yield put({
14 - type: 'saveList',  
15 - payload: Array.isArray(response) ? response : [], 16 + type: 'registerHandle',
  17 + payload: response,
16 }); 18 });
17 }, 19 },
18 }, 20 },
19 21
20 reducers: { 22 reducers: {
21 - saveList(state, action) { 23 + registerHandle(state, { payload }) {
  24 + setAuthority('user');
  25 + reloadAuthorized();
22 return { 26 return {
23 ...state, 27 ...state,
24 - list: action.payload, 28 + status: payload.status,
25 }; 29 };
26 }, 30 },
27 }, 31 },
1 -import { routerRedux } from 'dva/router';  
2 -import { stringify } from 'qs';  
3 -import { fakeAccountLogin, getFakeCaptcha } from '@/services/api';  
4 -import { setAuthority } from '@/utils/authority';  
5 -import { getPageQuery } from '@/utils/utils';  
6 -import { reloadAuthorized } from '@/utils/Authorized';  
7 -  
8 -export default {  
9 - namespace: 'login',  
10 -  
11 - state: {  
12 - status: undefined,  
13 - },  
14 -  
15 - effects: {  
16 - *login({ payload }, { call, put }) {  
17 - const response = yield call(fakeAccountLogin, payload);  
18 - yield put({  
19 - type: 'changeLoginStatus',  
20 - payload: response,  
21 - });  
22 - // Login successfully  
23 - if (response.status === 'ok') {  
24 - reloadAuthorized();  
25 - const urlParams = new URL(window.location.href);  
26 - const params = getPageQuery();  
27 - let { redirect } = params;  
28 - if (redirect) {  
29 - const redirectUrlParams = new URL(redirect);  
30 - if (redirectUrlParams.origin === urlParams.origin) {  
31 - redirect = redirect.substr(urlParams.origin.length);  
32 - if (redirect.match(/^\/.*#/)) {  
33 - redirect = redirect.substr(redirect.indexOf('#') + 1);  
34 - }  
35 - } else {  
36 - window.location.href = redirect;  
37 - return;  
38 - }  
39 - }  
40 - yield put(routerRedux.replace(redirect || '/'));  
41 - }  
42 - },  
43 -  
44 - *getCaptcha({ payload }, { call }) {  
45 - yield call(getFakeCaptcha, payload);  
46 - },  
47 -  
48 - *logout(_, { put }) {  
49 - yield put({  
50 - type: 'changeLoginStatus',  
51 - payload: {  
52 - status: false,  
53 - currentAuthority: 'guest',  
54 - },  
55 - });  
56 - reloadAuthorized();  
57 - yield put(  
58 - routerRedux.push({  
59 - pathname: '/user/login',  
60 - search: stringify({  
61 - redirect: window.location.href,  
62 - }),  
63 - })  
64 - );  
65 - },  
66 - },  
67 -  
68 - reducers: {  
69 - changeLoginStatus(state, { payload }) {  
70 - setAuthority(payload.currentAuthority);  
71 - return {  
72 - ...state,  
73 - status: payload.status,  
74 - type: payload.type,  
75 - };  
76 - },  
77 - },  
78 -};  
1 -import { message } from 'antd';  
2 -import defaultSettings from '../defaultSettings';  
3 -  
4 -let lessNodesAppended;  
5 -const updateTheme = primaryColor => {  
6 - // Don't compile less in production!  
7 - if (APP_TYPE !== 'site') {  
8 - return;  
9 - }  
10 - // Determine if the component is remounted  
11 - if (!primaryColor) {  
12 - return;  
13 - }  
14 - const hideMessage = message.loading('正在编译主题!', 0);  
15 - function buildIt() {  
16 - if (!window.less) {  
17 - return;  
18 - }  
19 - setTimeout(() => {  
20 - window.less  
21 - .modifyVars({  
22 - '@primary-color': primaryColor,  
23 - })  
24 - .then(() => {  
25 - hideMessage();  
26 - })  
27 - .catch(() => {  
28 - message.error('Failed to update theme');  
29 - hideMessage();  
30 - });  
31 - }, 200);  
32 - }  
33 - if (!lessNodesAppended) {  
34 - // insert less.js and color.less  
35 - const lessStyleNode = document.createElement('link');  
36 - const lessConfigNode = document.createElement('script');  
37 - const lessScriptNode = document.createElement('script');  
38 - lessStyleNode.setAttribute('rel', 'stylesheet/less');  
39 - lessStyleNode.setAttribute('href', '/color.less');  
40 - lessConfigNode.innerHTML = `  
41 - window.less = {  
42 - async: true,  
43 - env: 'production',  
44 - javascriptEnabled: true  
45 - };  
46 - `;  
47 - lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js';  
48 - lessScriptNode.async = true;  
49 - lessScriptNode.onload = () => {  
50 - buildIt();  
51 - lessScriptNode.onload = null;  
52 - };  
53 - document.body.appendChild(lessStyleNode);  
54 - document.body.appendChild(lessConfigNode);  
55 - document.body.appendChild(lessScriptNode);  
56 - lessNodesAppended = true;  
57 - } else {  
58 - buildIt();  
59 - }  
60 -};  
61 -  
62 -const updateColorWeak = colorWeak => {  
63 - document.body.className = colorWeak ? 'colorWeak' : '';  
64 -};  
65 -  
66 -export default {  
67 - namespace: 'setting',  
68 - state: defaultSettings,  
69 - reducers: {  
70 - getSetting(state) {  
71 - const setting = {};  
72 - const urlParams = new URL(window.location.href);  
73 - Object.keys(state).forEach(key => {  
74 - if (urlParams.searchParams.has(key)) {  
75 - const value = urlParams.searchParams.get(key);  
76 - setting[key] = value === '1' ? true : value;  
77 - }  
78 - });  
79 - const { primaryColor, colorWeak } = setting;  
80 - if (state.primaryColor !== primaryColor) {  
81 - updateTheme(primaryColor);  
82 - }  
83 - updateColorWeak(colorWeak);  
84 - return {  
85 - ...state,  
86 - ...setting,  
87 - };  
88 - },  
89 - changeSetting(state, { payload }) {  
90 - const urlParams = new URL(window.location.href);  
91 - Object.keys(defaultSettings).forEach(key => {  
92 - if (urlParams.searchParams.has(key)) {  
93 - urlParams.searchParams.delete(key);  
94 - }  
95 - });  
96 - Object.keys(payload).forEach(key => {  
97 - if (key === 'collapse') {  
98 - return;  
99 - }  
100 - let value = payload[key];  
101 - if (value === true) {  
102 - value = 1;  
103 - }  
104 - if (defaultSettings[key] !== value) {  
105 - urlParams.searchParams.set(key, value);  
106 - }  
107 - });  
108 - const { primaryColor, colorWeak, contentWidth } = payload;  
109 - if (state.primaryColor !== primaryColor) {  
110 - updateTheme(primaryColor);  
111 - }  
112 - if (state.contentWidth !== contentWidth && window.dispatchEvent) {  
113 - window.dispatchEvent(new Event('resize'));  
114 - }  
115 - updateColorWeak(colorWeak);  
116 - window.history.replaceState(null, 'setting', urlParams.href);  
117 - return {  
118 - ...state,  
119 - ...payload,  
120 - };  
121 - },  
122 - },  
123 -};  
  1 +import request from 'umi-request';
  2 +
  3 +export async function fakeRegister(params) {
  4 + return request('/api/register', {
  5 + method: 'POST',
  6 + body: params,
  7 + });
  8 +}
@@ -5,6 +5,6 @@ export default { @@ -5,6 +5,6 @@ export default {
5 dva: true, 5 dva: true,
6 locale: true, 6 locale: true,
7 antd: true, 7 antd: true,
8 - }] 8 + }],
9 ], 9 ],
10 -} 10 +};
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 updateRule(params) {  
37 - return request('/api/rule', {  
38 - method: 'POST',  
39 - body: {  
40 - ...params,  
41 - method: 'update',  
42 - },  
43 - });  
44 -}  
45 -  
46 -export async function fakeSubmitForm(params) {  
47 - return request('/api/forms', {  
48 - method: 'POST',  
49 - body: params,  
50 - });  
51 -}  
52 -  
53 -export async function fakeChartData() {  
54 - return request('/api/fake_chart_data');  
55 -}  
56 -  
57 -export async function queryTags() {  
58 - return request('/api/tags');  
59 -}  
60 -  
61 -export async function queryBasicProfile() {  
62 - return request('/api/profile/basic');  
63 -}  
64 -  
65 -export async function queryAdvancedProfile() {  
66 - return request('/api/profile/advanced');  
67 -}  
68 -  
69 -export async function queryFakeList(params) {  
70 - return request(`/api/fake_list?${stringify(params)}`);  
71 -}  
72 -  
73 -export async function removeFakeList(params) {  
74 - const { count = 5, ...restParams } = params;  
75 - return request(`/api/fake_list?count=${count}`, {  
76 - method: 'POST',  
77 - body: {  
78 - ...restParams,  
79 - method: 'delete',  
80 - },  
81 - });  
82 -}  
83 -  
84 -export async function addFakeList(params) {  
85 - const { count = 5, ...restParams } = params;  
86 - return request(`/api/fake_list?count=${count}`, {  
87 - method: 'POST',  
88 - body: {  
89 - ...restParams,  
90 - method: 'post',  
91 - },  
92 - });  
93 -}  
94 -  
95 -export async function updateFakeList(params) {  
96 - const { count = 5, ...restParams } = params;  
97 - return request(`/api/fake_list?count=${count}`, {  
98 - method: 'POST',  
99 - body: {  
100 - ...restParams,  
101 - method: 'update',  
102 - },  
103 - });  
104 -}  
105 -  
106 -export async function fakeAccountLogin(params) {  
107 - return request('/api/login/account', {  
108 - method: 'POST',  
109 - body: params,  
110 - });  
111 -}  
112 -  
113 -export async function fakeRegister(params) {  
114 - return request('/api/register', {  
115 - method: 'POST',  
116 - body: params,  
117 - });  
118 -}  
119 -  
120 -export async function queryNotices() {  
121 - return request('/api/notices');  
122 -}  
123 -  
124 -export async function getFakeCaptcha(mobile) {  
125 - return request(`/api/captcha?mobile=${mobile}`);  
126 -}  
1 -import RenderAuthorized from 'ant-design-pro/lib/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;  
1 -// use localStorage to store the authority info, which might be sent from server in actual project.  
2 -export function getAuthority(str) {  
3 - // return localStorage.getItem('antd-pro-authority') || ['admin', 'user'];  
4 - const authorityString =  
5 - typeof str === 'undefined' ? localStorage.getItem('antd-pro-authority') : str;  
6 - // authorityString could be admin, "admin", ["admin"]  
7 - let authority;  
8 - try {  
9 - authority = JSON.parse(authorityString);  
10 - } catch (e) {  
11 - authority = authorityString;  
12 - }  
13 - if (typeof authority === 'string') {  
14 - return [authority];  
15 - }  
16 - return authority || ['admin'];  
17 -}  
18 -  
19 -export function setAuthority(authority) {  
20 - const proAuthority = typeof authority === 'string' ? [authority] : authority;  
21 - return localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority));  
22 -}  
1 -import fetch from 'dva/fetch';  
2 -import { notification } from 'antd';  
3 -import router from 'umi/router';  
4 -import hash from 'hash.js';  
5 -import { isAntdPro } from './utils';  
6 -  
7 -const codeMessage = {  
8 - 200: '服务器成功返回请求的数据。',  
9 - 201: '新建或修改数据成功。',  
10 - 202: '一个请求已经进入后台排队(异步任务)。',  
11 - 204: '删除数据成功。',  
12 - 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',  
13 - 401: '用户没有权限(令牌、用户名、密码错误)。',  
14 - 403: '用户得到授权,但是访问是被禁止的。',  
15 - 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',  
16 - 406: '请求的格式不可得。',  
17 - 410: '请求的资源被永久删除,且不会再得到的。',  
18 - 422: '当创建一个对象时,发生一个验证错误。',  
19 - 500: '服务器发生错误,请检查服务器。',  
20 - 502: '网关错误。',  
21 - 503: '服务不可用,服务器暂时过载或维护。',  
22 - 504: '网关超时。',  
23 -};  
24 -  
25 -const checkStatus = response => {  
26 - if (response.status >= 200 && response.status < 300) {  
27 - return response;  
28 - }  
29 - const errortext = codeMessage[response.status] || response.statusText;  
30 - notification.error({  
31 - message: `请求错误 ${response.status}: ${response.url}`,  
32 - description: errortext,  
33 - });  
34 - const error = new Error(errortext);  
35 - error.name = response.status;  
36 - error.response = response;  
37 - throw error;  
38 -};  
39 -  
40 -const cachedSave = (response, hashcode) => {  
41 - /**  
42 - * Clone a response data and store it in sessionStorage  
43 - * Does not support data other than json, Cache only json  
44 - */  
45 - const contentType = response.headers.get('Content-Type');  
46 - if (contentType && contentType.match(/application\/json/i)) {  
47 - // All data is saved as text  
48 - response  
49 - .clone()  
50 - .text()  
51 - .then(content => {  
52 - sessionStorage.setItem(hashcode, content);  
53 - sessionStorage.setItem(`${hashcode}:timestamp`, Date.now());  
54 - });  
55 - }  
56 - return response;  
57 -};  
58 -  
59 -/**  
60 - * Requests a URL, returning a promise.  
61 - *  
62 - * @param {string} url The URL we want to request  
63 - * @param {object} [option] The options we want to pass to "fetch"  
64 - * @return {object} An object containing either "data" or "err"  
65 - */  
66 -export default function request(url, option) {  
67 - const options = {  
68 - expirys: isAntdPro(),  
69 - ...option,  
70 - };  
71 - /**  
72 - * Produce fingerprints based on url and parameters  
73 - * Maybe url has the same parameters  
74 - */  
75 - const fingerprint = url + (options.body ? JSON.stringify(options.body) : '');  
76 - const hashcode = hash  
77 - .sha256()  
78 - .update(fingerprint)  
79 - .digest('hex');  
80 -  
81 - const defaultOptions = {  
82 - credentials: 'include',  
83 - };  
84 - const newOptions = { ...defaultOptions, ...options };  
85 - if (  
86 - newOptions.method === 'POST' ||  
87 - newOptions.method === 'PUT' ||  
88 - newOptions.method === 'DELETE'  
89 - ) {  
90 - if (!(newOptions.body instanceof FormData)) {  
91 - newOptions.headers = {  
92 - Accept: 'application/json',  
93 - 'Content-Type': 'application/json; charset=utf-8',  
94 - ...newOptions.headers,  
95 - };  
96 - newOptions.body = JSON.stringify(newOptions.body);  
97 - } else {  
98 - // newOptions.body is FormData  
99 - newOptions.headers = {  
100 - Accept: 'application/json',  
101 - ...newOptions.headers,  
102 - };  
103 - }  
104 - }  
105 -  
106 - const expirys = options.expirys && 60;  
107 - // options.expirys !== false, return the cache,  
108 - if (options.expirys !== false) {  
109 - const cached = sessionStorage.getItem(hashcode);  
110 - const whenCached = sessionStorage.getItem(`${hashcode}:timestamp`);  
111 - if (cached !== null && whenCached !== null) {  
112 - const age = (Date.now() - whenCached) / 1000;  
113 - if (age < expirys) {  
114 - const response = new Response(new Blob([cached]));  
115 - return response.json();  
116 - }  
117 - sessionStorage.removeItem(hashcode);  
118 - sessionStorage.removeItem(`${hashcode}:timestamp`);  
119 - }  
120 - }  
121 - return fetch(url, newOptions)  
122 - .then(checkStatus)  
123 - .then(response => cachedSave(response, hashcode))  
124 - .then(response => {  
125 - // DELETE and 204 do not return data by default  
126 - // using .json will report an error.  
127 - if (newOptions.method === 'DELETE' || response.status === 204) {  
128 - return response.text();  
129 - }  
130 - return response.json();  
131 - })  
132 - .catch(e => {  
133 - const status = e.name;  
134 - if (status === 401) {  
135 - // @HACK  
136 - /* eslint-disable no-underscore-dangle */  
137 - window.g_app._store.dispatch({  
138 - type: 'login/logout',  
139 - });  
140 - return;  
141 - }  
142 - // environment should not be used  
143 - if (status === 403) {  
144 - router.push('/exception/403');  
145 - return;  
146 - }  
147 - if (status <= 504 && status >= 500) {  
148 - router.push('/exception/500');  
149 - return;  
150 - }  
151 - if (status >= 404 && status < 422) {  
152 - router.push('/exception/404');  
153 - }  
154 - });  
155 -}  
1 -import moment from 'moment';  
2 -import React from 'react';  
3 -import nzh from 'nzh/cn';  
4 -import { parse, stringify } from 'qs';  
5 -  
6 -export function fixedZero(val) {  
7 - return val * 1 < 10 ? `0${val}` : val;  
8 -}  
9 -  
10 -export function getTimeDistance(type) {  
11 - const now = new Date();  
12 - const oneDay = 1000 * 60 * 60 * 24;  
13 -  
14 - if (type === 'today') {  
15 - now.setHours(0);  
16 - now.setMinutes(0);  
17 - now.setSeconds(0);  
18 - return [moment(now), moment(now.getTime() + (oneDay - 1000))];  
19 - }  
20 -  
21 - if (type === 'week') {  
22 - let day = now.getDay();  
23 - now.setHours(0);  
24 - now.setMinutes(0);  
25 - now.setSeconds(0);  
26 -  
27 - if (day === 0) {  
28 - day = 6;  
29 - } else {  
30 - day -= 1;  
31 - }  
32 -  
33 - const beginTime = now.getTime() - day * oneDay;  
34 -  
35 - return [moment(beginTime), moment(beginTime + (7 * oneDay - 1000))];  
36 - }  
37 -  
38 - if (type === 'month') {  
39 - const year = now.getFullYear();  
40 - const month = now.getMonth();  
41 - const nextDate = moment(now).add(1, 'months');  
42 - const nextYear = nextDate.year();  
43 - const nextMonth = nextDate.month();  
44 -  
45 - return [  
46 - moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`),  
47 - moment(moment(`${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`).valueOf() - 1000),  
48 - ];  
49 - }  
50 -  
51 - const year = now.getFullYear();  
52 - return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)];  
53 -}  
54 -  
55 -export function getPlainNode(nodeList, parentPath = '') {  
56 - const arr = [];  
57 - nodeList.forEach(node => {  
58 - const item = node;  
59 - item.path = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/');  
60 - item.exact = true;  
61 - if (item.children && !item.component) {  
62 - arr.push(...getPlainNode(item.children, item.path));  
63 - } else {  
64 - if (item.children && item.component) {  
65 - item.exact = false;  
66 - }  
67 - arr.push(item);  
68 - }  
69 - });  
70 - return arr;  
71 -}  
72 -  
73 -export function digitUppercase(n) {  
74 - return nzh.toMoney(n);  
75 -}  
76 -  
77 -function getRelation(str1, str2) {  
78 - if (str1 === str2) {  
79 - console.warn('Two path are equal!'); // eslint-disable-line  
80 - }  
81 - const arr1 = str1.split('/');  
82 - const arr2 = str2.split('/');  
83 - if (arr2.every((item, index) => item === arr1[index])) {  
84 - return 1;  
85 - }  
86 - if (arr1.every((item, index) => item === arr2[index])) {  
87 - return 2;  
88 - }  
89 - return 3;  
90 -}  
91 -  
92 -function getRenderArr(routes) {  
93 - let renderArr = [];  
94 - renderArr.push(routes[0]);  
95 - for (let i = 1; i < routes.length; i += 1) {  
96 - // 去重  
97 - renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1);  
98 - // 是否包含  
99 - const isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);  
100 - if (isAdd) {  
101 - renderArr.push(routes[i]);  
102 - }  
103 - }  
104 - return renderArr;  
105 -}  
106 -  
107 -/**  
108 - * Get router routing configuration  
109 - * { path:{name,...param}}=>Array<{name,path ...param}>  
110 - * @param {string} path  
111 - * @param {routerData} routerData  
112 - */  
113 -export function getRoutes(path, routerData) {  
114 - let routes = Object.keys(routerData).filter(  
115 - routePath => routePath.indexOf(path) === 0 && routePath !== path  
116 - );  
117 - // Replace path to '' eg. path='user' /user/name => name  
118 - routes = routes.map(item => item.replace(path, ''));  
119 - // Get the route to be rendered to remove the deep rendering  
120 - const renderArr = getRenderArr(routes);  
121 - // Conversion and stitching parameters  
122 - const renderRoutes = renderArr.map(item => {  
123 - const exact = !routes.some(route => route !== item && getRelation(route, item) === 1);  
124 - return {  
125 - exact,  
126 - ...routerData[`${path}${item}`],  
127 - key: `${path}${item}`,  
128 - path: `${path}${item}`,  
129 - };  
130 - });  
131 - return renderRoutes;  
132 -}  
133 -  
134 -export function getPageQuery() {  
135 - return parse(window.location.href.split('?')[1]);  
136 -}  
137 -  
138 -export function getQueryPath(path = '', query = {}) {  
139 - const search = stringify(query);  
140 - if (search.length) {  
141 - return `${path}?${search}`;  
142 - }  
143 - return path;  
144 -}  
145 -  
146 -/* eslint no-useless-escape:0 */  
147 -const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;  
148 -  
149 -export function isUrl(path) {  
150 - return reg.test(path);  
151 -}  
152 -  
153 -export function formatWan(val) {  
154 - const v = val * 1;  
155 - if (!v || Number.isNaN(v)) return '';  
156 -  
157 - let result = val;  
158 - if (val > 10000) {  
159 - result = Math.floor(val / 10000);  
160 - result = (  
161 - <span>  
162 - {result}  
163 - <span  
164 - style={{  
165 - position: 'relative',  
166 - top: -2,  
167 - fontSize: 14,  
168 - fontStyle: 'normal',  
169 - marginLeft: 2,  
170 - }}  
171 - >  
172 -  
173 - </span>  
174 - </span>  
175 - );  
176 - }  
177 - return result;  
178 -}  
179 -  
180 -// 给官方演示站点用,用于关闭真实开发环境不需要使用的特性  
181 -export function isAntdPro() {  
182 - return window.location.hostname === 'preview.pro.ant.design';  
183 -}  
@@ -13,17 +13,12 @@ @@ -13,17 +13,12 @@
13 "dependencies": { 13 "dependencies": {
14 "react": "^16.6.3", 14 "react": "^16.6.3",
15 "antd": "^3.10.9", 15 "antd": "^3.10.9",
16 - "ant-design-pro": "^2.1.1",  
17 - "dva": "^2.4.0",  
18 - "qs": "^6.6.0",  
19 - "hash.js": "^1.1.5",  
20 - "moment": "^2.22.2",  
21 - "nzh": "^1.0.3" 16 + "ant-design-pro": "^2.1.1"
22 }, 17 },
23 "devDependencies": { 18 "devDependencies": {
24 "umi": "^2.3.0-beta.1", 19 "umi": "^2.3.0-beta.1",
25 "umi-plugin-react": "^1.3.0-beta.1", 20 "umi-plugin-react": "^1.3.0-beta.1",
26 "umi-plugin-block-dev": "^1.0.0" 21 "umi-plugin-block-dev": "^1.0.0"
27 }, 22 },
28 - "license": "ISC" 23 + "license": "MIT"
29 } 24 }
1 -// 代码中会兼容本地 service mock 以及部署站点的静态数据  
2 -export default {  
3 - // 支持值为 Object 和 Array  
4 - 'GET /api/currentUser': {  
5 - name: 'Serati Ma',  
6 - avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',  
7 - userid: '00000001',  
8 - email: 'antdesign@alipay.com',  
9 - signature: '海纳百川,有容乃大',  
10 - title: '交互专家',  
11 - group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',  
12 - tags: [  
13 - {  
14 - key: '0',  
15 - label: '很有想法的',  
16 - },  
17 - {  
18 - key: '1',  
19 - label: '专注设计',  
20 - },  
21 - {  
22 - key: '2',  
23 - label: '辣~',  
24 - },  
25 - {  
26 - key: '3',  
27 - label: '大长腿',  
28 - },  
29 - {  
30 - key: '4',  
31 - label: '川妹子',  
32 - },  
33 - {  
34 - key: '5',  
35 - label: '海纳百川',  
36 - },  
37 - ],  
38 - notifyCount: 12,  
39 - unreadCount: 11,  
40 - country: 'China',  
41 - geographic: {  
42 - province: {  
43 - label: '浙江省',  
44 - key: '330000',  
45 - },  
46 - city: {  
47 - label: '杭州市',  
48 - key: '330100',  
49 - },  
50 - },  
51 - address: '西湖区工专路 77 号',  
52 - phone: '0752-268888888',  
53 - },  
54 - // GET POST 可省略  
55 - 'GET /api/users': [  
56 - {  
57 - key: '1',  
58 - name: 'John Brown',  
59 - age: 32,  
60 - address: 'New York No. 1 Lake Park',  
61 - },  
62 - {  
63 - key: '2',  
64 - name: 'Jim Green',  
65 - age: 42,  
66 - address: 'London No. 1 Lake Park',  
67 - },  
68 - {  
69 - key: '3',  
70 - name: 'Joe Black',  
71 - age: 32,  
72 - address: 'Sidney No. 1 Lake Park',  
73 - },  
74 - ],  
75 - 'POST /api/login/account': (req, res) => {  
76 - const { password, userName, type } = req.body;  
77 - if (password === 'ant.design' && userName === 'admin') {  
78 - res.send({  
79 - status: 'ok',  
80 - type,  
81 - currentAuthority: 'admin',  
82 - });  
83 - return;  
84 - }  
85 - if (password === 'ant.design' && userName === 'user') {  
86 - res.send({  
87 - status: 'ok',  
88 - type,  
89 - currentAuthority: 'user',  
90 - });  
91 - return;  
92 - }  
93 - res.send({  
94 - status: 'error',  
95 - type,  
96 - currentAuthority: 'guest',  
97 - });  
98 - },  
99 - 'POST /api/register': (req, res) => {  
100 - res.send({ status: 'ok', currentAuthority: 'user' });  
101 - },  
102 - 'GET /api/500': (req, res) => {  
103 - res.status(500).send({  
104 - timestamp: 1513932555104,  
105 - status: 500,  
106 - error: 'error',  
107 - message: 'error',  
108 - path: '/base/category/list',  
109 - });  
110 - },  
111 - 'GET /api/404': (req, res) => {  
112 - res.status(404).send({  
113 - timestamp: 1513932643431,  
114 - status: 404,  
115 - error: 'Not Found',  
116 - message: 'No message available',  
117 - path: '/base/category/list/2121212',  
118 - });  
119 - },  
120 - 'GET /api/403': (req, res) => {  
121 - res.status(403).send({  
122 - timestamp: 1513932555104,  
123 - status: 403,  
124 - error: 'Unauthorized',  
125 - message: 'Unauthorized',  
126 - path: '/base/category/list',  
127 - });  
128 - },  
129 - 'GET /api/401': (req, res) => {  
130 - res.status(401).send({  
131 - timestamp: 1513932555104,  
132 - status: 401,  
133 - error: 'Unauthorized',  
134 - message: 'Unauthorized',  
135 - path: '/base/category/list',  
136 - });  
137 - },  
138 -};  
  1 +import React, { PureComponent } from 'react';
  2 +import { Dropdown } from 'antd';
  3 +import classNames from 'classnames';
  4 +import styles from './index.less';
  5 +
  6 +export default class HeaderDropdown extends PureComponent {
  7 + render() {
  8 + const { overlayClassName, ...props } = this.props;
  9 + return (
  10 + <Dropdown overlayClassName={classNames(styles.container, overlayClassName)} {...props} />
  11 + );
  12 + }
  13 +}
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.container > *:global(:not(.ant-dropdown-menu)) {
  4 + background-color: #fff;
  5 + box-shadow: @shadow-1-down;
  6 + border-radius: 4px;
  7 +}
  8 +
  9 +@media screen and (max-width: @screen-xs) {
  10 + .container {
  11 + width: 100% !important;
  12 + }
  13 + .container > * {
  14 + border-radius: 0 !important;
  15 + }
  16 +}
  1 +import React, { PureComponent } from 'react';
  2 +import { formatMessage, setLocale, getLocale } from 'umi/locale';
  3 +import { Menu, Icon } from 'antd';
  4 +import classNames from 'classnames';
  5 +import HeaderDropdown from '../HeaderDropdown';
  6 +import styles from './index.less';
  7 +
  8 +export default class SelectLang extends PureComponent {
  9 + changeLang = ({ key }) => {
  10 + setLocale(key);
  11 + };
  12 +
  13 + render() {
  14 + const { className } = this.props;
  15 + const selectedLang = getLocale();
  16 + const langMenu = (
  17 + <Menu className={styles.menu} selectedKeys={[selectedLang]} onClick={this.changeLang}>
  18 + <Menu.Item key="zh-CN">
  19 + <span role="img" aria-label="简体中文">
  20 + 🇨🇳
  21 + </span>{' '}
  22 + 简体中文
  23 + </Menu.Item>
  24 + <Menu.Item key="zh-TW">
  25 + <span role="img" aria-label="繁体中文">
  26 + 🇭🇰
  27 + </span>{' '}
  28 + 繁体中文
  29 + </Menu.Item>
  30 + <Menu.Item key="en-US">
  31 + <span role="img" aria-label="English">
  32 + 🇬🇧
  33 + </span>{' '}
  34 + English
  35 + </Menu.Item>
  36 + <Menu.Item key="pt-BR">
  37 + <span role="img" aria-label="Português">
  38 + 🇵🇹
  39 + </span>{' '}
  40 + Português
  41 + </Menu.Item>
  42 + </Menu>
  43 + );
  44 + return (
  45 + <HeaderDropdown overlay={langMenu} placement="bottomRight">
  46 + <span className={classNames(styles.dropDown, className)}>
  47 + <Icon type="global" title={formatMessage({ id: 'navBar.lang' })} />
  48 + </span>
  49 + </HeaderDropdown>
  50 + );
  51 + }
  52 +}
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.menu {
  4 + :global(.anticon) {
  5 + margin-right: 8px;
  6 + }
  7 + :global(.ant-dropdown-menu-item) {
  8 + min-width: 160px;
  9 + }
  10 +}
  11 +
  12 +.dropDown {
  13 + cursor: pointer;
  14 + vertical-align: top;
  15 + line-height: @layout-header-height;
  16 + > i {
  17 + font-size: 14px !important;
  18 + transform: none !important;
  19 + svg {
  20 + position: relative;
  21 + top: -1px;
  22 + }
  23 + }
  24 +}
  1 +import React, { Fragment } from 'react';
  2 +import { formatMessage } from 'umi/locale';
  3 +import Link from 'umi/link';
  4 +import { Icon } from 'antd';
  5 +import { GlobalFooter } from 'ant-design-pro';
  6 +import SelectLang from '../SelectLang';
  7 +import styles from './index.less';
  8 +import logo from './logo.svg';
  9 +
  10 +const links = [
  11 + {
  12 + key: 'help',
  13 + title: formatMessage({ id: 'layout.user.link.help' }),
  14 + href: '',
  15 + },
  16 + {
  17 + key: 'privacy',
  18 + title: formatMessage({ id: 'layout.user.link.privacy' }),
  19 + href: '',
  20 + },
  21 + {
  22 + key: 'terms',
  23 + title: formatMessage({ id: 'layout.user.link.terms' }),
  24 + href: '',
  25 + },
  26 +];
  27 +
  28 +const copyright = (
  29 + <Fragment>
  30 + Copyright <Icon type="copyright" /> 2018 蚂蚁金服体验技术部出品
  31 + </Fragment>
  32 +);
  33 +
  34 +class UserLayout extends React.PureComponent {
  35 + // @TODO title
  36 + // getPageTitle() {
  37 + // const { routerData, location } = this.props;
  38 + // const { pathname } = location;
  39 + // let title = 'Ant Design Pro';
  40 + // if (routerData[pathname] && routerData[pathname].name) {
  41 + // title = `${routerData[pathname].name} - Ant Design Pro`;
  42 + // }
  43 + // return title;
  44 + // }
  45 +
  46 + render() {
  47 + const { children } = this.props;
  48 + return (
  49 + // @TODO <DocumentTitle title={this.getPageTitle()}>
  50 + <div className={styles.container}>
  51 + <div className={styles.lang}>
  52 + <SelectLang />
  53 + </div>
  54 + <div className={styles.content}>
  55 + <div className={styles.top}>
  56 + <div className={styles.header}>
  57 + <Link to="/">
  58 + <img alt="logo" className={styles.logo} src={logo} />
  59 + <span className={styles.title}>Ant Design</span>
  60 + </Link>
  61 + </div>
  62 + <div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
  63 + </div>
  64 + {children}
  65 + </div>
  66 + <GlobalFooter links={links} copyright={copyright} />
  67 + </div>
  68 + );
  69 + }
  70 +}
  71 +
  72 +export default UserLayout;
  1 +@import '~antd/lib/style/themes/default.less';
  2 +
  3 +.container {
  4 + display: flex;
  5 + flex-direction: column;
  6 + height: 100vh;
  7 + overflow: auto;
  8 + background: @layout-body-background;
  9 +}
  10 +
  11 +.lang {
  12 + text-align: right;
  13 + width: 100%;
  14 + height: 40px;
  15 + line-height: 44px;
  16 + :global(.ant-dropdown-trigger) {
  17 + margin-right: 24px;
  18 + }
  19 +}
  20 +
  21 +.content {
  22 + padding: 32px 0;
  23 + flex: 1;
  24 +}
  25 +
  26 +@media (min-width: @screen-md-min) {
  27 + .container {
  28 + background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
  29 + background-repeat: no-repeat;
  30 + background-position: center 110px;
  31 + background-size: 100%;
  32 + }
  33 +
  34 + .content {
  35 + padding: 32px 0 24px 0;
  36 + }
  37 +}
  38 +
  39 +.top {
  40 + text-align: center;
  41 +}
  42 +
  43 +.header {
  44 + height: 44px;
  45 + line-height: 44px;
  46 + a {
  47 + text-decoration: none;
  48 + }
  49 +}
  50 +
  51 +.logo {
  52 + height: 44px;
  53 + vertical-align: top;
  54 + margin-right: 16px;
  55 +}
  56 +
  57 +.title {
  58 + font-size: 33px;
  59 + color: @heading-color;
  60 + font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
  61 + font-weight: 600;
  62 + position: relative;
  63 + top: 2px;
  64 +}
  65 +
  66 +.desc {
  67 + font-size: @font-size-base;
  68 + color: @text-color-secondary;
  69 + margin-top: 12px;
  70 + margin-bottom: 40px;
  71 +}
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  3 + <!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
  4 + <title>Group 28 Copy 5</title>
  5 + <desc>Created with Sketch.</desc>
  6 + <defs>
  7 + <linearGradient x1="62.1023273%" y1="0%" x2="108.19718%" y2="37.8635764%" id="linearGradient-1">
  8 + <stop stop-color="#4285EB" offset="0%"></stop>
  9 + <stop stop-color="#2EC7FF" offset="100%"></stop>
  10 + </linearGradient>
  11 + <linearGradient x1="69.644116%" y1="0%" x2="54.0428975%" y2="108.456714%" id="linearGradient-2">
  12 + <stop stop-color="#29CDFF" offset="0%"></stop>
  13 + <stop stop-color="#148EFF" offset="37.8600687%"></stop>
  14 + <stop stop-color="#0A60FF" offset="100%"></stop>
  15 + </linearGradient>
  16 + <linearGradient x1="69.6908165%" y1="-12.9743587%" x2="16.7228981%" y2="117.391248%" id="linearGradient-3">
  17 + <stop stop-color="#FA816E" offset="0%"></stop>
  18 + <stop stop-color="#F74A5C" offset="41.472606%"></stop>
  19 + <stop stop-color="#F51D2C" offset="100%"></stop>
  20 + </linearGradient>
  21 + <linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-4">
  22 + <stop stop-color="#FA8E7D" offset="0%"></stop>
  23 + <stop stop-color="#F74A5C" offset="51.2635191%"></stop>
  24 + <stop stop-color="#F51D2C" offset="100%"></stop>
  25 + </linearGradient>
  26 + </defs>
  27 + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  28 + <g id="logo" transform="translate(-20.000000, -20.000000)">
  29 + <g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)">
  30 + <g id="Group-27-Copy-3">
  31 + <g id="Group-25" fill-rule="nonzero">
  32 + <g id="2">
  33 + <path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-1)"></path>
  34 + <path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-2)"></path>
  35 + </g>
  36 + <path d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z" id="Shape" fill="url(#linearGradient-3)"></path>
  37 + </g>
  38 + <ellipse id="Combined-Shape" fill="url(#linearGradient-4)" cx="100.519339" cy="100.436681" rx="23.6001926" ry="23.580786"></ellipse>
  39 + </g>
  40 + </g>
  41 + </g>
  42 + </g>
  43 +</svg>
1 -module.exports = {  
2 - navTheme: 'dark', // theme for nav menu  
3 - primaryColor: '#1890FF', // primary color of ant design  
4 - layout: 'sidemenu', // nav menu position: sidemenu or topmenu  
5 - contentWidth: 'Fluid', // layout of content: Fluid or Fixed, only works when layout is topmenu  
6 - fixedHeader: false, // sticky header  
7 - autoHideHeader: false, // auto hide header  
8 - fixSiderbar: false, // sticky siderbar  
9 -};  
@@ -2,8 +2,9 @@ import React from 'react'; @@ -2,8 +2,9 @@ import React from 'react';
2 import { formatMessage, FormattedMessage } from 'umi/locale'; 2 import { formatMessage, FormattedMessage } from 'umi/locale';
3 import { Button } from 'antd'; 3 import { Button } from 'antd';
4 import Link from 'umi/link'; 4 import Link from 'umi/link';
5 -import Result from 'ant-design-pro/lib/Result';  
6 -import styles from './RegisterResult.less'; 5 +import { Result } from 'ant-design-pro';
  6 +import styles from './style.less';
  7 +import UserLayout from './components/UserLayout';
7 8
8 const actions = ( 9 const actions = (
9 <div className={styles.actions}> 10 <div className={styles.actions}>
@@ -21,21 +22,23 @@ const actions = ( @@ -21,21 +22,23 @@ const actions = (
21 ); 22 );
22 23
23 const RegisterResult = ({ location }) => ( 24 const RegisterResult = ({ location }) => (
24 - <Result  
25 - className={styles.registerResult}  
26 - type="success"  
27 - title={  
28 - <div className={styles.title}>  
29 - <FormattedMessage  
30 - id="app.register-result.msg"  
31 - values={{ email: location.state ? location.state.account : 'AntDesign@example.com' }}  
32 - />  
33 - </div>  
34 - }  
35 - description={formatMessage({ id: 'app.register-result.activation-email' })}  
36 - actions={actions}  
37 - style={{ marginTop: 56 }}  
38 - /> 25 + <UserLayout>
  26 + <Result
  27 + className={styles.registerResult}
  28 + type="success"
  29 + title={
  30 + <div className={styles.title}>
  31 + <FormattedMessage
  32 + id="app.register-result.msg"
  33 + values={{ email: location.state ? location.state.account : 'AntDesign@example.com' }}
  34 + />
  35 + </div>
  36 + }
  37 + description={formatMessage({ id: 'app.register-result.activation-email' })}
  38 + actions={actions}
  39 + style={{ marginTop: 56 }}
  40 + />
  41 + </UserLayout>
39 ); 42 );
40 43
41 export default RegisterResult; 44 export default RegisterResult;
@@ -18,22 +18,5 @@ export default { @@ -18,22 +18,5 @@ export default {
18 'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.', 18 'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
19 'app.register-result.back-home': 'Back to home', 19 'app.register-result.back-home': 'Back to home',
20 'app.register-result.view-mailbox': 'View mailbox', 20 'app.register-result.view-mailbox': 'View mailbox',
21 - 'validation.email.required': 'Please enter your email!',  
22 - 'validation.email.wrong-format': 'The email address is in the wrong format!',  
23 - 'validation.userName.required': 'Please enter your userName!',  
24 - 'validation.password.required': 'Please enter your password!',  
25 - 'validation.password.twice': 'The passwords entered twice do not match!',  
26 - 'validation.password.strength.msg':  
27 - "Please enter at least 6 characters and don't use passwords that are easy to guess.",  
28 - 'validation.password.strength.strong': 'Strength: strong',  
29 - 'validation.password.strength.medium': 'Strength: medium',  
30 - 'validation.password.strength.short': 'Strength: too short',  
31 - 'validation.confirm-password.required': 'Please confirm your password!',  
32 - 'validation.phone-number.required': 'Please enter your phone number!',  
33 - 'validation.phone-number.wrong-format': 'Malformed phone number!',  
34 - 'validation.verification-code.required': 'Please enter the verification code!',  
35 - 'validation.title.required': 'Please enter a title',  
36 - 'validation.date.required': 'Please select the start and end date',  
37 - 'validation.goal.required': 'Please enter a description of the goal',  
38 - 'validation.standard.required': 'Please enter a metric', 21 + 'navBar.lang': 'Languages',
39 }; 22 };
@@ -18,21 +18,5 @@ export default { @@ -18,21 +18,5 @@ export default {
18 '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。', 18 '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
19 'app.register-result.back-home': '返回首页', 19 'app.register-result.back-home': '返回首页',
20 'app.register-result.view-mailbox': '查看邮箱', 20 'app.register-result.view-mailbox': '查看邮箱',
21 - 'validation.email.required': '请输入邮箱地址!',  
22 - 'validation.email.wrong-format': '邮箱地址格式错误!',  
23 - 'validation.userName.required': '请输入用户名!',  
24 - 'validation.password.required': '请输入密码!',  
25 - 'validation.password.twice': '两次输入的密码不匹配!',  
26 - 'validation.password.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',  
27 - 'validation.password.strength.strong': '强度:强',  
28 - 'validation.password.strength.medium': '强度:中',  
29 - 'validation.password.strength.short': '强度:太短',  
30 - 'validation.confirm-password.required': '请确认密码!',  
31 - 'validation.phone-number.required': '请输入手机号!',  
32 - 'validation.phone-number.wrong-format': '手机号格式错误!',  
33 - 'validation.verification-code.required': '请输入验证码!',  
34 - 'validation.title.required': '请输入标题',  
35 - 'validation.date.required': '请选择起止日期',  
36 - 'validation.goal.required': '请输入目标描述',  
37 - 'validation.standard.required': '请输入衡量标准', 21 + 'navBar.lang': '语言',
38 }; 22 };
@@ -18,21 +18,5 @@ export default { @@ -18,21 +18,5 @@ export default {
18 '激活郵件已發送到妳的郵箱中,郵件有效期為24小時。請及時登錄郵箱,點擊郵件中的鏈接激活帳戶。', 18 '激活郵件已發送到妳的郵箱中,郵件有效期為24小時。請及時登錄郵箱,點擊郵件中的鏈接激活帳戶。',
19 'app.register-result.back-home': '返回首頁', 19 'app.register-result.back-home': '返回首頁',
20 'app.register-result.view-mailbox': '查看郵箱', 20 'app.register-result.view-mailbox': '查看郵箱',
21 - 'validation.email.required': '請輸入郵箱地址!',  
22 - 'validation.email.wrong-format': '郵箱地址格式錯誤!',  
23 - 'validation.userName.required': '請輸入賬戶!',  
24 - 'validation.password.required': '請輸入密碼!',  
25 - 'validation.password.twice': '兩次輸入的密碼不匹配!',  
26 - 'validation.password.strength.msg': '請至少輸入 6 個字符。請不要使用容易被猜到的密碼。',  
27 - 'validation.password.strength.strong': '強度:強',  
28 - 'validation.password.strength.medium': '強度:中',  
29 - 'validation.password.strength.short': '強度:太短',  
30 - 'validation.confirm-password.required': '請確認密碼!',  
31 - 'validation.phone-number.required': '請輸入手機號!',  
32 - 'validation.phone-number.wrong-format': '手機號格式錯誤!',  
33 - 'validation.verification-code.required': '請輸入驗證碼!',  
34 - 'validation.title.required': '請輸入標題',  
35 - 'validation.date.required': '請選擇起止日期',  
36 - 'validation.goal.required': '請輸入目標描述',  
37 - 'validation.standard.required': '請輸入衡量標淮', 21 + 'navBar.lang': '語言',
38 }; 22 };
1 -import { routerRedux } from 'dva/router';  
2 -import { stringify } from 'qs';  
3 -import { fakeAccountLogin, getFakeCaptcha } from '@/services/api';  
4 -import { setAuthority } from '@/utils/authority';  
5 -import { getPageQuery } from '@/utils/utils';  
6 -import { reloadAuthorized } from '@/utils/Authorized';  
7 -  
8 -export default {  
9 - namespace: 'login',  
10 -  
11 - state: {  
12 - status: undefined,  
13 - },  
14 -  
15 - effects: {  
16 - *login({ payload }, { call, put }) {  
17 - const response = yield call(fakeAccountLogin, payload);  
18 - yield put({  
19 - type: 'changeLoginStatus',  
20 - payload: response,  
21 - });  
22 - // Login successfully  
23 - if (response.status === 'ok') {  
24 - reloadAuthorized();  
25 - const urlParams = new URL(window.location.href);  
26 - const params = getPageQuery();  
27 - let { redirect } = params;  
28 - if (redirect) {  
29 - const redirectUrlParams = new URL(redirect);  
30 - if (redirectUrlParams.origin === urlParams.origin) {  
31 - redirect = redirect.substr(urlParams.origin.length);  
32 - if (redirect.match(/^\/.*#/)) {  
33 - redirect = redirect.substr(redirect.indexOf('#') + 1);  
34 - }  
35 - } else {  
36 - window.location.href = redirect;  
37 - return;  
38 - }  
39 - }  
40 - yield put(routerRedux.replace(redirect || '/'));  
41 - }  
42 - },  
43 -  
44 - *getCaptcha({ payload }, { call }) {  
45 - yield call(getFakeCaptcha, payload);  
46 - },  
47 -  
48 - *logout(_, { put }) {  
49 - yield put({  
50 - type: 'changeLoginStatus',  
51 - payload: {  
52 - status: false,  
53 - currentAuthority: 'guest',  
54 - },  
55 - });  
56 - reloadAuthorized();  
57 - yield put(  
58 - routerRedux.push({  
59 - pathname: '/user/login',  
60 - search: stringify({  
61 - redirect: window.location.href,  
62 - }),  
63 - })  
64 - );  
65 - },  
66 - },  
67 -  
68 - reducers: {  
69 - changeLoginStatus(state, { payload }) {  
70 - setAuthority(payload.currentAuthority);  
71 - return {  
72 - ...state,  
73 - status: payload.status,  
74 - type: payload.type,  
75 - };  
76 - },  
77 - },  
78 -};  
1 -import { message } from 'antd';  
2 -import defaultSettings from '../defaultSettings';  
3 -  
4 -let lessNodesAppended;  
5 -const updateTheme = primaryColor => {  
6 - // Don't compile less in production!  
7 - if (APP_TYPE !== 'site') {  
8 - return;  
9 - }  
10 - // Determine if the component is remounted  
11 - if (!primaryColor) {  
12 - return;  
13 - }  
14 - const hideMessage = message.loading('正在编译主题!', 0);  
15 - function buildIt() {  
16 - if (!window.less) {  
17 - return;  
18 - }  
19 - setTimeout(() => {  
20 - window.less  
21 - .modifyVars({  
22 - '@primary-color': primaryColor,  
23 - })  
24 - .then(() => {  
25 - hideMessage();  
26 - })  
27 - .catch(() => {  
28 - message.error('Failed to update theme');  
29 - hideMessage();  
30 - });  
31 - }, 200);  
32 - }  
33 - if (!lessNodesAppended) {  
34 - // insert less.js and color.less  
35 - const lessStyleNode = document.createElement('link');  
36 - const lessConfigNode = document.createElement('script');  
37 - const lessScriptNode = document.createElement('script');  
38 - lessStyleNode.setAttribute('rel', 'stylesheet/less');  
39 - lessStyleNode.setAttribute('href', '/color.less');  
40 - lessConfigNode.innerHTML = `  
41 - window.less = {  
42 - async: true,  
43 - env: 'production',  
44 - javascriptEnabled: true  
45 - };  
46 - `;  
47 - lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js';  
48 - lessScriptNode.async = true;  
49 - lessScriptNode.onload = () => {  
50 - buildIt();  
51 - lessScriptNode.onload = null;  
52 - };  
53 - document.body.appendChild(lessStyleNode);  
54 - document.body.appendChild(lessConfigNode);  
55 - document.body.appendChild(lessScriptNode);  
56 - lessNodesAppended = true;  
57 - } else {  
58 - buildIt();  
59 - }  
60 -};  
61 -  
62 -const updateColorWeak = colorWeak => {  
63 - document.body.className = colorWeak ? 'colorWeak' : '';  
64 -};  
65 -  
66 -export default {  
67 - namespace: 'setting',  
68 - state: defaultSettings,  
69 - reducers: {  
70 - getSetting(state) {  
71 - const setting = {};  
72 - const urlParams = new URL(window.location.href);  
73 - Object.keys(state).forEach(key => {  
74 - if (urlParams.searchParams.has(key)) {  
75 - const value = urlParams.searchParams.get(key);  
76 - setting[key] = value === '1' ? true : value;  
77 - }  
78 - });  
79 - const { primaryColor, colorWeak } = setting;  
80 - if (state.primaryColor !== primaryColor) {  
81 - updateTheme(primaryColor);  
82 - }  
83 - updateColorWeak(colorWeak);  
84 - return {  
85 - ...state,  
86 - ...setting,  
87 - };  
88 - },  
89 - changeSetting(state, { payload }) {  
90 - const urlParams = new URL(window.location.href);  
91 - Object.keys(defaultSettings).forEach(key => {  
92 - if (urlParams.searchParams.has(key)) {  
93 - urlParams.searchParams.delete(key);  
94 - }  
95 - });  
96 - Object.keys(payload).forEach(key => {  
97 - if (key === 'collapse') {  
98 - return;  
99 - }  
100 - let value = payload[key];  
101 - if (value === true) {  
102 - value = 1;  
103 - }  
104 - if (defaultSettings[key] !== value) {  
105 - urlParams.searchParams.set(key, value);  
106 - }  
107 - });  
108 - const { primaryColor, colorWeak, contentWidth } = payload;  
109 - if (state.primaryColor !== primaryColor) {  
110 - updateTheme(primaryColor);  
111 - }  
112 - if (state.contentWidth !== contentWidth && window.dispatchEvent) {  
113 - window.dispatchEvent(new Event('resize'));  
114 - }  
115 - updateColorWeak(colorWeak);  
116 - window.history.replaceState(null, 'setting', urlParams.href);  
117 - return {  
118 - ...state,  
119 - ...payload,  
120 - };  
121 - },  
122 - },  
123 -};  
1 export default { 1 export default {
2 plugins: [ 2 plugins: [
3 - ['umi-plugin-block-dev', {}], 3 + ['umi-plugin-block-dev', {
  4 + layout: 'ant-design-pro',
  5 + }],
4 ['umi-plugin-react', { 6 ['umi-plugin-react', {
5 dva: true, 7 dva: true,
6 locale: true, 8 locale: true,
7 antd: true, 9 antd: true,
8 - }] 10 + }],
9 ], 11 ],
10 -} 12 +};
1 -import React, { PureComponent } from 'react';  
2 -import { connect } from 'dva';  
3 -import styles from './GridContent.less';  
4 -  
5 -class GridContent extends PureComponent {  
6 - render() {  
7 - const { contentWidth, children } = this.props;  
8 - let className = `${styles.main}`;  
9 - if (contentWidth === 'Fixed') {  
10 - className = `${styles.main} ${styles.wide}`;  
11 - }  
12 - return <div className={className}>{children}</div>;  
13 - }  
14 -}  
15 -  
16 -export default connect(({ setting }) => ({  
17 - contentWidth: setting.contentWidth,  
18 -}))(GridContent);  
1 -.main {  
2 - width: 100%;  
3 - height: 100%;  
4 - min-height: 100%;  
5 - transition: 0.3s;  
6 - &.wide {  
7 - max-width: 1200px;  
8 - margin: 0 auto;  
9 - }  
10 -}  
1 -import { createContext } from 'react';  
2 -  
3 -export default createContext();  
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 updateRule(params) {  
37 - return request('/api/rule', {  
38 - method: 'POST',  
39 - body: {  
40 - ...params,  
41 - method: 'update',  
42 - },  
43 - });  
44 -}  
45 -  
46 -export async function fakeSubmitForm(params) {  
47 - return request('/api/forms', {  
48 - method: 'POST',  
49 - body: params,  
50 - });  
51 -}  
52 -  
53 -export async function fakeChartData() {  
54 - return request('/api/fake_chart_data');  
55 -}  
56 -  
57 -export async function queryTags() {  
58 - return request('/api/tags');  
59 -}  
60 -  
61 -export async function queryBasicProfile() {  
62 - return request('/api/profile/basic');  
63 -}  
64 -  
65 -export async function queryAdvancedProfile() {  
66 - return request('/api/profile/advanced');  
67 -}  
68 -  
69 -export async function queryFakeList(params) {  
70 - return request(`/api/fake_list?${stringify(params)}`);  
71 -}  
72 -  
73 -export async function removeFakeList(params) {  
74 - const { count = 5, ...restParams } = params;  
75 - return request(`/api/fake_list?count=${count}`, {  
76 - method: 'POST',  
77 - body: {  
78 - ...restParams,  
79 - method: 'delete',  
80 - },  
81 - });  
82 -}  
83 -  
84 -export async function addFakeList(params) {  
85 - const { count = 5, ...restParams } = params;  
86 - return request(`/api/fake_list?count=${count}`, {  
87 - method: 'POST',  
88 - body: {  
89 - ...restParams,  
90 - method: 'post',  
91 - },  
92 - });  
93 -}  
94 -  
95 -export async function updateFakeList(params) {  
96 - const { count = 5, ...restParams } = params;  
97 - return request(`/api/fake_list?count=${count}`, {  
98 - method: 'POST',  
99 - body: {  
100 - ...restParams,  
101 - method: 'update',  
102 - },  
103 - });  
104 -}  
105 -  
106 -export async function fakeAccountLogin(params) {  
107 - return request('/api/login/account', {  
108 - method: 'POST',  
109 - body: params,  
110 - });  
111 -}  
112 -  
113 -export async function fakeRegister(params) {  
114 - return request('/api/register', {  
115 - method: 'POST',  
116 - body: params,  
117 - });  
118 -}  
119 -  
120 -export async function queryNotices() {  
121 - return request('/api/notices');  
122 -}  
123 -  
124 -export async function getFakeCaptcha(mobile) {  
125 - return request(`/api/captcha?mobile=${mobile}`);  
126 -}  
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 -}  
1 -import fetch from 'dva/fetch';  
2 -import { notification } from 'antd';  
3 -import router from 'umi/router';  
4 -import hash from 'hash.js';  
5 -import { isAntdPro } from './utils';  
6 -  
7 -const codeMessage = {  
8 - 200: '服务器成功返回请求的数据。',  
9 - 201: '新建或修改数据成功。',  
10 - 202: '一个请求已经进入后台排队(异步任务)。',  
11 - 204: '删除数据成功。',  
12 - 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',  
13 - 401: '用户没有权限(令牌、用户名、密码错误)。',  
14 - 403: '用户得到授权,但是访问是被禁止的。',  
15 - 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',  
16 - 406: '请求的格式不可得。',  
17 - 410: '请求的资源被永久删除,且不会再得到的。',  
18 - 422: '当创建一个对象时,发生一个验证错误。',  
19 - 500: '服务器发生错误,请检查服务器。',  
20 - 502: '网关错误。',  
21 - 503: '服务不可用,服务器暂时过载或维护。',  
22 - 504: '网关超时。',  
23 -};  
24 -  
25 -const checkStatus = response => {  
26 - if (response.status >= 200 && response.status < 300) {  
27 - return response;  
28 - }  
29 - const errortext = codeMessage[response.status] || response.statusText;  
30 - notification.error({  
31 - message: `请求错误 ${response.status}: ${response.url}`,  
32 - description: errortext,  
33 - });  
34 - const error = new Error(errortext);  
35 - error.name = response.status;  
36 - error.response = response;  
37 - throw error;  
38 -};  
39 -  
40 -const cachedSave = (response, hashcode) => {  
41 - /**  
42 - * Clone a response data and store it in sessionStorage  
43 - * Does not support data other than json, Cache only json  
44 - */  
45 - const contentType = response.headers.get('Content-Type');  
46 - if (contentType && contentType.match(/application\/json/i)) {  
47 - // All data is saved as text  
48 - response  
49 - .clone()  
50 - .text()  
51 - .then(content => {  
52 - sessionStorage.setItem(hashcode, content);  
53 - sessionStorage.setItem(`${hashcode}:timestamp`, Date.now());  
54 - });  
55 - }  
56 - return response;  
57 -};  
58 -  
59 -/**  
60 - * Requests a URL, returning a promise.  
61 - *  
62 - * @param {string} url The URL we want to request  
63 - * @param {object} [option] The options we want to pass to "fetch"  
64 - * @return {object} An object containing either "data" or "err"  
65 - */  
66 -export default function request(url, option) {  
67 - const options = {  
68 - expirys: isAntdPro(),  
69 - ...option,  
70 - };  
71 - /**  
72 - * Produce fingerprints based on url and parameters  
73 - * Maybe url has the same parameters  
74 - */  
75 - const fingerprint = url + (options.body ? JSON.stringify(options.body) : '');  
76 - const hashcode = hash  
77 - .sha256()  
78 - .update(fingerprint)  
79 - .digest('hex');  
80 -  
81 - const defaultOptions = {  
82 - credentials: 'include',  
83 - };  
84 - const newOptions = { ...defaultOptions, ...options };  
85 - if (  
86 - newOptions.method === 'POST' ||  
87 - newOptions.method === 'PUT' ||  
88 - newOptions.method === 'DELETE'  
89 - ) {  
90 - if (!(newOptions.body instanceof FormData)) {  
91 - newOptions.headers = {  
92 - Accept: 'application/json',  
93 - 'Content-Type': 'application/json; charset=utf-8',  
94 - ...newOptions.headers,  
95 - };  
96 - newOptions.body = JSON.stringify(newOptions.body);  
97 - } else {  
98 - // newOptions.body is FormData  
99 - newOptions.headers = {  
100 - Accept: 'application/json',  
101 - ...newOptions.headers,  
102 - };  
103 - }  
104 - }  
105 -  
106 - const expirys = options.expirys && 60;  
107 - // options.expirys !== false, return the cache,  
108 - if (options.expirys !== false) {  
109 - const cached = sessionStorage.getItem(hashcode);  
110 - const whenCached = sessionStorage.getItem(`${hashcode}:timestamp`);  
111 - if (cached !== null && whenCached !== null) {  
112 - const age = (Date.now() - whenCached) / 1000;  
113 - if (age < expirys) {  
114 - const response = new Response(new Blob([cached]));  
115 - return response.json();  
116 - }  
117 - sessionStorage.removeItem(hashcode);  
118 - sessionStorage.removeItem(`${hashcode}:timestamp`);  
119 - }  
120 - }  
121 - return fetch(url, newOptions)  
122 - .then(checkStatus)  
123 - .then(response => cachedSave(response, hashcode))  
124 - .then(response => {  
125 - // DELETE and 204 do not return data by default  
126 - // using .json will report an error.  
127 - if (newOptions.method === 'DELETE' || response.status === 204) {  
128 - return response.text();  
129 - }  
130 - return response.json();  
131 - })  
132 - .catch(e => {  
133 - const status = e.name;  
134 - if (status === 401) {  
135 - // @HACK  
136 - /* eslint-disable no-underscore-dangle */  
137 - window.g_app._store.dispatch({  
138 - type: 'login/logout',  
139 - });  
140 - return;  
141 - }  
142 - // environment should not be used  
143 - if (status === 403) {  
144 - router.push('/exception/403');  
145 - return;  
146 - }  
147 - if (status <= 504 && status >= 500) {  
148 - router.push('/exception/500');  
149 - return;  
150 - }  
151 - if (status >= 404 && status < 422) {  
152 - router.push('/exception/404');  
153 - }  
154 - });  
155 -}  
1 -import moment from 'moment';  
2 -import React from 'react';  
3 -import nzh from 'nzh/cn';  
4 -import { parse, stringify } from 'qs';  
5 -  
6 -export function fixedZero(val) {  
7 - return val * 1 < 10 ? `0${val}` : val;  
8 -}  
9 -  
10 -export function getTimeDistance(type) {  
11 - const now = new Date();  
12 - const oneDay = 1000 * 60 * 60 * 24;  
13 -  
14 - if (type === 'today') {  
15 - now.setHours(0);  
16 - now.setMinutes(0);  
17 - now.setSeconds(0);  
18 - return [moment(now), moment(now.getTime() + (oneDay - 1000))];  
19 - }  
20 -  
21 - if (type === 'week') {  
22 - let day = now.getDay();  
23 - now.setHours(0);  
24 - now.setMinutes(0);  
25 - now.setSeconds(0);  
26 -  
27 - if (day === 0) {  
28 - day = 6;  
29 - } else {  
30 - day -= 1;  
31 - }  
32 -  
33 - const beginTime = now.getTime() - day * oneDay;  
34 -  
35 - return [moment(beginTime), moment(beginTime + (7 * oneDay - 1000))];  
36 - }  
37 -  
38 - if (type === 'month') {  
39 - const year = now.getFullYear();  
40 - const month = now.getMonth();  
41 - const nextDate = moment(now).add(1, 'months');  
42 - const nextYear = nextDate.year();  
43 - const nextMonth = nextDate.month();  
44 -  
45 - return [  
46 - moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`),  
47 - moment(moment(`${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`).valueOf() - 1000),  
48 - ];  
49 - }  
50 -  
51 - const year = now.getFullYear();  
52 - return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)];  
53 -}  
54 -  
55 -export function getPlainNode(nodeList, parentPath = '') {  
56 - const arr = [];  
57 - nodeList.forEach(node => {  
58 - const item = node;  
59 - item.path = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/');  
60 - item.exact = true;  
61 - if (item.children && !item.component) {  
62 - arr.push(...getPlainNode(item.children, item.path));  
63 - } else {  
64 - if (item.children && item.component) {  
65 - item.exact = false;  
66 - }  
67 - arr.push(item);  
68 - }  
69 - });  
70 - return arr;  
71 -}  
72 -  
73 -export function digitUppercase(n) {  
74 - return nzh.toMoney(n);  
75 -}  
76 -  
77 -function getRelation(str1, str2) {  
78 - if (str1 === str2) {  
79 - console.warn('Two path are equal!'); // eslint-disable-line  
80 - }  
81 - const arr1 = str1.split('/');  
82 - const arr2 = str2.split('/');  
83 - if (arr2.every((item, index) => item === arr1[index])) {  
84 - return 1;  
85 - }  
86 - if (arr1.every((item, index) => item === arr2[index])) {  
87 - return 2;  
88 - }  
89 - return 3;  
90 -}  
91 -  
92 -function getRenderArr(routes) {  
93 - let renderArr = [];  
94 - renderArr.push(routes[0]);  
95 - for (let i = 1; i < routes.length; i += 1) {  
96 - // 去重  
97 - renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1);  
98 - // 是否包含  
99 - const isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);  
100 - if (isAdd) {  
101 - renderArr.push(routes[i]);  
102 - }  
103 - }  
104 - return renderArr;  
105 -}  
106 -  
107 -/**  
108 - * Get router routing configuration  
109 - * { path:{name,...param}}=>Array<{name,path ...param}>  
110 - * @param {string} path  
111 - * @param {routerData} routerData  
112 - */  
113 -export function getRoutes(path, routerData) {  
114 - let routes = Object.keys(routerData).filter(  
115 - routePath => routePath.indexOf(path) === 0 && routePath !== path  
116 - );  
117 - // Replace path to '' eg. path='user' /user/name => name  
118 - routes = routes.map(item => item.replace(path, ''));  
119 - // Get the route to be rendered to remove the deep rendering  
120 - const renderArr = getRenderArr(routes);  
121 - // Conversion and stitching parameters  
122 - const renderRoutes = renderArr.map(item => {  
123 - const exact = !routes.some(route => route !== item && getRelation(route, item) === 1);  
124 - return {  
125 - exact,  
126 - ...routerData[`${path}${item}`],  
127 - key: `${path}${item}`,  
128 - path: `${path}${item}`,  
129 - };  
130 - });  
131 - return renderRoutes;  
132 -}  
133 -  
134 -export function getPageQuery() {  
135 - return parse(window.location.href.split('?')[1]);  
136 -}  
137 -  
138 -export function getQueryPath(path = '', query = {}) {  
139 - const search = stringify(query);  
140 - if (search.length) {  
141 - return `${path}?${search}`;  
142 - }  
143 - return path;  
144 -}  
145 -  
146 -/* eslint no-useless-escape:0 */  
147 -const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;  
148 -  
149 -export function isUrl(path) {  
150 - return reg.test(path);  
151 -}  
152 -  
153 -export function formatWan(val) {  
154 - const v = val * 1;  
155 - if (!v || Number.isNaN(v)) return '';  
156 -  
157 - let result = val;  
158 - if (val > 10000) {  
159 - result = Math.floor(val / 10000);  
160 - result = (  
161 - <span>  
162 - {result}  
163 - <span  
164 - style={{  
165 - position: 'relative',  
166 - top: -2,  
167 - fontSize: 14,  
168 - fontStyle: 'normal',  
169 - marginLeft: 2,  
170 - }}  
171 - >  
172 -  
173 - </span>  
174 - </span>  
175 - );  
176 - }  
177 - return result;  
178 -}  
179 -  
180 -// 给官方演示站点用,用于关闭真实开发环境不需要使用的特性  
181 -export function isAntdPro() {  
182 - return window.location.hostname === 'preview.pro.ant.design';  
183 -}  
@@ -11,21 +11,18 @@ @@ -11,21 +11,18 @@
11 "url": "https://github.com/umijs/umi-blocks/workplace" 11 "url": "https://github.com/umijs/umi-blocks/workplace"
12 }, 12 },
13 "dependencies": { 13 "dependencies": {
14 - "react": "^16.6.3",  
15 - "moment": "^2.22.2",  
16 - "dva": "^2.4.0",  
17 - "antd": "^3.10.9",  
18 "ant-design-pro": "^2.1.1", 14 "ant-design-pro": "^2.1.1",
  15 + "antd": "^3.10.9",
  16 + "dva": "^2.4.0",
  17 + "moment": "^2.22.2",
19 "prop-types": "^15.5.10", 18 "prop-types": "^15.5.10",
20 - "qs": "^6.6.0",  
21 - "hash.js": "^1.1.5",  
22 - "nzh": "^1.0.3",  
23 - "mockjs": "*" 19 + "react": "^16.6.3",
  20 + "umi-request": "^1.0.0"
24 }, 21 },
25 "devDependencies": { 22 "devDependencies": {
26 "umi": "^2.3.0-beta.1", 23 "umi": "^2.3.0-beta.1",
27 - "umi-plugin-react": "^1.3.0-beta.1",  
28 - "umi-plugin-block-dev": "^1.0.0" 24 + "umi-plugin-block-dev": "^1.0.0",
  25 + "umi-plugin-react": "^1.3.0-beta.1"
29 }, 26 },
30 - "license": "ISC" 27 + "license": "MIT"
31 } 28 }
1 -import mockjs from 'mockjs';  
2 -  
3 const titles = [ 1 const titles = [
4 'Alipay', 2 'Alipay',
5 'Angular', 3 'Angular',
@@ -34,125 +32,6 @@ const avatars2 = [ @@ -34,125 +32,6 @@ const avatars2 = [
34 'https://gw.alipayobjects.com/zos/rmsportal/UrQsqscbKEpNuJcvBZBu.png', 32 'https://gw.alipayobjects.com/zos/rmsportal/UrQsqscbKEpNuJcvBZBu.png',
35 ]; 33 ];
36 34
37 -const covers = [  
38 - 'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png',  
39 - 'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png',  
40 - 'https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png',  
41 - 'https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png',  
42 -];  
43 -const desc = [  
44 - '那是一种内在的东西, 他们到达不了,也无法触及的',  
45 - '希望是一个好东西,也许是最好的,好东西是不会消亡的',  
46 - '生命就像一盒巧克力,结果往往出人意料',  
47 - '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',  
48 - '那时候我只会想自己想要什么,从不想自己拥有什么',  
49 -];  
50 -  
51 -const user = [  
52 - '付小小',  
53 - '曲丽丽',  
54 - '林东东',  
55 - '周星星',  
56 - '吴加好',  
57 - '朱偏右',  
58 - '鱼酱',  
59 - '乐哥',  
60 - '谭小仪',  
61 - '仲尼',  
62 -];  
63 -  
64 -function fakeList(count) {  
65 - const list = [];  
66 - for (let i = 0; i < count; i += 1) {  
67 - list.push({  
68 - id: `fake-list-${i}`,  
69 - owner: user[i % 10],  
70 - title: titles[i % 8],  
71 - avatar: avatars[i % 8],  
72 - cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],  
73 - status: ['active', 'exception', 'normal'][i % 3],  
74 - percent: Math.ceil(Math.random() * 50) + 50,  
75 - logo: avatars[i % 8],  
76 - href: 'https://ant.design',  
77 - updatedAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),  
78 - createdAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),  
79 - subDescription: desc[i % 5],  
80 - description:  
81 - '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',  
82 - activeUser: Math.ceil(Math.random() * 100000) + 100000,  
83 - newUser: Math.ceil(Math.random() * 1000) + 1000,  
84 - star: Math.ceil(Math.random() * 100) + 100,  
85 - like: Math.ceil(Math.random() * 100) + 100,  
86 - message: Math.ceil(Math.random() * 10) + 10,  
87 - content:  
88 - '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。',  
89 - members: [  
90 - {  
91 - avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png',  
92 - name: '曲丽丽',  
93 - id: 'member1',  
94 - },  
95 - {  
96 - avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png',  
97 - name: '王昭君',  
98 - id: 'member2',  
99 - },  
100 - {  
101 - avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png',  
102 - name: '董娜娜',  
103 - id: 'member3',  
104 - },  
105 - ],  
106 - });  
107 - }  
108 -  
109 - return list;  
110 -}  
111 -  
112 -let sourceData;  
113 -  
114 -function getFakeList(req, res) {  
115 - const params = req.query;  
116 -  
117 - const count = params.count * 1 || 20;  
118 -  
119 - const result = fakeList(count);  
120 - sourceData = result;  
121 - return res.json(result);  
122 -}  
123 -  
124 -function postFakeList(req, res) {  
125 - const { /* url = '', */ body } = req;  
126 - // const params = getUrlParams(url);  
127 - const { method, id } = body;  
128 - // const count = (params.count * 1) || 20;  
129 - let result = sourceData;  
130 -  
131 - switch (method) {  
132 - case 'delete':  
133 - result = result.filter(item => item.id !== id);  
134 - break;  
135 - case 'update':  
136 - result.forEach((item, i) => {  
137 - if (item.id === id) {  
138 - result[i] = Object.assign(item, body);  
139 - }  
140 - });  
141 - break;  
142 - case 'post':  
143 - result.unshift({  
144 - body,  
145 - id: `fake-list-${result.length}`,  
146 - createdAt: new Date().getTime(),  
147 - });  
148 - break;  
149 - default:  
150 - break;  
151 - }  
152 -  
153 - return res.json(result);  
154 -}  
155 -  
156 const getNotice = [ 35 const getNotice = [
157 { 36 {
158 id: 'xxx1', 37 id: 'xxx1',
@@ -317,20 +196,108 @@ const getActivities = [ @@ -317,20 +196,108 @@ const getActivities = [
317 }, 196 },
318 ]; 197 ];
319 198
320 -function getFakeCaptcha(req, res) {  
321 - return res.json('captcha-xxx');  
322 -} 199 +const radarOriginData = [
  200 + {
  201 + name: '个人',
  202 + ref: 10,
  203 + koubei: 8,
  204 + output: 4,
  205 + contribute: 5,
  206 + hot: 7,
  207 + },
  208 + {
  209 + name: '团队',
  210 + ref: 3,
  211 + koubei: 9,
  212 + output: 6,
  213 + contribute: 3,
  214 + hot: 1,
  215 + },
  216 + {
  217 + name: '部门',
  218 + ref: 4,
  219 + koubei: 1,
  220 + output: 6,
  221 + contribute: 5,
  222 + hot: 7,
  223 + },
  224 +];
  225 +
  226 +const radarData = [];
  227 +const radarTitleMap = {
  228 + ref: '引用',
  229 + koubei: '口碑',
  230 + output: '产量',
  231 + contribute: '贡献',
  232 + hot: '热度',
  233 +};
  234 +radarOriginData.forEach(item => {
  235 + Object.keys(item).forEach(key => {
  236 + if (key !== 'name') {
  237 + radarData.push({
  238 + name: item.name,
  239 + label: radarTitleMap[key],
  240 + value: item[key],
  241 + });
  242 + }
  243 + });
  244 +});
323 245
324 export default { 246 export default {
325 'GET /api/project/notice': getNotice, 247 'GET /api/project/notice': getNotice,
326 'GET /api/activities': getActivities, 248 'GET /api/activities': getActivities,
327 - 'POST /api/forms': (req, res) => {  
328 - res.send({ message: 'Ok' }); 249 + 'GET /api/fake_chart_data': {
  250 + radarData,
  251 + },
  252 +
  253 + 'GET /api/currentUser': {
  254 + name: 'Serati Ma',
  255 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
  256 + userid: '00000001',
  257 + email: 'antdesign@alipay.com',
  258 + signature: '海纳百川,有容乃大',
  259 + title: '交互专家',
  260 + group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
  261 + tags: [
  262 + {
  263 + key: '0',
  264 + label: '很有想法的',
  265 + },
  266 + {
  267 + key: '1',
  268 + label: '专注设计',
  269 + },
  270 + {
  271 + key: '2',
  272 + label: '辣~',
  273 + },
  274 + {
  275 + key: '3',
  276 + label: '大长腿',
  277 + },
  278 + {
  279 + key: '4',
  280 + label: '川妹子',
  281 + },
  282 + {
  283 + key: '5',
  284 + label: '海纳百川',
  285 + },
  286 + ],
  287 + notifyCount: 12,
  288 + unreadCount: 11,
  289 + country: 'China',
  290 + geographic: {
  291 + province: {
  292 + label: '浙江省',
  293 + key: '330000',
  294 + },
  295 + city: {
  296 + label: '杭州市',
  297 + key: '330100',
  298 + },
  299 + },
  300 + address: '西湖区工专路 77 号',
  301 + phone: '0752-268888888',
329 }, 302 },
330 - 'GET /api/tags': mockjs.mock({  
331 - 'list|100': [{ name: '@city', 'value|1-100': 150, 'type|0-2': 1 }],  
332 - }),  
333 - 'GET /api/fake_list': getFakeList,  
334 - 'POST /api/fake_list': postFakeList,  
335 - 'GET /api/captcha': getFakeCaptcha,  
336 }; 303 };
1 import React from 'react'; 1 import React from 'react';
2 import { FormattedMessage } from 'umi/locale'; 2 import { FormattedMessage } from 'umi/locale';
3 import Link from 'umi/link'; 3 import Link from 'umi/link';
4 -import PageHeader from 'ant-design-pro/lib/PageHeader';  
5 -import { connect } from 'dva';  
6 -import GridContent from './GridContent'; 4 +import { PageHeader } from 'ant-design-pro';
7 import styles from './index.less'; 5 import styles from './index.less';
8 -import MenuContext from '@/layouts/MenuContext';  
9 6
10 const PageHeaderWrapper = ({ children, contentWidth, wrapperClassName, top, ...restProps }) => ( 7 const PageHeaderWrapper = ({ children, contentWidth, wrapperClassName, top, ...restProps }) => (
11 <div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}> 8 <div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}>
12 {top} 9 {top}
13 - <MenuContext.Consumer>  
14 - {value => (  
15 - <PageHeader  
16 - wide={contentWidth === 'Fixed'}  
17 - home={<FormattedMessage id="menu.home" defaultMessage="Home" />}  
18 - {...value}  
19 - key="pageheader"  
20 - {...restProps}  
21 - linkElement={Link}  
22 - itemRender={item => {  
23 - if (item.locale) {  
24 - return <FormattedMessage id={item.locale} defaultMessage={item.title} />;  
25 - }  
26 - return item.title;  
27 - }}  
28 - />  
29 - )}  
30 - </MenuContext.Consumer> 10 + <PageHeader
  11 + wide={contentWidth === 'Fixed'}
  12 + home={<FormattedMessage id="menu.home" defaultMessage="Home" />}
  13 + key="pageheader"
  14 + {...restProps}
  15 + linkElement={Link}
  16 + itemRender={item => {
  17 + if (item.locale) {
  18 + return <FormattedMessage id={item.locale} defaultMessage={item.title} />;
  19 + }
  20 + return item.title;
  21 + }}
  22 + />
31 {children ? ( 23 {children ? (
32 <div className={styles.content}> 24 <div className={styles.content}>
33 - <GridContent>{children}</GridContent> 25 + { children }
34 </div> 26 </div>
35 ) : null} 27 ) : null}
36 </div> 28 </div>
37 ); 29 );
38 30
39 -export default connect(({ setting }) => ({  
40 - contentWidth: setting.contentWidth,  
41 -}))(PageHeaderWrapper); 31 +export default PageHeaderWrapper;
1 -module.exports = {  
2 - navTheme: 'dark', // theme for nav menu  
3 - primaryColor: '#1890FF', // primary color of ant design  
4 - layout: 'sidemenu', // nav menu position: sidemenu or topmenu  
5 - contentWidth: 'Fluid', // layout of content: Fluid or Fixed, only works when layout is topmenu  
6 - fixedHeader: false, // sticky header  
7 - autoHideHeader: false, // auto hide header  
8 - fixSiderbar: false, // sticky siderbar  
9 -};  
@@ -4,11 +4,13 @@ import { connect } from 'dva'; @@ -4,11 +4,13 @@ import { connect } from 'dva';
4 import Link from 'umi/link'; 4 import Link from 'umi/link';
5 import { Row, Col, Card, List, Avatar } from 'antd'; 5 import { Row, Col, Card, List, Avatar } from 'antd';
6 6
7 -import { Radar } from 'ant-design-pro/lib/Charts';  
8 -import EditableLinkGroup from '@/components/EditableLinkGroup';  
9 -import PageHeaderWrapper from '@/components/PageHeaderWrapper'; 7 +import { Charts } from 'ant-design-pro';
  8 +import EditableLinkGroup from './components/EditableLinkGroup';
  9 +import PageHeaderWrapper from './components/PageHeaderWrapper';
10 10
11 -import styles from './Workplace.less'; 11 +import styles from './style.less';
  12 +
  13 +const { Radar } = Charts;
12 14
13 const links = [ 15 const links = [
14 { 16 {
@@ -37,29 +39,20 @@ const links = [ @@ -37,29 +39,20 @@ const links = [
37 }, 39 },
38 ]; 40 ];
39 41
40 -@connect(({ user, project, activities, chart, loading }) => ({ 42 +@connect(({ workplace: { user, project, activities, chart }, loading }) => ({
41 currentUser: user.currentUser, 43 currentUser: user.currentUser,
42 project, 44 project,
43 activities, 45 activities,
44 chart, 46 chart,
45 - currentUserLoading: loading.effects['user/fetchCurrent'],  
46 - projectLoading: loading.effects['project/fetchNotice'],  
47 - activitiesLoading: loading.effects['activities/fetchList'], 47 + currentUserLoading: loading.effects['workplace/fetchUserCurrent'],
  48 + projectLoading: loading.effects['workplace/fetchProjectNotice'],
  49 + activitiesLoading: loading.effects['workplace/fetchActivitiesList'],
48 })) 50 }))
49 class Workplace extends PureComponent { 51 class Workplace extends PureComponent {
50 componentDidMount() { 52 componentDidMount() {
51 const { dispatch } = this.props; 53 const { dispatch } = this.props;
52 dispatch({ 54 dispatch({
53 - type: 'user/fetchCurrent',  
54 - });  
55 - dispatch({  
56 - type: 'project/fetchNotice',  
57 - });  
58 - dispatch({  
59 - type: 'activities/fetchList',  
60 - });  
61 - dispatch({  
62 - type: 'chart/fetch', 55 + type: 'workplace/init',
63 }); 56 });
64 } 57 }
65 58
1 -import { fakeChartData } from '@/services/api'; 1 +import { queryCurrent, queryProjectNotice, queryActivities, fakeChartData } from './service';
2 2
3 export default { 3 export default {
4 - namespace: 'chart',  
5 - 4 + namespace: 'workplace',
6 state: { 5 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 - }); 6 + user: {
  7 + currentUser: {},
27 }, 8 },
28 - *fetchSalesData(_, { call, put }) {  
29 - const response = yield call(fakeChartData);  
30 - yield put({  
31 - type: 'save',  
32 - payload: {  
33 - salesData: response.salesData,  
34 - },  
35 - }); 9 + project: {
  10 + notice: [],
  11 + },
  12 + activities: {
  13 + list: [],
  14 + },
  15 + chart: {
  16 + visitData: [],
  17 + visitData2: [],
  18 + salesData: [],
  19 + searchData: [],
  20 + offlineData: [],
  21 + offlineChartData: [],
  22 + salesTypeData: [],
  23 + salesTypeDataOnline: [],
  24 + salesTypeDataOffline: [],
  25 + radarData: [],
  26 + loading: false,
36 }, 27 },
37 }, 28 },
38 -  
39 reducers: { 29 reducers: {
40 - save(state, { payload }) { 30 + saveCurrentUser(state, action) {
41 return { 31 return {
42 ...state, 32 ...state,
43 - ...payload, 33 + user: {
  34 + currentUser: action.payload || {},
  35 + },
44 }; 36 };
45 }, 37 },
46 - clear() { 38 + saveNotice(state, action) {
47 return { 39 return {
48 - visitData: [],  
49 - visitData2: [],  
50 - salesData: [],  
51 - searchData: [],  
52 - offlineData: [],  
53 - offlineChartData: [],  
54 - salesTypeData: [],  
55 - salesTypeDataOnline: [],  
56 - salesTypeDataOffline: [],  
57 - radarData: [], 40 + ...state,
  41 + project: {
  42 + notice: action.payload,
  43 + },
58 }; 44 };
59 }, 45 },
  46 + saveList(state, action) {
  47 + return {
  48 + ...state,
  49 + activities: {
  50 + list: action.payload,
  51 + },
  52 + };
  53 + },
  54 + saveChart(state, { payload }) {
  55 + return {
  56 + ...state,
  57 + chart: {
  58 + ...payload,
  59 + },
  60 + };
  61 + },
  62 + clear(state) {
  63 + return {
  64 + ...state,
  65 + chart: {
  66 + visitData: [],
  67 + visitData2: [],
  68 + salesData: [],
  69 + searchData: [],
  70 + offlineData: [],
  71 + offlineChartData: [],
  72 + salesTypeData: [],
  73 + salesTypeDataOnline: [],
  74 + salesTypeDataOffline: [],
  75 + radarData: [],
  76 + },
  77 + };
  78 + },
  79 + },
  80 + effects: {
  81 + *init(_, { put }) {
  82 + yield put({ type: 'fetchUserCurrent' });
  83 + yield put({ type: 'fetchProjectNotice' });
  84 + yield put({ type: 'fetchActivitiesList' });
  85 + yield put({ type: 'fetchChart' });
  86 + },
  87 + *fetchUserCurrent(_, { call, put }) {
  88 + const response = yield call(queryCurrent);
  89 + yield put({
  90 + type: 'saveCurrentUser',
  91 + payload: response,
  92 + });
  93 + },
  94 + *fetchProjectNotice(_, { call, put }) {
  95 + const response = yield call(queryProjectNotice);
  96 + yield put({
  97 + type: 'saveNotice',
  98 + payload: Array.isArray(response) ? response : [],
  99 + });
  100 + },
  101 + *fetchActivitiesList(_, { call, put }) {
  102 + const response = yield call(queryActivities);
  103 + yield put({
  104 + type: 'saveList',
  105 + payload: Array.isArray(response) ? response : [],
  106 + });
  107 + },
  108 + *fetchChart(_, { call, put }) {
  109 + const response = yield call(fakeChartData);
  110 + yield put({
  111 + type: 'saveChart',
  112 + payload: response,
  113 + });
  114 + },
60 }, 115 },
61 -}; 116 +}
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 -};  
注册登录 后发表评论