提交 dece795dc5d0399f0204091cbb8a6572a4af513a

作者 陈帅
1 个父辈 fc474140

Migrate User Center

正在显示 84 个修改的文件 包含 152 行增加4003 行删除
@@ -67,3 +67,4 @@ pages @@ -67,3 +67,4 @@ pages
67 67
68 # MacOS 68 # MacOS
69 .DS_Store 69 .DS_Store
  70 +yarn.lock
1 export default { 1 export default {
2 plugins: [ 2 plugins: [
3 - ['umi-plugin-block-dev', {}],  
4 - ['umi-plugin-react', {  
5 - dva: true,  
6 - locale: true,  
7 - antd: true,  
8 - }] 3 + [
  4 + 'umi-plugin-block-dev',
  5 + {
  6 + layout: 'ant-design-pro',
  7 + },
  8 + ],
  9 + [
  10 + 'umi-plugin-react',
  11 + {
  12 + dva: true,
  13 + locale: true,
  14 + antd: true,
  15 + },
  16 + ],
9 ], 17 ],
10 -} 18 +};
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 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 -}  
  1 +{
  2 + "compilerOptions": {
  3 + "emitDecoratorMetadata": true,
  4 + "experimentalDecorators": true,
  5 + "baseUrl": ".",
  6 + "paths": {
  7 + "@/*": ["./src/*"]
  8 + }
  9 + }
  10 +}
@@ -11,18 +11,19 @@ @@ -11,18 +11,19 @@
11 "url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettings" 11 "url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettings"
12 }, 12 },
13 "dependencies": { 13 "dependencies": {
14 - "react": "^16.6.3",  
15 - "dva": "^2.4.0",  
16 "antd": "^3.10.9", 14 "antd": "^3.10.9",
  15 + "dva": "^2.4.0",
17 "hash.js": "^1.1.5", 16 "hash.js": "^1.1.5",
18 "moment": "^2.22.2", 17 "moment": "^2.22.2",
19 "nzh": "^1.0.3", 18 "nzh": "^1.0.3",
20 - "qs": "^6.6.0" 19 + "qs": "^6.6.0",
  20 + "react": "^16.6.3",
  21 + "umi-request": "^1.0.0"
21 }, 22 },
22 "devDependencies": { 23 "devDependencies": {
23 "umi": "^2.3.0-beta.1", 24 "umi": "^2.3.0-beta.1",
24 - "umi-plugin-react": "^1.3.0-beta.1",  
25 - "umi-plugin-block-dev": "^1.0.0" 25 + "umi-plugin-block-dev": "^1.0.0",
  26 + "umi-plugin-react": "^1.3.0-beta.1"
26 }, 27 },
27 "license": "ISC" 28 "license": "ISC"
28 } 29 }
  1 +import city from './geographic/city.json';
  2 +import province from './geographic/province.json';
  3 +
  4 +function getProvince(req, res) {
  5 + return res.json(province);
  6 +}
  7 +
  8 +function getCity(req, res) {
  9 + return res.json(city[req.params.province]);
  10 +}
1 // 代码中会兼容本地 service mock 以及部署站点的静态数据 11 // 代码中会兼容本地 service mock 以及部署站点的静态数据
2 export default { 12 export default {
3 // 支持值为 Object 和 Array 13 // 支持值为 Object 和 Array
@@ -51,88 +61,6 @@ export default { @@ -51,88 +61,6 @@ export default {
51 address: '西湖区工专路 77 号', 61 address: '西湖区工专路 77 号',
52 phone: '0752-268888888', 62 phone: '0752-268888888',
53 }, 63 },
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 - }, 64 + 'GET /api/geographic/province': getProvince,
  65 + 'GET /api/geographic/city/:province': getCity,
138 }; 66 };
@@ -3,8 +3,8 @@ import { formatMessage, FormattedMessage } from 'umi/locale'; @@ -3,8 +3,8 @@ import { formatMessage, FormattedMessage } from 'umi/locale';
3 import { Form, Input, Upload, Select, Button } from 'antd'; 3 import { Form, Input, Upload, Select, Button } from 'antd';
4 import { connect } from 'dva'; 4 import { connect } from 'dva';
5 import styles from './BaseView.less'; 5 import styles from './BaseView.less';
6 -import GeographicView from './GeographicView';  
7 -import PhoneView from './PhoneView'; 6 +import GeographicView from './components/GeographicView';
  7 +import PhoneView from './components/PhoneView';
8 // import { getTimeDistance } from '@/utils/utils'; 8 // import { getTimeDistance } from '@/utils/utils';
9 9
10 const FormItem = Form.Item; 10 const FormItem = Form.Item;
@@ -10,19 +10,19 @@ const nullSlectItem = { @@ -10,19 +10,19 @@ const nullSlectItem = {
10 key: '', 10 key: '',
11 }; 11 };
12 12
13 -@connect(({ geographic }) => {  
14 - const { province, isLoading, city } = geographic; 13 +@connect(({ user, loading }) => {
  14 + const { province, city } = user;
15 return { 15 return {
16 province, 16 province,
17 city, 17 city,
18 - isLoading, 18 + loading: loading.models.user,
19 }; 19 };
20 }) 20 })
21 class GeographicView extends PureComponent { 21 class GeographicView extends PureComponent {
22 componentDidMount = () => { 22 componentDidMount = () => {
23 const { dispatch } = this.props; 23 const { dispatch } = this.props;
24 dispatch({ 24 dispatch({
25 - type: 'geographic/fetchProvince', 25 + type: 'user/fetchProvince',
26 }); 26 });
27 }; 27 };
28 28
@@ -31,7 +31,7 @@ class GeographicView extends PureComponent { @@ -31,7 +31,7 @@ class GeographicView extends PureComponent {
31 31
32 if (!props.value && !!value && !!value.province) { 32 if (!props.value && !!value && !!value.province) {
33 dispatch({ 33 dispatch({
34 - type: 'geographic/fetchCity', 34 + type: 'user/fetchCity',
35 payload: value.province.key, 35 payload: value.province.key,
36 }); 36 });
37 } 37 }
@@ -65,7 +65,7 @@ class GeographicView extends PureComponent { @@ -65,7 +65,7 @@ class GeographicView extends PureComponent {
65 selectProvinceItem = item => { 65 selectProvinceItem = item => {
66 const { dispatch, onChange } = this.props; 66 const { dispatch, onChange } = this.props;
67 dispatch({ 67 dispatch({
68 - type: 'geographic/fetchCity', 68 + type: 'user/fetchCity',
69 payload: item.key, 69 payload: item.key,
70 }); 70 });
71 onChange({ 71 onChange({
@@ -99,9 +99,9 @@ class GeographicView extends PureComponent { @@ -99,9 +99,9 @@ class GeographicView extends PureComponent {
99 99
100 render() { 100 render() {
101 const { province, city } = this.conversionObject(); 101 const { province, city } = this.conversionObject();
102 - const { isLoading } = this.props; 102 + const { loading } = this.props;
103 return ( 103 return (
104 - <Spin spinning={isLoading} wrapperClassName={styles.row}> 104 + <Spin spinning={loading} wrapperClassName={styles.row}>
105 <Select 105 <Select
106 className={styles.item} 106 className={styles.item}
107 value={province} 107 value={province}
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,11 @@ import { connect } from 'dva'; @@ -3,8 +3,11 @@ import { connect } from 'dva';
3 import router from 'umi/router'; 3 import router from 'umi/router';
4 import { FormattedMessage } from 'umi/locale'; 4 import { FormattedMessage } from 'umi/locale';
5 import { Menu } from 'antd'; 5 import { Menu } from 'antd';
6 -import GridContent from '@/components/PageHeaderWrapper/GridContent';  
7 import styles from './Info.less'; 6 import styles from './Info.less';
  7 +import BaseView from './base';
  8 +import SecurityView from './security';
  9 +import BindingView from './binding';
  10 +import NotificationView from './notification';
8 11
9 const { Item } = Menu; 12 const { Item } = Menu;
10 13
@@ -39,8 +42,8 @@ class Info extends Component { @@ -39,8 +42,8 @@ class Info extends Component {
39 } 42 }
40 43
41 static getDerivedStateFromProps(props, state) { 44 static getDerivedStateFromProps(props, state) {
42 - const { match, location } = props;  
43 - let selectKey = location.pathname.replace(`${match.path}/`, ''); 45 + const { location } = props;
  46 + let selectKey = location.pathname.split('/').pop();
44 selectKey = state.menuMap[selectKey] ? selectKey : 'base'; 47 selectKey = state.menuMap[selectKey] ? selectKey : 'base';
45 if (selectKey !== state.selectKey) { 48 if (selectKey !== state.selectKey) {
46 return { selectKey }; 49 return { selectKey };
@@ -83,6 +86,9 @@ class Info extends Component { @@ -83,6 +86,9 @@ class Info extends Component {
83 return; 86 return;
84 } 87 }
85 requestAnimationFrame(() => { 88 requestAnimationFrame(() => {
  89 + if (!this.main) {
  90 + return;
  91 + }
86 let mode = 'inline'; 92 let mode = 'inline';
87 const { offsetWidth } = this.main; 93 const { offsetWidth } = this.main;
88 if (this.main.offsetWidth < 641 && offsetWidth > 400) { 94 if (this.main.offsetWidth < 641 && offsetWidth > 400) {
@@ -97,31 +103,47 @@ class Info extends Component { @@ -97,31 +103,47 @@ class Info extends Component {
97 }); 103 });
98 }; 104 };
99 105
  106 + renderChildren = () => {
  107 + const { selectKey } = this.state;
  108 + switch (selectKey) {
  109 + case 'base':
  110 + return <BaseView />;
  111 + case 'security':
  112 + return <SecurityView />;
  113 + case 'binding':
  114 + return <BindingView />;
  115 + case 'notification':
  116 + return <NotificationView />;
  117 + default:
  118 + break;
  119 + }
  120 +
  121 + return null;
  122 + };
  123 +
100 render() { 124 render() {
101 - const { children, currentUser } = this.props; 125 + const { currentUser } = this.props;
102 if (!currentUser.userid) { 126 if (!currentUser.userid) {
103 return ''; 127 return '';
104 } 128 }
105 const { mode, selectKey } = this.state; 129 const { mode, selectKey } = this.state;
106 return ( 130 return (
107 - <GridContent>  
108 - <div  
109 - className={styles.main}  
110 - ref={ref => {  
111 - this.main = ref;  
112 - }}  
113 - >  
114 - <div className={styles.leftmenu}>  
115 - <Menu mode={mode} selectedKeys={[selectKey]} onClick={this.selectKey}>  
116 - {this.getmenu()}  
117 - </Menu>  
118 - </div>  
119 - <div className={styles.right}>  
120 - <div className={styles.title}>{this.getRightTitle()}</div>  
121 - {children}  
122 - </div> 131 + <div
  132 + className={styles.main}
  133 + ref={ref => {
  134 + this.main = ref;
  135 + }}
  136 + >
  137 + <div className={styles.leftmenu}>
  138 + <Menu mode={mode} selectedKeys={[selectKey]} onClick={this.selectKey}>
  139 + {this.getmenu()}
  140 + </Menu>
  141 + </div>
  142 + <div className={styles.right}>
  143 + <div className={styles.title}>{this.getRightTitle()}</div>
  144 + {this.renderChildren()}
123 </div> 145 </div>
124 - </GridContent> 146 + </div>
125 ); 147 );
126 } 148 }
127 } 149 }
1 -import { query as queryUsers, queryCurrent } from '@/services/user'; 1 +import { query as queryUsers, queryCurrent, queryProvince, queryCity } from './service';
2 2
3 export default { 3 export default {
4 namespace: 'user', 4 namespace: 'user',
@@ -6,6 +6,8 @@ export default { @@ -6,6 +6,8 @@ export default {
6 state: { 6 state: {
7 list: [], 7 list: [],
8 currentUser: {}, 8 currentUser: {},
  9 + province: [],
  10 + city: [],
9 }, 11 },
10 12
11 effects: { 13 effects: {
@@ -23,6 +25,24 @@ export default { @@ -23,6 +25,24 @@ export default {
23 payload: response, 25 payload: response,
24 }); 26 });
25 }, 27 },
  28 + *fetchProvince(_, { call, put }) {
  29 + yield put({
  30 + type: 'changeLoading',
  31 + payload: true,
  32 + });
  33 + const response = yield call(queryProvince);
  34 + yield put({
  35 + type: 'setProvince',
  36 + payload: response,
  37 + });
  38 + },
  39 + *fetchCity({ payload }, { call, put }) {
  40 + const response = yield call(queryCity, payload);
  41 + yield put({
  42 + type: 'setCity',
  43 + payload: response,
  44 + });
  45 + },
26 }, 46 },
27 47
28 reducers: { 48 reducers: {
@@ -48,5 +68,23 @@ export default { @@ -48,5 +68,23 @@ export default {
48 }, 68 },
49 }; 69 };
50 }, 70 },
  71 + setProvince(state, action) {
  72 + return {
  73 + ...state,
  74 + province: action.payload,
  75 + };
  76 + },
  77 + setCity(state, action) {
  78 + return {
  79 + ...state,
  80 + city: action.payload,
  81 + };
  82 + },
  83 + changeLoading(state, action) {
  84 + return {
  85 + ...state,
  86 + isLoading: action.payload,
  87 + };
  88 + },
51 }, 89 },
52 }; 90 };
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 '@/utils/request';  
2 -  
3 -export async function query() {  
4 - return request('/api/users');  
5 -} 1 +import request from 'umi-request';
6 2
7 export async function queryCurrent() { 3 export async function queryCurrent() {
8 return request('/api/currentUser'); 4 return request('/api/currentUser');
9 } 5 }
  6 +
  7 +export async function queryProvince() {
  8 + return request('/api/geographic/province');
  9 +}
  10 +
  11 +export async function queryCity(province) {
  12 + return request(`/api/geographic/city/${province}`);
  13 +}
  14 +
  15 +export async function query() {
  16 + return request('/api/users');
  17 +}
1 -/yarn.lock  
2 -/package-lock.json  
3 -/dist  
4 -/node_modules  
5 -  
6 -.umi  
7 -.umi-production  
1 -export default {  
2 - plugins: [  
3 - ['umi-plugin-block-dev', {}],  
4 - ['umi-plugin-react', {  
5 - dva: true,  
6 - locale: true,  
7 - antd: true,  
8 - }]  
9 - ],  
10 -}  
1 -import request from '@/utils/request';  
2 -  
3 -export async function queryProvince() {  
4 - return request('/api/geographic/province');  
5 -}  
6 -  
7 -export async function queryCity(province) {  
8 - return request(`/api/geographic/city/${province}`);  
9 -}  
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 -}  
1 -# @umi-blocks/ant-design-pro/accountsettingsbase  
2 -  
3 -AccountSettingsBase  
4 -  
5 -## Usage  
6 -  
7 -```sh  
8 -umi block ant-design-proaccountsettingsbase  
9 -```  
10 -  
11 -## LICENSE  
12 -  
13 -MIT  
1 -{  
2 - "name": "@umi-block/accountsettingsbase",  
3 - "version": "0.0.1",  
4 - "description": "AccountSettingsBase",  
5 - "main": "src/index.js",  
6 - "scripts": {  
7 - "dev": "umi dev"  
8 - },  
9 - "repository": {  
10 - "type": "git",  
11 - "url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettingsbase"  
12 - },  
13 - "dependencies": {  
14 - "react": "^16.6.3",  
15 - "antd": "^3.10.9",  
16 - "dva": "^2.4.0",  
17 - "moment": "^2.22.2",  
18 - "nzh": "^1.0.3",  
19 - "qs": "^6.6.0",  
20 - "hash.js": "^1.1.5"  
21 - },  
22 - "devDependencies": {  
23 - "umi": "^2.3.0-beta.1",  
24 - "umi-plugin-react": "^1.3.0-beta.1",  
25 - "umi-plugin-block-dev": "^1.0.0"  
26 - },  
27 - "license": "ISC"  
28 -}  
1 -import city from './geographic/city.json';  
2 -import province from './geographic/province.json';  
3 -  
4 -function getProvince(req, res) {  
5 - return res.json(province);  
6 -}  
7 -  
8 -function getCity(req, res) {  
9 - return res.json(city[req.params.province]);  
10 -}  
11 -  
12 -export default {  
13 - 'GET /api/geographic/province': getProvince,  
14 - 'GET /api/geographic/city/:province': getCity,  
15 -};  
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 -};  
1 -import { queryProvince, queryCity } from '@/services/geographic';  
2 -  
3 -export default {  
4 - namespace: 'geographic',  
5 -  
6 - state: {  
7 - province: [],  
8 - city: [],  
9 - isLoading: false,  
10 - },  
11 -  
12 - effects: {  
13 - *fetchProvince(_, { call, put }) {  
14 - yield put({  
15 - type: 'changeLoading',  
16 - payload: true,  
17 - });  
18 - const response = yield call(queryProvince);  
19 - yield put({  
20 - type: 'setProvince',  
21 - payload: response,  
22 - });  
23 - yield put({  
24 - type: 'changeLoading',  
25 - payload: false,  
26 - });  
27 - },  
28 - *fetchCity({ payload }, { call, put }) {  
29 - yield put({  
30 - type: 'changeLoading',  
31 - payload: true,  
32 - });  
33 - const response = yield call(queryCity, payload);  
34 - yield put({  
35 - type: 'setCity',  
36 - payload: response,  
37 - });  
38 - yield put({  
39 - type: 'changeLoading',  
40 - payload: false,  
41 - });  
42 - },  
43 - },  
44 -  
45 - reducers: {  
46 - setProvince(state, action) {  
47 - return {  
48 - ...state,  
49 - province: action.payload,  
50 - };  
51 - },  
52 - setCity(state, action) {  
53 - return {  
54 - ...state,  
55 - city: action.payload,  
56 - };  
57 - },  
58 - changeLoading(state, action) {  
59 - return {  
60 - ...state,  
61 - isLoading: action.payload,  
62 - };  
63 - },  
64 - },  
65 -};  
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 { query as queryUsers, queryCurrent } from '@/services/user';  
2 -  
3 -export default {  
4 - namespace: 'user',  
5 -  
6 - state: {  
7 - list: [],  
8 - currentUser: {},  
9 - },  
10 -  
11 - effects: {  
12 - *fetch(_, { call, put }) {  
13 - const response = yield call(queryUsers);  
14 - yield put({  
15 - type: 'save',  
16 - payload: response,  
17 - });  
18 - },  
19 - *fetchCurrent(_, { call, put }) {  
20 - const response = yield call(queryCurrent);  
21 - yield put({  
22 - type: 'saveCurrentUser',  
23 - payload: response,  
24 - });  
25 - },  
26 - },  
27 -  
28 - reducers: {  
29 - save(state, action) {  
30 - return {  
31 - ...state,  
32 - list: action.payload,  
33 - };  
34 - },  
35 - saveCurrentUser(state, action) {  
36 - return {  
37 - ...state,  
38 - currentUser: action.payload || {},  
39 - };  
40 - },  
41 - changeNotifyCount(state, action) {  
42 - return {  
43 - ...state,  
44 - currentUser: {  
45 - ...state.currentUser,  
46 - notifyCount: action.payload.totalCount,  
47 - unreadCount: action.payload.unreadCount,  
48 - },  
49 - };  
50 - },  
51 - },  
52 -};  
1 -/yarn.lock  
2 -/package-lock.json  
3 -/dist  
4 -/node_modules  
5 -  
6 -.umi  
7 -.umi-production  
1 -export default {  
2 - plugins: [  
3 - ['umi-plugin-block-dev', {}],  
4 - ['umi-plugin-react', {  
5 - dva: true,  
6 - locale: true,  
7 - antd: true,  
8 - }]  
9 - ],  
10 -}  
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 -}  
1 -# @umi-blocks/ant-design-pro/accountsettingsbinding  
2 -  
3 -AccountSettingsBinding  
4 -  
5 -## Usage  
6 -  
7 -```sh  
8 -umi block ant-design-proaccountsettingsbinding  
9 -```  
10 -  
11 -## LICENSE  
12 -  
13 -MIT  
1 -{  
2 - "name": "@umi-block/accountsettingsbinding",  
3 - "version": "0.0.1",  
4 - "description": "AccountSettingsBinding",  
5 - "main": "src/index.js",  
6 - "scripts": {  
7 - "dev": "umi dev"  
8 - },  
9 - "repository": {  
10 - "type": "git",  
11 - "url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettingsbinding"  
12 - },  
13 - "dependencies": {  
14 - "react": "^16.6.3",  
15 - "antd": "^3.10.9",  
16 - "dva": "^2.4.0",  
17 - "hash.js": "^1.1.5",  
18 - "moment": "^2.22.2",  
19 - "nzh": "^1.0.3",  
20 - "qs": "^6.6.0"  
21 - },  
22 - "devDependencies": {  
23 - "umi": "^2.3.0-beta.1",  
24 - "umi-plugin-react": "^1.3.0-beta.1",  
25 - "umi-plugin-block-dev": "^1.0.0"  
26 - },  
27 - "license": "ISC"  
28 -}  
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 -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 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': 'Basic Settings',  
3 - 'app.settings.menuMap.security': 'Security Settings',  
4 - 'app.settings.menuMap.binding': 'Account Binding',  
5 - 'app.settings.menuMap.notification': 'New Message Notification',  
6 - 'app.settings.basic.avatar': 'Avatar',  
7 - 'app.settings.basic.change-avatar': 'Change avatar',  
8 - 'app.settings.basic.email': 'Email',  
9 - 'app.settings.basic.email-message': 'Please input your email!',  
10 - 'app.settings.basic.nickname': 'Nickname',  
11 - 'app.settings.basic.nickname-message': 'Please input your Nickname!',  
12 - 'app.settings.basic.profile': 'Personal profile',  
13 - 'app.settings.basic.profile-message': 'Please input your personal profile!',  
14 - 'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',  
15 - 'app.settings.basic.country': 'Country/Region',  
16 - 'app.settings.basic.country-message': 'Please input your country!',  
17 - 'app.settings.basic.geographic': 'Province or city',  
18 - 'app.settings.basic.geographic-message': 'Please input your geographic info!',  
19 - 'app.settings.basic.address': 'Street Address',  
20 - 'app.settings.basic.address-message': 'Please input your address!',  
21 - 'app.settings.basic.phone': 'Phone Number',  
22 - 'app.settings.basic.phone-message': 'Please input your phone!',  
23 - 'app.settings.basic.update': 'Update Information',  
24 - 'app.settings.security.strong': 'Strong',  
25 - 'app.settings.security.medium': 'Medium',  
26 - 'app.settings.security.weak': 'Weak',  
27 - 'app.settings.security.password': 'Account Password',  
28 - 'app.settings.security.password-description': 'Current password strength:',  
29 - 'app.settings.security.phone': 'Security Phone',  
30 - 'app.settings.security.phone-description': 'Bound phone:',  
31 - 'app.settings.security.question': 'Security Question',  
32 - 'app.settings.security.question-description':  
33 - 'The security question is not set, and the security policy can effectively protect the account security',  
34 - 'app.settings.security.email': 'Backup Email',  
35 - 'app.settings.security.email-description': 'Bound Email:',  
36 - 'app.settings.security.mfa': 'MFA Device',  
37 - 'app.settings.security.mfa-description':  
38 - 'Unbound MFA device, after binding, can be confirmed twice',  
39 - 'app.settings.security.modify': 'Modify',  
40 - 'app.settings.security.set': 'Set',  
41 - 'app.settings.security.bind': 'Bind',  
42 - 'app.settings.binding.taobao': 'Binding Taobao',  
43 - 'app.settings.binding.taobao-description': 'Currently unbound Taobao account',  
44 - 'app.settings.binding.alipay': 'Binding Alipay',  
45 - 'app.settings.binding.alipay-description': 'Currently unbound Alipay account',  
46 - 'app.settings.binding.dingding': 'Binding DingTalk',  
47 - 'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',  
48 - 'app.settings.binding.bind': 'Bind',  
49 - 'app.settings.notification.password': 'Account Password',  
50 - 'app.settings.notification.password-description':  
51 - 'Messages from other users will be notified in the form of a station letter',  
52 - 'app.settings.notification.messages': 'System Messages',  
53 - 'app.settings.notification.messages-description':  
54 - 'System messages will be notified in the form of a station letter',  
55 - 'app.settings.notification.todo': 'To-do Notification',  
56 - 'app.settings.notification.todo-description':  
57 - 'The to-do list will be notified in the form of a letter from the station',  
58 - 'app.settings.open': 'Open',  
59 - 'app.settings.close': 'Close',  
60 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': '基本设置',  
3 - 'app.settings.menuMap.security': '安全设置',  
4 - 'app.settings.menuMap.binding': '账号绑定',  
5 - 'app.settings.menuMap.notification': '新消息通知',  
6 - 'app.settings.basic.avatar': '头像',  
7 - 'app.settings.basic.change-avatar': '更换头像',  
8 - 'app.settings.basic.email': '邮箱',  
9 - 'app.settings.basic.email-message': '请输入您的邮箱!',  
10 - 'app.settings.basic.nickname': '昵称',  
11 - 'app.settings.basic.nickname-message': '请输入您的昵称!',  
12 - 'app.settings.basic.profile': '个人简介',  
13 - 'app.settings.basic.profile-message': '请输入个人简介!',  
14 - 'app.settings.basic.profile-placeholder': '个人简介',  
15 - 'app.settings.basic.country': '国家/地区',  
16 - 'app.settings.basic.country-message': '请输入您的国家或地区!',  
17 - 'app.settings.basic.geographic': '所在省市',  
18 - 'app.settings.basic.geographic-message': '请输入您的所在省市!',  
19 - 'app.settings.basic.address': '街道地址',  
20 - 'app.settings.basic.address-message': '请输入您的街道地址!',  
21 - 'app.settings.basic.phone': '联系电话',  
22 - 'app.settings.basic.phone-message': '请输入您的联系电话!',  
23 - 'app.settings.basic.update': '更新基本信息',  
24 - 'app.settings.security.strong': '强',  
25 - 'app.settings.security.medium': '中',  
26 - 'app.settings.security.weak': '弱',  
27 - 'app.settings.security.password': '账户密码',  
28 - 'app.settings.security.password-description': '当前密码强度:',  
29 - 'app.settings.security.phone': '密保手机',  
30 - 'app.settings.security.phone-description': '已绑定手机:',  
31 - 'app.settings.security.question': '密保问题',  
32 - 'app.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',  
33 - 'app.settings.security.email': '备用邮箱',  
34 - 'app.settings.security.email-description': '已绑定邮箱:',  
35 - 'app.settings.security.mfa': 'MFA 设备',  
36 - 'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',  
37 - 'app.settings.security.modify': '修改',  
38 - 'app.settings.security.set': '设置',  
39 - 'app.settings.security.bind': '绑定',  
40 - 'app.settings.binding.taobao': '绑定淘宝',  
41 - 'app.settings.binding.taobao-description': '当前未绑定淘宝账号',  
42 - 'app.settings.binding.alipay': '绑定支付宝',  
43 - 'app.settings.binding.alipay-description': '当前未绑定支付宝账号',  
44 - 'app.settings.binding.dingding': '绑定钉钉',  
45 - 'app.settings.binding.dingding-description': '当前未绑定钉钉账号',  
46 - 'app.settings.binding.bind': '绑定',  
47 - 'app.settings.notification.password': '账户密码',  
48 - 'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',  
49 - 'app.settings.notification.messages': '系统消息',  
50 - 'app.settings.notification.messages-description': '系统消息将以站内信的形式通知',  
51 - 'app.settings.notification.todo': '待办任务',  
52 - 'app.settings.notification.todo-description': '待办任务将以站内信的形式通知',  
53 - 'app.settings.open': '开',  
54 - 'app.settings.close': '关',  
55 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': '基本設置',  
3 - 'app.settings.menuMap.security': '安全設置',  
4 - 'app.settings.menuMap.binding': '賬號綁定',  
5 - 'app.settings.menuMap.notification': '新消息通知',  
6 - 'app.settings.basic.avatar': '頭像',  
7 - 'app.settings.basic.change-avatar': '更換頭像',  
8 - 'app.settings.basic.email': '郵箱',  
9 - 'app.settings.basic.email-message': '請輸入您的郵箱!',  
10 - 'app.settings.basic.nickname': '昵稱',  
11 - 'app.settings.basic.nickname-message': '請輸入您的昵稱!',  
12 - 'app.settings.basic.profile': '個人簡介',  
13 - 'app.settings.basic.profile-message': '請輸入個人簡介!',  
14 - 'app.settings.basic.profile-placeholder': '個人簡介',  
15 - 'app.settings.basic.country': '國家/地區',  
16 - 'app.settings.basic.country-message': '請輸入您的國家或地區!',  
17 - 'app.settings.basic.geographic': '所在省市',  
18 - 'app.settings.basic.geographic-message': '請輸入您的所在省市!',  
19 - 'app.settings.basic.address': '街道地址',  
20 - 'app.settings.basic.address-message': '請輸入您的街道地址!',  
21 - 'app.settings.basic.phone': '聯系電話',  
22 - 'app.settings.basic.phone-message': '請輸入您的聯系電話!',  
23 - 'app.settings.basic.update': '更新基本信息',  
24 - 'app.settings.security.strong': '強',  
25 - 'app.settings.security.medium': '中',  
26 - 'app.settings.security.weak': '弱',  
27 - 'app.settings.security.password': '賬戶密碼',  
28 - 'app.settings.security.password-description': '當前密碼強度:',  
29 - 'app.settings.security.phone': '密保手機',  
30 - 'app.settings.security.phone-description': '已綁定手機:',  
31 - 'app.settings.security.question': '密保問題',  
32 - 'app.settings.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全',  
33 - 'app.settings.security.email': '備用郵箱',  
34 - 'app.settings.security.email-description': '已綁定郵箱:',  
35 - 'app.settings.security.mfa': 'MFA 設備',  
36 - 'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',  
37 - 'app.settings.security.modify': '修改',  
38 - 'app.settings.security.set': '設置',  
39 - 'app.settings.security.bind': '綁定',  
40 - 'app.settings.binding.taobao': '綁定淘寶',  
41 - 'app.settings.binding.taobao-description': '當前未綁定淘寶賬號',  
42 - 'app.settings.binding.alipay': '綁定支付寶',  
43 - 'app.settings.binding.alipay-description': '當前未綁定支付寶賬號',  
44 - 'app.settings.binding.dingding': '綁定釘釘',  
45 - 'app.settings.binding.dingding-description': '當前未綁定釘釘賬號',  
46 - 'app.settings.binding.bind': '綁定',  
47 - 'app.settings.notification.password': '賬戶密碼',  
48 - 'app.settings.notification.password-description': '其他用戶的消息將以站內信的形式通知',  
49 - 'app.settings.notification.messages': '系統消息',  
50 - 'app.settings.notification.messages-description': '系統消息將以站內信的形式通知',  
51 - 'app.settings.notification.todo': '待辦任務',  
52 - 'app.settings.notification.todo-description': '待辦任務將以站內信的形式通知',  
53 - 'app.settings.open': '開',  
54 - 'app.settings.close': '關',  
55 -};  
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 { query as queryUsers, queryCurrent } from '@/services/user';  
2 -  
3 -export default {  
4 - namespace: 'user',  
5 -  
6 - state: {  
7 - list: [],  
8 - currentUser: {},  
9 - },  
10 -  
11 - effects: {  
12 - *fetch(_, { call, put }) {  
13 - const response = yield call(queryUsers);  
14 - yield put({  
15 - type: 'save',  
16 - payload: response,  
17 - });  
18 - },  
19 - *fetchCurrent(_, { call, put }) {  
20 - const response = yield call(queryCurrent);  
21 - yield put({  
22 - type: 'saveCurrentUser',  
23 - payload: response,  
24 - });  
25 - },  
26 - },  
27 -  
28 - reducers: {  
29 - save(state, action) {  
30 - return {  
31 - ...state,  
32 - list: action.payload,  
33 - };  
34 - },  
35 - saveCurrentUser(state, action) {  
36 - return {  
37 - ...state,  
38 - currentUser: action.payload || {},  
39 - };  
40 - },  
41 - changeNotifyCount(state, action) {  
42 - return {  
43 - ...state,  
44 - currentUser: {  
45 - ...state.currentUser,  
46 - notifyCount: action.payload.totalCount,  
47 - unreadCount: action.payload.unreadCount,  
48 - },  
49 - };  
50 - },  
51 - },  
52 -};  
1 -/yarn.lock  
2 -/package-lock.json  
3 -/dist  
4 -/node_modules  
5 -  
6 -.umi  
7 -.umi-production  
1 -export default {  
2 - plugins: [  
3 - ['umi-plugin-block-dev', {}],  
4 - ['umi-plugin-react', {  
5 - dva: true,  
6 - locale: true,  
7 - antd: true,  
8 - }]  
9 - ],  
10 -}  
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 -}  
1 -# @umi-blocks/ant-design-pro/accountsettingsnotification  
2 -  
3 -AccountSettingsNotification  
4 -  
5 -## Usage  
6 -  
7 -```sh  
8 -umi block ant-design-proaccountsettingsnotification  
9 -```  
10 -  
11 -## LICENSE  
12 -  
13 -MIT  
1 -{  
2 - "name": "@umi-block/accountsettingsnotification",  
3 - "version": "0.0.1",  
4 - "description": "AccountSettingsNotification",  
5 - "main": "src/index.js",  
6 - "scripts": {  
7 - "dev": "umi dev"  
8 - },  
9 - "repository": {  
10 - "type": "git",  
11 - "url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettingsnotification"  
12 - },  
13 - "dependencies": {  
14 - "react": "^16.6.3",  
15 - "antd": "^3.10.9",  
16 - "dva": "^2.4.0",  
17 - "hash.js": "^1.1.5",  
18 - "moment": "^2.22.2",  
19 - "nzh": "^1.0.3",  
20 - "qs": "^6.6.0"  
21 - },  
22 - "devDependencies": {  
23 - "umi": "^2.3.0-beta.1",  
24 - "umi-plugin-react": "^1.3.0-beta.1",  
25 - "umi-plugin-block-dev": "^1.0.0"  
26 - },  
27 - "license": "ISC"  
28 -}  
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 -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 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': 'Basic Settings',  
3 - 'app.settings.menuMap.security': 'Security Settings',  
4 - 'app.settings.menuMap.binding': 'Account Binding',  
5 - 'app.settings.menuMap.notification': 'New Message Notification',  
6 - 'app.settings.basic.avatar': 'Avatar',  
7 - 'app.settings.basic.change-avatar': 'Change avatar',  
8 - 'app.settings.basic.email': 'Email',  
9 - 'app.settings.basic.email-message': 'Please input your email!',  
10 - 'app.settings.basic.nickname': 'Nickname',  
11 - 'app.settings.basic.nickname-message': 'Please input your Nickname!',  
12 - 'app.settings.basic.profile': 'Personal profile',  
13 - 'app.settings.basic.profile-message': 'Please input your personal profile!',  
14 - 'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',  
15 - 'app.settings.basic.country': 'Country/Region',  
16 - 'app.settings.basic.country-message': 'Please input your country!',  
17 - 'app.settings.basic.geographic': 'Province or city',  
18 - 'app.settings.basic.geographic-message': 'Please input your geographic info!',  
19 - 'app.settings.basic.address': 'Street Address',  
20 - 'app.settings.basic.address-message': 'Please input your address!',  
21 - 'app.settings.basic.phone': 'Phone Number',  
22 - 'app.settings.basic.phone-message': 'Please input your phone!',  
23 - 'app.settings.basic.update': 'Update Information',  
24 - 'app.settings.security.strong': 'Strong',  
25 - 'app.settings.security.medium': 'Medium',  
26 - 'app.settings.security.weak': 'Weak',  
27 - 'app.settings.security.password': 'Account Password',  
28 - 'app.settings.security.password-description': 'Current password strength:',  
29 - 'app.settings.security.phone': 'Security Phone',  
30 - 'app.settings.security.phone-description': 'Bound phone:',  
31 - 'app.settings.security.question': 'Security Question',  
32 - 'app.settings.security.question-description':  
33 - 'The security question is not set, and the security policy can effectively protect the account security',  
34 - 'app.settings.security.email': 'Backup Email',  
35 - 'app.settings.security.email-description': 'Bound Email:',  
36 - 'app.settings.security.mfa': 'MFA Device',  
37 - 'app.settings.security.mfa-description':  
38 - 'Unbound MFA device, after binding, can be confirmed twice',  
39 - 'app.settings.security.modify': 'Modify',  
40 - 'app.settings.security.set': 'Set',  
41 - 'app.settings.security.bind': 'Bind',  
42 - 'app.settings.binding.taobao': 'Binding Taobao',  
43 - 'app.settings.binding.taobao-description': 'Currently unbound Taobao account',  
44 - 'app.settings.binding.alipay': 'Binding Alipay',  
45 - 'app.settings.binding.alipay-description': 'Currently unbound Alipay account',  
46 - 'app.settings.binding.dingding': 'Binding DingTalk',  
47 - 'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',  
48 - 'app.settings.binding.bind': 'Bind',  
49 - 'app.settings.notification.password': 'Account Password',  
50 - 'app.settings.notification.password-description':  
51 - 'Messages from other users will be notified in the form of a station letter',  
52 - 'app.settings.notification.messages': 'System Messages',  
53 - 'app.settings.notification.messages-description':  
54 - 'System messages will be notified in the form of a station letter',  
55 - 'app.settings.notification.todo': 'To-do Notification',  
56 - 'app.settings.notification.todo-description':  
57 - 'The to-do list will be notified in the form of a letter from the station',  
58 - 'app.settings.open': 'Open',  
59 - 'app.settings.close': 'Close',  
60 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': '基本设置',  
3 - 'app.settings.menuMap.security': '安全设置',  
4 - 'app.settings.menuMap.binding': '账号绑定',  
5 - 'app.settings.menuMap.notification': '新消息通知',  
6 - 'app.settings.basic.avatar': '头像',  
7 - 'app.settings.basic.change-avatar': '更换头像',  
8 - 'app.settings.basic.email': '邮箱',  
9 - 'app.settings.basic.email-message': '请输入您的邮箱!',  
10 - 'app.settings.basic.nickname': '昵称',  
11 - 'app.settings.basic.nickname-message': '请输入您的昵称!',  
12 - 'app.settings.basic.profile': '个人简介',  
13 - 'app.settings.basic.profile-message': '请输入个人简介!',  
14 - 'app.settings.basic.profile-placeholder': '个人简介',  
15 - 'app.settings.basic.country': '国家/地区',  
16 - 'app.settings.basic.country-message': '请输入您的国家或地区!',  
17 - 'app.settings.basic.geographic': '所在省市',  
18 - 'app.settings.basic.geographic-message': '请输入您的所在省市!',  
19 - 'app.settings.basic.address': '街道地址',  
20 - 'app.settings.basic.address-message': '请输入您的街道地址!',  
21 - 'app.settings.basic.phone': '联系电话',  
22 - 'app.settings.basic.phone-message': '请输入您的联系电话!',  
23 - 'app.settings.basic.update': '更新基本信息',  
24 - 'app.settings.security.strong': '强',  
25 - 'app.settings.security.medium': '中',  
26 - 'app.settings.security.weak': '弱',  
27 - 'app.settings.security.password': '账户密码',  
28 - 'app.settings.security.password-description': '当前密码强度:',  
29 - 'app.settings.security.phone': '密保手机',  
30 - 'app.settings.security.phone-description': '已绑定手机:',  
31 - 'app.settings.security.question': '密保问题',  
32 - 'app.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',  
33 - 'app.settings.security.email': '备用邮箱',  
34 - 'app.settings.security.email-description': '已绑定邮箱:',  
35 - 'app.settings.security.mfa': 'MFA 设备',  
36 - 'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',  
37 - 'app.settings.security.modify': '修改',  
38 - 'app.settings.security.set': '设置',  
39 - 'app.settings.security.bind': '绑定',  
40 - 'app.settings.binding.taobao': '绑定淘宝',  
41 - 'app.settings.binding.taobao-description': '当前未绑定淘宝账号',  
42 - 'app.settings.binding.alipay': '绑定支付宝',  
43 - 'app.settings.binding.alipay-description': '当前未绑定支付宝账号',  
44 - 'app.settings.binding.dingding': '绑定钉钉',  
45 - 'app.settings.binding.dingding-description': '当前未绑定钉钉账号',  
46 - 'app.settings.binding.bind': '绑定',  
47 - 'app.settings.notification.password': '账户密码',  
48 - 'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',  
49 - 'app.settings.notification.messages': '系统消息',  
50 - 'app.settings.notification.messages-description': '系统消息将以站内信的形式通知',  
51 - 'app.settings.notification.todo': '待办任务',  
52 - 'app.settings.notification.todo-description': '待办任务将以站内信的形式通知',  
53 - 'app.settings.open': '开',  
54 - 'app.settings.close': '关',  
55 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': '基本設置',  
3 - 'app.settings.menuMap.security': '安全設置',  
4 - 'app.settings.menuMap.binding': '賬號綁定',  
5 - 'app.settings.menuMap.notification': '新消息通知',  
6 - 'app.settings.basic.avatar': '頭像',  
7 - 'app.settings.basic.change-avatar': '更換頭像',  
8 - 'app.settings.basic.email': '郵箱',  
9 - 'app.settings.basic.email-message': '請輸入您的郵箱!',  
10 - 'app.settings.basic.nickname': '昵稱',  
11 - 'app.settings.basic.nickname-message': '請輸入您的昵稱!',  
12 - 'app.settings.basic.profile': '個人簡介',  
13 - 'app.settings.basic.profile-message': '請輸入個人簡介!',  
14 - 'app.settings.basic.profile-placeholder': '個人簡介',  
15 - 'app.settings.basic.country': '國家/地區',  
16 - 'app.settings.basic.country-message': '請輸入您的國家或地區!',  
17 - 'app.settings.basic.geographic': '所在省市',  
18 - 'app.settings.basic.geographic-message': '請輸入您的所在省市!',  
19 - 'app.settings.basic.address': '街道地址',  
20 - 'app.settings.basic.address-message': '請輸入您的街道地址!',  
21 - 'app.settings.basic.phone': '聯系電話',  
22 - 'app.settings.basic.phone-message': '請輸入您的聯系電話!',  
23 - 'app.settings.basic.update': '更新基本信息',  
24 - 'app.settings.security.strong': '強',  
25 - 'app.settings.security.medium': '中',  
26 - 'app.settings.security.weak': '弱',  
27 - 'app.settings.security.password': '賬戶密碼',  
28 - 'app.settings.security.password-description': '當前密碼強度:',  
29 - 'app.settings.security.phone': '密保手機',  
30 - 'app.settings.security.phone-description': '已綁定手機:',  
31 - 'app.settings.security.question': '密保問題',  
32 - 'app.settings.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全',  
33 - 'app.settings.security.email': '備用郵箱',  
34 - 'app.settings.security.email-description': '已綁定郵箱:',  
35 - 'app.settings.security.mfa': 'MFA 設備',  
36 - 'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',  
37 - 'app.settings.security.modify': '修改',  
38 - 'app.settings.security.set': '設置',  
39 - 'app.settings.security.bind': '綁定',  
40 - 'app.settings.binding.taobao': '綁定淘寶',  
41 - 'app.settings.binding.taobao-description': '當前未綁定淘寶賬號',  
42 - 'app.settings.binding.alipay': '綁定支付寶',  
43 - 'app.settings.binding.alipay-description': '當前未綁定支付寶賬號',  
44 - 'app.settings.binding.dingding': '綁定釘釘',  
45 - 'app.settings.binding.dingding-description': '當前未綁定釘釘賬號',  
46 - 'app.settings.binding.bind': '綁定',  
47 - 'app.settings.notification.password': '賬戶密碼',  
48 - 'app.settings.notification.password-description': '其他用戶的消息將以站內信的形式通知',  
49 - 'app.settings.notification.messages': '系統消息',  
50 - 'app.settings.notification.messages-description': '系統消息將以站內信的形式通知',  
51 - 'app.settings.notification.todo': '待辦任務',  
52 - 'app.settings.notification.todo-description': '待辦任務將以站內信的形式通知',  
53 - 'app.settings.open': '開',  
54 - 'app.settings.close': '關',  
55 -};  
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 { query as queryUsers, queryCurrent } from '@/services/user';  
2 -  
3 -export default {  
4 - namespace: 'user',  
5 -  
6 - state: {  
7 - list: [],  
8 - currentUser: {},  
9 - },  
10 -  
11 - effects: {  
12 - *fetch(_, { call, put }) {  
13 - const response = yield call(queryUsers);  
14 - yield put({  
15 - type: 'save',  
16 - payload: response,  
17 - });  
18 - },  
19 - *fetchCurrent(_, { call, put }) {  
20 - const response = yield call(queryCurrent);  
21 - yield put({  
22 - type: 'saveCurrentUser',  
23 - payload: response,  
24 - });  
25 - },  
26 - },  
27 -  
28 - reducers: {  
29 - save(state, action) {  
30 - return {  
31 - ...state,  
32 - list: action.payload,  
33 - };  
34 - },  
35 - saveCurrentUser(state, action) {  
36 - return {  
37 - ...state,  
38 - currentUser: action.payload || {},  
39 - };  
40 - },  
41 - changeNotifyCount(state, action) {  
42 - return {  
43 - ...state,  
44 - currentUser: {  
45 - ...state.currentUser,  
46 - notifyCount: action.payload.totalCount,  
47 - unreadCount: action.payload.unreadCount,  
48 - },  
49 - };  
50 - },  
51 - },  
52 -};  
1 -/yarn.lock  
2 -/package-lock.json  
3 -/dist  
4 -/node_modules  
5 -  
6 -.umi  
7 -.umi-production  
1 -export default {  
2 - plugins: [  
3 - ['umi-plugin-block-dev', {}],  
4 - ['umi-plugin-react', {  
5 - dva: true,  
6 - locale: true,  
7 - antd: true,  
8 - }]  
9 - ],  
10 -}  
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 -}  
1 -# @umi-blocks/ant-design-pro/accountsettingssecurity  
2 -  
3 -AccountSettingsSecurity  
4 -  
5 -## Usage  
6 -  
7 -```sh  
8 -umi block ant-design-proaccountsettingssecurity  
9 -```  
10 -  
11 -## LICENSE  
12 -  
13 -MIT  
1 -{  
2 - "name": "@umi-block/accountsettingssecurity",  
3 - "version": "0.0.1",  
4 - "description": "AccountSettingsSecurity",  
5 - "main": "src/index.js",  
6 - "scripts": {  
7 - "dev": "umi dev"  
8 - },  
9 - "repository": {  
10 - "type": "git",  
11 - "url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettingssecurity"  
12 - },  
13 - "dependencies": {  
14 - "react": "^16.6.3",  
15 - "antd": "^3.10.9",  
16 - "moment": "^2.22.2",  
17 - "nzh": "^1.0.3",  
18 - "qs": "^6.6.0",  
19 - "dva": "^2.4.0",  
20 - "hash.js": "^1.1.5"  
21 - },  
22 - "devDependencies": {  
23 - "umi": "^2.3.0-beta.1",  
24 - "umi-plugin-react": "^1.3.0-beta.1",  
25 - "umi-plugin-block-dev": "^1.0.0"  
26 - },  
27 - "license": "ISC"  
28 -}  
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 -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 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': 'Basic Settings',  
3 - 'app.settings.menuMap.security': 'Security Settings',  
4 - 'app.settings.menuMap.binding': 'Account Binding',  
5 - 'app.settings.menuMap.notification': 'New Message Notification',  
6 - 'app.settings.basic.avatar': 'Avatar',  
7 - 'app.settings.basic.change-avatar': 'Change avatar',  
8 - 'app.settings.basic.email': 'Email',  
9 - 'app.settings.basic.email-message': 'Please input your email!',  
10 - 'app.settings.basic.nickname': 'Nickname',  
11 - 'app.settings.basic.nickname-message': 'Please input your Nickname!',  
12 - 'app.settings.basic.profile': 'Personal profile',  
13 - 'app.settings.basic.profile-message': 'Please input your personal profile!',  
14 - 'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',  
15 - 'app.settings.basic.country': 'Country/Region',  
16 - 'app.settings.basic.country-message': 'Please input your country!',  
17 - 'app.settings.basic.geographic': 'Province or city',  
18 - 'app.settings.basic.geographic-message': 'Please input your geographic info!',  
19 - 'app.settings.basic.address': 'Street Address',  
20 - 'app.settings.basic.address-message': 'Please input your address!',  
21 - 'app.settings.basic.phone': 'Phone Number',  
22 - 'app.settings.basic.phone-message': 'Please input your phone!',  
23 - 'app.settings.basic.update': 'Update Information',  
24 - 'app.settings.security.strong': 'Strong',  
25 - 'app.settings.security.medium': 'Medium',  
26 - 'app.settings.security.weak': 'Weak',  
27 - 'app.settings.security.password': 'Account Password',  
28 - 'app.settings.security.password-description': 'Current password strength:',  
29 - 'app.settings.security.phone': 'Security Phone',  
30 - 'app.settings.security.phone-description': 'Bound phone:',  
31 - 'app.settings.security.question': 'Security Question',  
32 - 'app.settings.security.question-description':  
33 - 'The security question is not set, and the security policy can effectively protect the account security',  
34 - 'app.settings.security.email': 'Backup Email',  
35 - 'app.settings.security.email-description': 'Bound Email:',  
36 - 'app.settings.security.mfa': 'MFA Device',  
37 - 'app.settings.security.mfa-description':  
38 - 'Unbound MFA device, after binding, can be confirmed twice',  
39 - 'app.settings.security.modify': 'Modify',  
40 - 'app.settings.security.set': 'Set',  
41 - 'app.settings.security.bind': 'Bind',  
42 - 'app.settings.binding.taobao': 'Binding Taobao',  
43 - 'app.settings.binding.taobao-description': 'Currently unbound Taobao account',  
44 - 'app.settings.binding.alipay': 'Binding Alipay',  
45 - 'app.settings.binding.alipay-description': 'Currently unbound Alipay account',  
46 - 'app.settings.binding.dingding': 'Binding DingTalk',  
47 - 'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',  
48 - 'app.settings.binding.bind': 'Bind',  
49 - 'app.settings.notification.password': 'Account Password',  
50 - 'app.settings.notification.password-description':  
51 - 'Messages from other users will be notified in the form of a station letter',  
52 - 'app.settings.notification.messages': 'System Messages',  
53 - 'app.settings.notification.messages-description':  
54 - 'System messages will be notified in the form of a station letter',  
55 - 'app.settings.notification.todo': 'To-do Notification',  
56 - 'app.settings.notification.todo-description':  
57 - 'The to-do list will be notified in the form of a letter from the station',  
58 - 'app.settings.open': 'Open',  
59 - 'app.settings.close': 'Close',  
60 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': '基本设置',  
3 - 'app.settings.menuMap.security': '安全设置',  
4 - 'app.settings.menuMap.binding': '账号绑定',  
5 - 'app.settings.menuMap.notification': '新消息通知',  
6 - 'app.settings.basic.avatar': '头像',  
7 - 'app.settings.basic.change-avatar': '更换头像',  
8 - 'app.settings.basic.email': '邮箱',  
9 - 'app.settings.basic.email-message': '请输入您的邮箱!',  
10 - 'app.settings.basic.nickname': '昵称',  
11 - 'app.settings.basic.nickname-message': '请输入您的昵称!',  
12 - 'app.settings.basic.profile': '个人简介',  
13 - 'app.settings.basic.profile-message': '请输入个人简介!',  
14 - 'app.settings.basic.profile-placeholder': '个人简介',  
15 - 'app.settings.basic.country': '国家/地区',  
16 - 'app.settings.basic.country-message': '请输入您的国家或地区!',  
17 - 'app.settings.basic.geographic': '所在省市',  
18 - 'app.settings.basic.geographic-message': '请输入您的所在省市!',  
19 - 'app.settings.basic.address': '街道地址',  
20 - 'app.settings.basic.address-message': '请输入您的街道地址!',  
21 - 'app.settings.basic.phone': '联系电话',  
22 - 'app.settings.basic.phone-message': '请输入您的联系电话!',  
23 - 'app.settings.basic.update': '更新基本信息',  
24 - 'app.settings.security.strong': '强',  
25 - 'app.settings.security.medium': '中',  
26 - 'app.settings.security.weak': '弱',  
27 - 'app.settings.security.password': '账户密码',  
28 - 'app.settings.security.password-description': '当前密码强度:',  
29 - 'app.settings.security.phone': '密保手机',  
30 - 'app.settings.security.phone-description': '已绑定手机:',  
31 - 'app.settings.security.question': '密保问题',  
32 - 'app.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',  
33 - 'app.settings.security.email': '备用邮箱',  
34 - 'app.settings.security.email-description': '已绑定邮箱:',  
35 - 'app.settings.security.mfa': 'MFA 设备',  
36 - 'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',  
37 - 'app.settings.security.modify': '修改',  
38 - 'app.settings.security.set': '设置',  
39 - 'app.settings.security.bind': '绑定',  
40 - 'app.settings.binding.taobao': '绑定淘宝',  
41 - 'app.settings.binding.taobao-description': '当前未绑定淘宝账号',  
42 - 'app.settings.binding.alipay': '绑定支付宝',  
43 - 'app.settings.binding.alipay-description': '当前未绑定支付宝账号',  
44 - 'app.settings.binding.dingding': '绑定钉钉',  
45 - 'app.settings.binding.dingding-description': '当前未绑定钉钉账号',  
46 - 'app.settings.binding.bind': '绑定',  
47 - 'app.settings.notification.password': '账户密码',  
48 - 'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',  
49 - 'app.settings.notification.messages': '系统消息',  
50 - 'app.settings.notification.messages-description': '系统消息将以站内信的形式通知',  
51 - 'app.settings.notification.todo': '待办任务',  
52 - 'app.settings.notification.todo-description': '待办任务将以站内信的形式通知',  
53 - 'app.settings.open': '开',  
54 - 'app.settings.close': '关',  
55 -};  
1 -export default {  
2 - 'app.settings.menuMap.basic': '基本設置',  
3 - 'app.settings.menuMap.security': '安全設置',  
4 - 'app.settings.menuMap.binding': '賬號綁定',  
5 - 'app.settings.menuMap.notification': '新消息通知',  
6 - 'app.settings.basic.avatar': '頭像',  
7 - 'app.settings.basic.change-avatar': '更換頭像',  
8 - 'app.settings.basic.email': '郵箱',  
9 - 'app.settings.basic.email-message': '請輸入您的郵箱!',  
10 - 'app.settings.basic.nickname': '昵稱',  
11 - 'app.settings.basic.nickname-message': '請輸入您的昵稱!',  
12 - 'app.settings.basic.profile': '個人簡介',  
13 - 'app.settings.basic.profile-message': '請輸入個人簡介!',  
14 - 'app.settings.basic.profile-placeholder': '個人簡介',  
15 - 'app.settings.basic.country': '國家/地區',  
16 - 'app.settings.basic.country-message': '請輸入您的國家或地區!',  
17 - 'app.settings.basic.geographic': '所在省市',  
18 - 'app.settings.basic.geographic-message': '請輸入您的所在省市!',  
19 - 'app.settings.basic.address': '街道地址',  
20 - 'app.settings.basic.address-message': '請輸入您的街道地址!',  
21 - 'app.settings.basic.phone': '聯系電話',  
22 - 'app.settings.basic.phone-message': '請輸入您的聯系電話!',  
23 - 'app.settings.basic.update': '更新基本信息',  
24 - 'app.settings.security.strong': '強',  
25 - 'app.settings.security.medium': '中',  
26 - 'app.settings.security.weak': '弱',  
27 - 'app.settings.security.password': '賬戶密碼',  
28 - 'app.settings.security.password-description': '當前密碼強度:',  
29 - 'app.settings.security.phone': '密保手機',  
30 - 'app.settings.security.phone-description': '已綁定手機:',  
31 - 'app.settings.security.question': '密保問題',  
32 - 'app.settings.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全',  
33 - 'app.settings.security.email': '備用郵箱',  
34 - 'app.settings.security.email-description': '已綁定郵箱:',  
35 - 'app.settings.security.mfa': 'MFA 設備',  
36 - 'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',  
37 - 'app.settings.security.modify': '修改',  
38 - 'app.settings.security.set': '設置',  
39 - 'app.settings.security.bind': '綁定',  
40 - 'app.settings.binding.taobao': '綁定淘寶',  
41 - 'app.settings.binding.taobao-description': '當前未綁定淘寶賬號',  
42 - 'app.settings.binding.alipay': '綁定支付寶',  
43 - 'app.settings.binding.alipay-description': '當前未綁定支付寶賬號',  
44 - 'app.settings.binding.dingding': '綁定釘釘',  
45 - 'app.settings.binding.dingding-description': '當前未綁定釘釘賬號',  
46 - 'app.settings.binding.bind': '綁定',  
47 - 'app.settings.notification.password': '賬戶密碼',  
48 - 'app.settings.notification.password-description': '其他用戶的消息將以站內信的形式通知',  
49 - 'app.settings.notification.messages': '系統消息',  
50 - 'app.settings.notification.messages-description': '系統消息將以站內信的形式通知',  
51 - 'app.settings.notification.todo': '待辦任務',  
52 - 'app.settings.notification.todo-description': '待辦任務將以站內信的形式通知',  
53 - 'app.settings.open': '開',  
54 - 'app.settings.close': '關',  
55 -};  
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 { query as queryUsers, queryCurrent } from '@/services/user';  
2 -  
3 -export default {  
4 - namespace: 'user',  
5 -  
6 - state: {  
7 - list: [],  
8 - currentUser: {},  
9 - },  
10 -  
11 - effects: {  
12 - *fetch(_, { call, put }) {  
13 - const response = yield call(queryUsers);  
14 - yield put({  
15 - type: 'save',  
16 - payload: response,  
17 - });  
18 - },  
19 - *fetchCurrent(_, { call, put }) {  
20 - const response = yield call(queryCurrent);  
21 - yield put({  
22 - type: 'saveCurrentUser',  
23 - payload: response,  
24 - });  
25 - },  
26 - },  
27 -  
28 - reducers: {  
29 - save(state, action) {  
30 - return {  
31 - ...state,  
32 - list: action.payload,  
33 - };  
34 - },  
35 - saveCurrentUser(state, action) {  
36 - return {  
37 - ...state,  
38 - currentUser: action.payload || {},  
39 - };  
40 - },  
41 - changeNotifyCount(state, action) {  
42 - return {  
43 - ...state,  
44 - currentUser: {  
45 - ...state.currentUser,  
46 - notifyCount: action.payload.totalCount,  
47 - unreadCount: action.payload.unreadCount,  
48 - },  
49 - };  
50 - },  
51 - },  
52 -};  
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 "jsx": "react", 9 "jsx": "react",
10 "allowSyntheticDefaultImports": true, 10 "allowSyntheticDefaultImports": true,
11 "moduleResolution": "node", 11 "moduleResolution": "node",
12 - "rootDirs": ["/src", "/test", "/mock","./typings"], 12 + "rootDirs": ["/src", "/test", "/mock", "./typings"],
13 "forceConsistentCasingInFileNames": true, 13 "forceConsistentCasingInFileNames": true,
14 "noImplicitReturns": true, 14 "noImplicitReturns": true,
15 "suppressImplicitAnyIndexErrors": true, 15 "suppressImplicitAnyIndexErrors": true,
@@ -17,10 +17,10 @@ @@ -17,10 +17,10 @@
17 "allowJs": true, 17 "allowJs": true,
18 "experimentalDecorators": true, 18 "experimentalDecorators": true,
19 "paths": { 19 "paths": {
20 - "@/*": ["./src/*"] 20 + "@/*": ["./ant-design-pro/*/src/*"]
21 } 21 }
22 }, 22 },
23 - "include": ["./src"], 23 + "include": ["./ant-design-pro"],
24 "exclude": [ 24 "exclude": [
25 "node_modules", 25 "node_modules",
26 "build", 26 "build",
注册登录 后发表评论