Browse Source

merge master

pull/1238/head
jim 9 years ago
parent
commit
6ef237e517
  1. 44
      .eslintrc
  2. 1
      .gitignore
  3. 2
      .prettierignore
  4. 11
      .prettierrc
  5. 3
      .stylelintrc
  6. 3
      .vscode/settings.json
  7. 22
      .webpackrc.js
  8. 15
      mock/api.js
  9. 10
      mock/chart.js
  10. 16
      mock/rule.js
  11. 13
      package.json
  12. 298
      src/common/menu.js
  13. 71
      src/common/router.js
  14. 2
      src/components/ActiveChart/index.js
  15. 22
      src/components/Authorized/AuthorizedRoute.js
  16. 16
      src/components/Authorized/CheckPermissions.test.js
  17. 2
      src/components/Authorized/PromiseRender.js
  18. 6
      src/components/Authorized/Secured.js
  19. 11
      src/components/Authorized/index.d.ts
  20. 2
      src/components/Authorized/index.js
  21. 4
      src/components/AvatarList/index.d.ts
  22. 18
      src/components/AvatarList/index.js
  23. 2
      src/components/AvatarList/index.less
  24. 2
      src/components/Charts/Bar/index.d.ts
  25. 4
      src/components/Charts/Bar/index.js
  26. 2
      src/components/Charts/ChartCard/index.d.ts
  27. 12
      src/components/Charts/ChartCard/index.js
  28. 2
      src/components/Charts/ChartCard/index.less
  29. 2
      src/components/Charts/Field/index.d.ts
  30. 2
      src/components/Charts/Field/index.less
  31. 2
      src/components/Charts/Gauge/index.d.ts
  32. 2
      src/components/Charts/Gauge/index.js
  33. 7
      src/components/Charts/MiniProgress/index.d.ts
  34. 15
      src/components/Charts/MiniProgress/index.js
  35. 4
      src/components/Charts/MiniProgress/index.less
  36. 12
      src/components/Charts/Pie/index.js
  37. 4
      src/components/Charts/Pie/index.less
  38. 2
      src/components/Charts/Radar/index.d.ts
  39. 19
      src/components/Charts/Radar/index.js
  40. 2
      src/components/Charts/Radar/index.less
  41. 2
      src/components/Charts/TagCloud/index.d.ts
  42. 4
      src/components/Charts/TagCloud/index.js
  43. 7
      src/components/Charts/TimelineChart/index.d.ts
  44. 2
      src/components/Charts/TimelineChart/index.js
  45. 4
      src/components/Charts/WaterWave/index.less
  46. 4
      src/components/Charts/autoHeight.js
  47. 2
      src/components/CountDown/index.d.ts
  48. 61
      src/components/CountDown/index.js
  49. 12
      src/components/DescriptionList/DescriptionList.js
  50. 5
      src/components/DescriptionList/index.d.ts
  51. 9
      src/components/DescriptionList/index.less
  52. 44
      src/components/EditableItem/index.js
  53. 2
      src/components/EditableItem/index.less
  54. 14
      src/components/EditableLinkGroup/index.js
  55. 2
      src/components/EditableLinkGroup/index.less
  56. 10
      src/components/Ellipsis/index.js
  57. 4
      src/components/Exception/index.d.ts
  58. 12
      src/components/Exception/index.js
  59. 4
      src/components/Exception/index.less
  60. 5
      src/components/FooterToolbar/index.d.ts
  61. 5
      src/components/FooterToolbar/index.js
  62. 6
      src/components/FooterToolbar/index.less
  63. 7
      src/components/GlobalFooter/index.d.ts
  64. 24
      src/components/GlobalFooter/index.js
  65. 4
      src/components/GlobalFooter/index.less
  66. 5
      src/components/GlobalHeader/index.js
  67. 5
      src/components/HeaderSearch/index.d.ts
  68. 21
      src/components/HeaderSearch/index.js
  69. 4
      src/components/HeaderSearch/index.less
  70. 6
      src/components/Login/LoginItem.js
  71. 4
      src/components/Login/index.d.ts
  72. 56
      src/components/Login/index.js
  73. 3
      src/components/Login/index.less
  74. 42
      src/components/Login/map.js
  75. 5
      src/components/NoticeIcon/NoticeIconTab.d.ts
  76. 15
      src/components/NoticeIcon/NoticeList.js
  77. 8
      src/components/NoticeIcon/NoticeList.less
  78. 14
      src/components/NoticeIcon/index.js
  79. 4
      src/components/NoticeIcon/index.less
  80. 5
      src/components/NumberInfo/index.d.ts
  81. 26
      src/components/NumberInfo/index.js
  82. 4
      src/components/NumberInfo/index.less
  83. 39
      src/components/PageHeader/index.js
  84. 24
      src/components/PageHeader/index.less
  85. 20
      src/components/PageHeader/index.test.js
  86. 8
      src/components/Result/index.js
  87. 2
      src/components/Result/index.less
  88. 26
      src/components/SiderMenu/BaseMeun.js
  89. 32
      src/components/SiderMenu/SiderMenu.js
  90. 20
      src/components/SiderMenu/SilderMenu.test.js
  91. 11
      src/components/SiderMenu/index.js
  92. 9
      src/components/SiderMenu/index.less
  93. 16
      src/components/StandardFormRow/index.js
  94. 2
      src/components/StandardFormRow/index.less
  95. 34
      src/components/StandardTable/index.js
  96. 2
      src/components/StandardTable/index.less
  97. 5
      src/components/TagSelect/TagSelectOption.d.ts
  98. 53
      src/components/TagSelect/index.js
  99. 6
      src/components/TagSelect/index.less
  100. 22
      src/components/Trend/index.js

44
.eslintrc

@ -1,7 +1,6 @@
{ {
"parser": "babel-eslint", "parser": "babel-eslint",
"extends": "airbnb", "extends": ["airbnb", "prettier"],
"plugins": ["compat"],
"env": { "env": {
"browser": true, "browser": true,
"node": true, "node": true,
@ -20,15 +19,18 @@
"react/jsx-no-bind": [0], "react/jsx-no-bind": [0],
"react/prop-types": [0], "react/prop-types": [0],
"react/prefer-stateless-function": [0], "react/prefer-stateless-function": [0],
"react/jsx-wrap-multilines": ["error", { "react/jsx-wrap-multilines": [
"declaration": "parens-new-line", "error",
"assignment": "parens-new-line", {
"return": "parens-new-line", "declaration": "parens-new-line",
"arrow": "parens-new-line", "assignment": "parens-new-line",
"condition": "parens-new-line", "return": "parens-new-line",
"logical": "parens-new-line", "arrow": "parens-new-line",
"prop": "ignore" "condition": "parens-new-line",
}], "logical": "parens-new-line",
"prop": "ignore"
}
],
"no-else-return": [0], "no-else-return": [0],
"no-restricted-syntax": [0], "no-restricted-syntax": [0],
"import/no-extraneous-dependencies": [0], "import/no-extraneous-dependencies": [0],
@ -43,18 +45,20 @@
"no-bitwise": [0], "no-bitwise": [0],
"no-cond-assign": [0], "no-cond-assign": [0],
"import/no-unresolved": [0], "import/no-unresolved": [0],
"comma-dangle": ["error", { "comma-dangle": [
"arrays": "always-multiline", "error",
"objects": "always-multiline", {
"imports": "always-multiline", "arrays": "always-multiline",
"exports": "always-multiline", "objects": "always-multiline",
"functions": "ignore" "imports": "always-multiline",
}], "exports": "always-multiline",
"functions": "ignore"
}
],
"object-curly-newline": [0], "object-curly-newline": [0],
"function-paren-newline": [0], "function-paren-newline": [0],
"no-restricted-globals": [0], "no-restricted-globals": [0],
"require-yield": [1], "require-yield": [1]
"compat/compat": "error"
}, },
"parserOptions": { "parserOptions": {
"ecmaFeatures": { "ecmaFeatures": {

1
.gitignore

@ -20,4 +20,3 @@ yarn.lock
package-lock.json package-lock.json
*bak *bak
jsconfig.json jsconfig.json
.prettierrc

2
.prettierignore

@ -0,0 +1,2 @@
**/*.md
**/*.svg

11
.prettierrc

@ -0,0 +1,11 @@
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}

3
.stylelintrc

@ -1,5 +1,5 @@
{ {
"extends": "stylelint-config-standard", "extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": { "rules": {
"selector-pseudo-class-no-unknown": null, "selector-pseudo-class-no-unknown": null,
"shorthand-property-no-redundant-values": null, "shorthand-property-no-redundant-values": null,
@ -17,6 +17,7 @@
"number-no-trailing-zeros": null, "number-no-trailing-zeros": null,
"rule-empty-line-before": null, "rule-empty-line-before": null,
"selector-combinator-space-after": null, "selector-combinator-space-after": null,
"selector-descendant-combinator-no-non-space": null,
"selector-list-comma-newline-after": null, "selector-list-comma-newline-after": null,
"selector-pseudo-element-colon-notation": null, "selector-pseudo-element-colon-notation": null,
"unit-no-unknown": null, "unit-no-unknown": null,

3
.vscode/settings.json

@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}

22
.webpackrc.js

@ -1,25 +1,25 @@
const path = require("path"); const path = require('path');
export default { export default {
entry: "src/index.js", entry: 'src/index.js',
extraBabelPlugins: [ extraBabelPlugins: [
"transform-decorators-legacy", 'transform-decorators-legacy',
["import", { libraryName: "antd", libraryDirectory: "es", style: true }] ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
], ],
env: { env: {
development: { development: {
extraBabelPlugins: ["dva-hmr"] extraBabelPlugins: ['dva-hmr'],
} },
}, },
alias: { alias: {
components: path.resolve(__dirname, "src/components/") components: path.resolve(__dirname, 'src/components/'),
}, },
ignoreMomentLocale: true, ignoreMomentLocale: true,
theme: "./src/theme.js", theme: './src/theme.js',
html: { html: {
template: "./src/index.ejs" template: './src/index.ejs',
}, },
disableDynamicImport: true, disableDynamicImport: true,
publicPath: "/", publicPath: '/',
hash: true hash: true,
}; };

15
mock/api.js

@ -69,21 +69,23 @@ export function fakeList(count) {
owner: user[i % 10], owner: user[i % 10],
title: titles[i % 8], title: titles[i % 8],
avatar: avatars[i % 8], avatar: avatars[i % 8],
cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)], cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - i % 4],
status: ['active', 'exception', 'normal'][i % 3], status: ['active', 'exception', 'normal'][i % 3],
percent: Math.ceil(Math.random() * 50) + 50, percent: Math.ceil(Math.random() * 50) + 50,
logo: avatars[i % 8], logo: avatars[i % 8],
href: 'https://ant.design', href: 'https://ant.design',
updatedAt: new Date(new Date().getTime() - (1000 * 60 * 60 * 2 * i)), updatedAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
createdAt: new Date(new Date().getTime() - (1000 * 60 * 60 * 2 * i)), createdAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
subDescription: desc[i % 5], subDescription: desc[i % 5],
description: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。', description:
'在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',
activeUser: Math.ceil(Math.random() * 100000) + 100000, activeUser: Math.ceil(Math.random() * 100000) + 100000,
newUser: Math.ceil(Math.random() * 1000) + 1000, newUser: Math.ceil(Math.random() * 1000) + 1000,
star: Math.ceil(Math.random() * 100) + 100, star: Math.ceil(Math.random() * 100) + 100,
like: Math.ceil(Math.random() * 100) + 100, like: Math.ceil(Math.random() * 100) + 100,
message: Math.ceil(Math.random() * 10) + 10, message: Math.ceil(Math.random() * 10) + 10,
content: '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。', content:
'段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。',
members: [ members: [
{ {
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png',
@ -117,7 +119,7 @@ export function getFakeList(req, res, u) {
const params = parse(url, true).query; const params = parse(url, true).query;
const count = (params.count * 1) || 20; const count = params.count * 1 || 20;
const result = fakeList(count); const result = fakeList(count);
sourceData = result; sourceData = result;
@ -330,7 +332,6 @@ export const getActivities = [
}, },
]; ];
export default { export default {
getNotice, getNotice,
getActivities, getActivities,

10
mock/chart.js

@ -7,7 +7,7 @@ const beginDay = new Date().getTime();
const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5]; const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5];
for (let i = 0; i < fakeY.length; i += 1) { for (let i = 0; i < fakeY.length; i += 1) {
visitData.push({ visitData.push({
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'), x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: fakeY[i], y: fakeY[i],
}); });
} }
@ -16,7 +16,7 @@ const visitData2 = [];
const fakeY2 = [1, 6, 4, 8, 3, 7, 2]; const fakeY2 = [1, 6, 4, 8, 3, 7, 2];
for (let i = 0; i < fakeY2.length; i += 1) { for (let i = 0; i < fakeY2.length; i += 1) {
visitData2.push({ visitData2.push({
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'), x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: fakeY2[i], y: fakeY2[i],
}); });
} }
@ -125,7 +125,7 @@ for (let i = 0; i < 10; i += 1) {
const offlineChartData = []; const offlineChartData = [];
for (let i = 0; i < 20; i += 1) { for (let i = 0; i < 20; i += 1) {
offlineChartData.push({ offlineChartData.push({
x: (new Date().getTime()) + (1000 * 60 * 30 * i), x: new Date().getTime() + 1000 * 60 * 30 * i,
y1: Math.floor(Math.random() * 100) + 10, y1: Math.floor(Math.random() * 100) + 10,
y2: Math.floor(Math.random() * 100) + 10, y2: Math.floor(Math.random() * 100) + 10,
}); });
@ -167,8 +167,8 @@ const radarTitleMap = {
contribute: '贡献', contribute: '贡献',
hot: '热度', hot: '热度',
}; };
radarOriginData.forEach((item) => { radarOriginData.forEach(item => {
Object.keys(item).forEach((key) => { Object.keys(item).forEach(key => {
if (key !== 'name') { if (key !== 'name') {
radarData.push({ radarData.push({
name: item.name, name: item.name,

16
mock/rule.js

@ -5,9 +5,12 @@ let tableListDataSource = [];
for (let i = 0; i < 46; i += 1) { for (let i = 0; i < 46; i += 1) {
tableListDataSource.push({ tableListDataSource.push({
key: i, key: i,
disabled: ((i % 6) === 0), disabled: i % 6 === 0,
href: 'https://ant.design', href: 'https://ant.design',
avatar: ['https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png'][i % 2], avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
name: `TradeCode ${i}`, name: `TradeCode ${i}`,
title: `一个任务名称 ${i}`, title: `一个任务名称 ${i}`,
owner: '曲丽丽', owner: '曲丽丽',
@ -43,7 +46,7 @@ export function getRule(req, res, u) {
if (params.status) { if (params.status) {
const status = params.status.split(','); const status = params.status.split(',');
let filterDataSource = []; let filterDataSource = [];
status.forEach((s) => { status.forEach(s => {
filterDataSource = filterDataSource.concat( filterDataSource = filterDataSource.concat(
[...dataSource].filter(data => parseInt(data.status, 10) === parseInt(s[0], 10)) [...dataSource].filter(data => parseInt(data.status, 10) === parseInt(s[0], 10))
); );
@ -95,7 +98,10 @@ export function postRule(req, res, u, b) {
tableListDataSource.unshift({ tableListDataSource.unshift({
key: i, key: i,
href: 'https://ant.design', href: 'https://ant.design',
avatar: ['https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png'][i % 2], avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
name: `TradeCode ${i}`, name: `TradeCode ${i}`,
title: `一个任务名称 ${i}`, title: `一个任务名称 ${i}`,
owner: '曲丽丽', owner: '曲丽丽',
@ -108,7 +114,7 @@ export function postRule(req, res, u, b) {
}); });
break; break;
case 'update': case 'update':
tableListDataSource = tableListDataSource.map((item) => { tableListDataSource = tableListDataSource.map(item => {
if (item.key === key) { if (item.key === key) {
return { ...item, desc, name }; return { ...item, desc, name };
} }

13
package.json

@ -17,7 +17,8 @@
"lint-staged:js": "eslint --ext .js", "lint-staged:js": "eslint --ext .js",
"test": "roadhog test", "test": "roadhog test",
"test:component": "roadhog test ./src/components", "test:component": "roadhog test ./src/components",
"test:all": "node ./tests/run-tests.js" "test:all": "node ./tests/run-tests.js",
"prettier": "prettier --write ./src/**/**/**/*"
}, },
"dependencies": { "dependencies": {
"@antv/data-set": "^0.8.0", "@antv/data-set": "^0.8.0",
@ -64,31 +65,31 @@
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-markdown": "^1.0.0-beta.6", "eslint-plugin-markdown": "^1.0.0-beta.6",
"eslint-plugin-react": "^7.0.1", "eslint-plugin-react": "^7.0.1",
"eslint-config-prettier": "^2.9.0",
"gh-pages": "^1.0.0", "gh-pages": "^1.0.0",
"husky": "^0.14.3", "husky": "^0.14.3",
"lint-staged": "^6.0.0", "lint-staged": "^6.0.0",
"mockjs": "^1.0.1-beta3", "mockjs": "^1.0.1-beta3",
"prettier": "1.11.1",
"pro-download": "^1.0.1", "pro-download": "^1.0.1",
"redbox-react": "^1.5.0", "redbox-react": "^1.5.0",
"regenerator-runtime": "^0.11.1", "regenerator-runtime": "^0.11.1",
"roadhog": "^2.3.0", "roadhog": "^2.3.0",
"roadhog-api-doc": "^0.3.4", "roadhog-api-doc": "^0.3.4",
"stylelint": "^8.4.0", "stylelint": "^8.4.0",
"stylelint-config-prettier": "^3.0.4",
"stylelint-config-standard": "^18.0.0" "stylelint-config-standard": "^18.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"puppeteer": "^1.1.1" "puppeteer": "^1.1.1"
}, },
"lint-staged": { "lint-staged": {
"**/*.{js,jsx,less}": ["prettier --wirter", "git add"],
"**/*.{js,jsx}": "lint-staged:js", "**/*.{js,jsx}": "lint-staged:js",
"**/*.less": "stylelint --syntax less" "**/*.less": "stylelint --syntax less"
}, },
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"
}, },
"browserslist": [ "browserslist": ["> 1%", "last 2 versions", "not ie <= 10"]
"> 1%",
"last 2 versions",
"not ie <= 10"
]
} }

298
src/common/menu.js

@ -1,134 +1,178 @@
import { isUrl } from '../utils/utils'; import { isUrl } from '../utils/utils';
const menuData = [{ const menuData = [
name: 'dashboard', {
icon: 'dashboard', name: 'dashboard',
path: 'dashboard', icon: 'dashboard',
children: [{ path: 'dashboard',
name: '分析页', children: [
path: 'analysis', {
}, { name: '分析页',
name: '监控页', path: 'analysis',
path: 'monitor', },
}, { {
name: '工作台', name: '监控页',
path: 'workplace', path: 'monitor',
// hideInBreadcrumb: true, },
// hideInMenu: true, {
}], name: '工作台',
}, { path: 'workplace',
name: '表单页', // hideInBreadcrumb: true,
icon: 'form', // hideInMenu: true,
path: 'form', },
children: [{ ],
name: '基础表单', },
path: 'basic-form', {
}, { name: '表单页',
name: '分步表单', icon: 'form',
path: 'step-form', path: 'form',
}, { children: [
name: '高级表单', {
authority: 'admin', name: '基础表单',
path: 'advanced-form', path: 'basic-form',
}], },
}, { {
name: '列表页', name: '分步表单',
icon: 'table', path: 'step-form',
path: 'list', },
children: [{ {
name: '查询表格', name: '高级表单',
path: 'table-list', authority: 'admin',
}, { path: 'advanced-form',
name: '标准列表', },
path: 'basic-list', ],
}, { },
name: '卡片列表', {
path: 'card-list', name: '列表页',
}, { icon: 'table',
name: '搜索列表', path: 'list',
path: 'search', children: [
children: [{ {
name: '搜索列表(文章)', name: '查询表格',
path: 'articles', path: 'table-list',
}, { },
name: '搜索列表(项目)', {
path: 'projects', name: '标准列表',
}, { path: 'basic-list',
name: '搜索列表(应用)', },
path: 'applications', {
}], name: '卡片列表',
}], path: 'card-list',
}, { },
name: '详情页', {
icon: 'profile', name: '搜索列表',
path: 'profile', path: 'search',
children: [{ children: [
name: '基础详情页', {
path: 'basic', name: '搜索列表(文章)',
}, { path: 'articles',
name: '高级详情页', },
path: 'advanced', {
authority: 'admin', name: '搜索列表(项目)',
}], path: 'projects',
}, { },
name: '结果页', {
icon: 'check-circle-o', name: '搜索列表(应用)',
path: 'result', path: 'applications',
children: [{ },
name: '成功', ],
path: 'success', },
}, { ],
name: '失败', },
path: 'fail', {
}], name: '详情页',
}, { icon: 'profile',
name: '异常页', path: 'profile',
icon: 'warning', children: [
path: 'exception', {
children: [{ name: '基础详情页',
name: '403', path: 'basic',
path: '403', },
}, { {
name: '404', name: '高级详情页',
path: '404', path: 'advanced',
}, { authority: 'admin',
name: '500', },
path: '500', ],
}, { },
name: '触发异常', {
path: 'trigger', name: '结果页',
hideInMenu: true, icon: 'check-circle-o',
}], path: 'result',
}, { children: [
name: '账户', {
icon: 'user', name: '成功',
path: 'user', path: 'success',
authority: 'guest', },
children: [{ {
name: '登录', name: '失败',
path: 'login', path: 'fail',
}, { },
name: '注册', ],
path: 'register', },
}, { {
name: '注册结果', name: '异常页',
path: 'register-result', icon: 'warning',
}], path: 'exception',
}, { children: [
name: '个人页', {
icon: 'user', name: '403',
path: 'account', path: '403',
children: [{ },
name: '个人中心', {
path: 'center', name: '404',
}, { path: '404',
name: '个人设置', },
path: 'settings', {
}], name: '500',
}]; path: '500',
},
{
name: '触发异常',
path: 'trigger',
hideInMenu: true,
},
],
},
{
name: '账户',
icon: 'user',
path: 'user',
authority: 'guest',
children: [
{
name: '登录',
path: 'login',
},
{
name: '注册',
path: 'register',
},
{
name: '注册结果',
path: 'register-result',
},
],
},
{
name: '个人页',
icon: 'user',
path: 'account',
children: [
{
name: '个人中心',
path: 'center',
},
{
name: '个人设置',
path: 'settings',
},
],
},
];
function formatter(data, parentPath = '/', parentAuthority) { function formatter(data, parentPath = '/', parentAuthority) {
return data.map((item) => { return data.map(item => {
let { path } = item; let { path } = item;
if (!isUrl(path)) { if (!isUrl(path)) {
path = parentPath + item.path; path = parentPath + item.path;

71
src/common/router.js

@ -5,25 +5,24 @@ import { getMenuData } from './menu';
let routerDataCache; let routerDataCache;
const modelNotExisted = (app, model) => ( const modelNotExisted = (app, model) =>
// eslint-disable-next-line // eslint-disable-next-line
!app._models.some(({ namespace }) => { !app._models.some(({ namespace }) => {
return namespace === model.substring(model.lastIndexOf('/') + 1); return namespace === model.substring(model.lastIndexOf('/') + 1);
}) });
);
// wrapper of dynamic // wrapper of dynamic
const dynamicWrapper = (app, models, component) => { const dynamicWrapper = (app, models, component) => {
// () => require('module') // () => require('module')
// transformed by babel-plugin-dynamic-import-node-sync // transformed by babel-plugin-dynamic-import-node-sync
if (component.toString().indexOf('.then(') < 0) { if (component.toString().indexOf('.then(') < 0) {
models.forEach((model) => { models.forEach(model => {
if (modelNotExisted(app, model)) { if (modelNotExisted(app, model)) {
// eslint-disable-next-line // eslint-disable-next-line
app.model(require(`../models/${model}`).default); app.model(require(`../models/${model}`).default);
} }
}); });
return (props) => { return props => {
if (!routerDataCache) { if (!routerDataCache) {
routerDataCache = getRouterData(app); routerDataCache = getRouterData(app);
} }
@ -36,20 +35,20 @@ const dynamicWrapper = (app, models, component) => {
// () => import('module') // () => import('module')
return dynamic({ return dynamic({
app, app,
models: () => models.filter( models: () =>
model => modelNotExisted(app, model)).map(m => import(`../models/${m}.js`) models.filter(model => modelNotExisted(app, model)).map(m => import(`../models/${m}.js`)),
),
// add routerData prop // add routerData prop
component: () => { component: () => {
if (!routerDataCache) { if (!routerDataCache) {
routerDataCache = getRouterData(app); routerDataCache = getRouterData(app);
} }
return component().then((raw) => { return component().then(raw => {
const Component = raw.default || raw; const Component = raw.default || raw;
return props => createElement(Component, { return props =>
...props, createElement(Component, {
routerData: routerDataCache, ...props,
}); routerData: routerDataCache,
});
}); });
}, },
}); });
@ -57,7 +56,7 @@ const dynamicWrapper = (app, models, component) => {
function getFlatMenuData(menus) { function getFlatMenuData(menus) {
let keys = {}; let keys = {};
menus.forEach((item) => { menus.forEach(item => {
if (item.children) { if (item.children) {
keys[item.path] = { ...item }; keys[item.path] = { ...item };
keys = { ...keys, ...getFlatMenuData(item.children) }; keys = { ...keys, ...getFlatMenuData(item.children) };
@ -68,10 +67,12 @@ function getFlatMenuData(menus) {
return keys; return keys;
} }
export const getRouterData = (app) => { export const getRouterData = app => {
const routerConfig = { const routerConfig = {
'/': { '/': {
component: dynamicWrapper(app, ['user', 'login', 'setting'], () => import('../layouts/LoadingPage')), component: dynamicWrapper(app, ['user', 'login', 'setting'], () =>
import('../layouts/LoadingPage')
),
}, },
'/dashboard/analysis': { '/dashboard/analysis': {
component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')), component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),
@ -80,7 +81,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')), component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')),
}, },
'/dashboard/workplace': { '/dashboard/workplace': {
component: dynamicWrapper(app, ['user', 'project', 'activities', 'chart'], () => import('../routes/Dashboard/Workplace')), component: dynamicWrapper(app, ['user', 'project', 'activities', 'chart'], () =>
import('../routes/Dashboard/Workplace')
),
// hideInBreadcrumb: true, // hideInBreadcrumb: true,
// name: '工作台', // name: '工作台',
// authority: 'admin', // authority: 'admin',
@ -131,7 +134,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/BasicProfile')), component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/BasicProfile')),
}, },
'/profile/advanced': { '/profile/advanced': {
component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/AdvancedProfile')), component: dynamicWrapper(app, ['profile'], () =>
import('../routes/Profile/AdvancedProfile')
),
}, },
'/result/success': { '/result/success': {
component: dynamicWrapper(app, [], () => import('../routes/Result/Success')), component: dynamicWrapper(app, [], () => import('../routes/Result/Success')),
@ -149,7 +154,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, [], () => import('../routes/Exception/500')), component: dynamicWrapper(app, [], () => import('../routes/Exception/500')),
}, },
'/exception/trigger': { '/exception/trigger': {
component: dynamicWrapper(app, ['error'], () => import('../routes/Exception/triggerException')), component: dynamicWrapper(app, ['error'], () =>
import('../routes/Exception/triggerException')
),
}, },
'/user': { '/user': {
component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')), component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')),
@ -164,7 +171,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, [], () => import('../routes/User/RegisterResult')), component: dynamicWrapper(app, [], () => import('../routes/User/RegisterResult')),
}, },
'/account/center': { '/account/center': {
component: dynamicWrapper(app, ['list', 'user', 'project'], () => import('../routes/Account/Center/Center')), component: dynamicWrapper(app, ['list', 'user', 'project'], () =>
import('../routes/Account/Center/Center')
),
}, },
'/account/center/articles': { '/account/center/articles': {
component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Articles')), component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Articles')),
@ -176,19 +185,29 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Projects')), component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Projects')),
}, },
'/account/settings': { '/account/settings': {
component: dynamicWrapper(app, ['geographic'], () => import('../routes/Account/Settings/Info')), component: dynamicWrapper(app, ['geographic'], () =>
import('../routes/Account/Settings/Info')
),
}, },
'/account/settings/base': { '/account/settings/base': {
component: dynamicWrapper(app, ['geographic'], () => import('../routes/Account/Settings/BaseView')), component: dynamicWrapper(app, ['geographic'], () =>
import('../routes/Account/Settings/BaseView')
),
}, },
'/account/settings/security': { '/account/settings/security': {
component: dynamicWrapper(app, ['geographic'], () => import('../routes/Account/Settings/SecurityView')), component: dynamicWrapper(app, ['geographic'], () =>
import('../routes/Account/Settings/SecurityView')
),
}, },
'/account/settings/binding': { '/account/settings/binding': {
component: dynamicWrapper(app, ['geographic'], () => import('../routes/Account/Settings/BindingView')), component: dynamicWrapper(app, ['geographic'], () =>
import('../routes/Account/Settings/BindingView')
),
}, },
'/account/settings/notification': { '/account/settings/notification': {
component: dynamicWrapper(app, ['geographic'], () => import('../routes/Account/Settings/NotificationView')), component: dynamicWrapper(app, ['geographic'], () =>
import('../routes/Account/Settings/NotificationView')
),
}, },
// '/user/:id': { // '/user/:id': {
// component: dynamicWrapper(app, [], () => import('../routes/User/SomeComponent')), // component: dynamicWrapper(app, [], () => import('../routes/User/SomeComponent')),
@ -201,7 +220,7 @@ export const getRouterData = (app) => {
// eg. {name,authority ...routerConfig } // eg. {name,authority ...routerConfig }
const routerData = {}; const routerData = {};
// The route matches the menu // The route matches the menu
Object.keys(routerConfig).forEach((path) => { Object.keys(routerConfig).forEach(path => {
// Regular match item name // Regular match item name
// eg. router /user/:id === /user/chen // eg. router /user/:id === /user/chen
const pathRegexp = pathToRegexp(path); const pathRegexp = pathToRegexp(path);

2
src/components/ActiveChart/index.js

@ -14,7 +14,7 @@ function getActiveData() {
for (let i = 0; i < 24; i += 1) { for (let i = 0; i < 24; i += 1) {
activeData.push({ activeData.push({
x: `${fixedZero(i)}:00`, x: `${fixedZero(i)}:00`,
y: Math.floor(Math.random() * 200) + (i * 50), y: Math.floor(Math.random() * 200) + i * 50,
}); });
} }
return activeData; return activeData;

22
src/components/Authorized/AuthorizedRoute.js

@ -4,29 +4,13 @@ import Authorized from './Authorized';
class AuthorizedRoute extends React.Component { class AuthorizedRoute extends React.Component {
render() { render() {
const { const { component: Component, render, authority, redirectPath, ...rest } = this.props;
component: Component,
render,
authority,
redirectPath,
...rest
} = this.props;
return ( return (
<Authorized <Authorized
authority={authority} authority={authority}
noMatch={ noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
<Route
{...rest}
render={() => <Redirect to={{ pathname: redirectPath }} />}
/>
}
> >
<Route <Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />
{...rest}
render={props =>
(Component ? <Component {...props} /> : render(props))
}
/>
</Authorized> </Authorized>
); );
} }

16
src/components/Authorized/CheckPermissions.test.js

@ -20,24 +20,16 @@ describe('test CheckPermissions', () => {
expect(checkPermissions('admin', 'user', target, error)).toEqual('error'); expect(checkPermissions('admin', 'user', target, error)).toEqual('error');
}); });
it('Correct Array permission authentication', () => { it('Correct Array permission authentication', () => {
expect(checkPermissions(['user', 'admin'], 'user', target, error)).toEqual( expect(checkPermissions(['user', 'admin'], 'user', target, error)).toEqual('ok');
'ok'
);
}); });
it('Wrong Array permission authentication,currentAuthority error', () => { it('Wrong Array permission authentication,currentAuthority error', () => {
expect( expect(checkPermissions(['user', 'admin'], 'user,admin', target, error)).toEqual('error');
checkPermissions(['user', 'admin'], 'user,admin', target, error)
).toEqual('error');
}); });
it('Wrong Array permission authentication', () => { it('Wrong Array permission authentication', () => {
expect(checkPermissions(['user', 'admin'], 'guest', target, error)).toEqual( expect(checkPermissions(['user', 'admin'], 'guest', target, error)).toEqual('error');
'error'
);
}); });
it('Wrong Function permission authentication', () => { it('Wrong Function permission authentication', () => {
expect(checkPermissions(() => false, 'guest', target, error)).toEqual( expect(checkPermissions(() => false, 'guest', target, error)).toEqual('error');
'error'
);
}); });
it('Correct Function permission authentication', () => { it('Correct Function permission authentication', () => {
expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok'); expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok');

2
src/components/Authorized/PromiseRender.js

@ -32,7 +32,7 @@ export default class PromiseRender extends React.PureComponent {
// AuthorizedRoute is already instantiated // AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated // Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated // Secured is not instantiated
checkIsInstantiation = (target) => { checkIsInstantiation = target => {
if (!React.isValidElement(target)) { if (!React.isValidElement(target)) {
return target; return target;
} }

6
src/components/Authorized/Secured.js

@ -5,15 +5,13 @@ import CheckPermissions from './CheckPermissions';
* 默认不能访问任何页面 * 默认不能访问任何页面
* default is "NULL" * default is "NULL"
*/ */
const Exception403 = () => ( const Exception403 = () => <Exception type="403" style={{ minHeight: 500, height: '80%' }} />;
<Exception type="403" style={{ minHeight: 500, height: '80%' }} />
);
// Determine whether the incoming component has been instantiated // Determine whether the incoming component has been instantiated
// AuthorizedRoute is already instantiated // AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated // Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated // Secured is not instantiated
const checkIsInstantiation = (target) => { const checkIsInstantiation = target => {
if (!React.isValidElement(target)) { if (!React.isValidElement(target)) {
return target; return target;
} }

11
src/components/Authorized/index.d.ts

@ -11,24 +11,19 @@ export type IReactComponent<P = any> =
| React.ClassicComponentClass<P>; | React.ClassicComponentClass<P>;
interface Secured { interface Secured {
(authority: authority, error?: React.ReactNode): <T extends IReactComponent>( (authority: authority, error?: React.ReactNode): <T extends IReactComponent>(target: T) => T;
target: T,
) => T;
} }
export interface AuthorizedRouteProps extends RouteProps { export interface AuthorizedRouteProps extends RouteProps {
authority: authority; authority: authority;
} }
export class AuthorizedRoute extends React.Component< export class AuthorizedRoute extends React.Component<AuthorizedRouteProps, any> {}
AuthorizedRouteProps,
any
> {}
interface check { interface check {
<T extends IReactComponent, S extends IReactComponent>( <T extends IReactComponent, S extends IReactComponent>(
authority: authority, authority: authority,
target: T, target: T,
Exception: S, Exception: S
): T | S; ): T | S;
} }

2
src/components/Authorized/index.js

@ -14,7 +14,7 @@ Authorized.check = check;
* use authority or getAuthority * use authority or getAuthority
* @param {string|()=>String} currentAuthority * @param {string|()=>String} currentAuthority
*/ */
const renderAuthorize = (currentAuthority) => { const renderAuthorize = currentAuthority => {
if (currentAuthority) { if (currentAuthority) {
if (currentAuthority.constructor.name === 'Function') { if (currentAuthority.constructor.name === 'Function') {
CURRENT = currentAuthority(); CURRENT = currentAuthority();

4
src/components/AvatarList/index.d.ts

@ -4,9 +4,7 @@ import AvatarItem from './AvatarItem';
export interface IAvatarListProps { export interface IAvatarListProps {
size?: 'large' | 'small' | 'mini' | 'default'; size?: 'large' | 'small' | 'mini' | 'default';
style?: React.CSSProperties; style?: React.CSSProperties;
children: children: React.ReactElement<AvatarItem> | Array<React.ReactElement<AvatarItem>>;
| React.ReactElement<AvatarItem>
| Array<React.ReactElement<AvatarItem>>;
} }
export default class AvatarList extends React.Component<IAvatarListProps, any> { export default class AvatarList extends React.Component<IAvatarListProps, any> {

18
src/components/AvatarList/index.js

@ -18,7 +18,7 @@ const AvatarList = ({ children, size, ...other }) => {
); );
}; };
const Item = ({ src, size, tips, onClick = (() => {}) }) => { const Item = ({ src, size, tips, onClick = () => {} }) => {
const cls = classNames(styles.avatarItem, { const cls = classNames(styles.avatarItem, {
[styles.avatarItemLarge]: size === 'large', [styles.avatarItemLarge]: size === 'large',
[styles.avatarItemSmall]: size === 'small', [styles.avatarItemSmall]: size === 'small',
@ -26,14 +26,14 @@ const Item = ({ src, size, tips, onClick = (() => {}) }) => {
}); });
return ( return (
<li className={cls} onClick={onClick} > <li className={cls} onClick={onClick}>
{ {tips ? (
tips ? ( <Tooltip title={tips}>
<Tooltip title={tips}> <Avatar src={src} size={size} style={{ cursor: 'pointer' }} />
<Avatar src={src} size={size} style={{ cursor: 'pointer' }} /> </Tooltip>
</Tooltip> ) : (
) : <Avatar src={src} size={size} /> <Avatar src={src} size={size} />
} )}
</li> </li>
); );
}; };

2
src/components/AvatarList/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.avatarList { .avatarList {
display: inline-block; display: inline-block;

2
src/components/Charts/Bar/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IBarProps { export interface IBarProps {
title: React.ReactNode; title: React.ReactNode;
color?: string; color?: string;

4
src/components/Charts/Bar/index.js

@ -46,11 +46,11 @@ class Bar extends Component {
} }
} }
handleRoot = (n) => { handleRoot = n => {
this.root = n; this.root = n;
}; };
handleRef = (n) => { handleRef = n => {
this.node = n; this.node = n;
}; };

2
src/components/Charts/ChartCard/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IChartCardProps { export interface IChartCardProps {
title: React.ReactNode; title: React.ReactNode;
action?: React.ReactNode; action?: React.ReactNode;

12
src/components/Charts/ChartCard/index.js

@ -4,7 +4,7 @@ import classNames from 'classnames';
import styles from './index.less'; import styles from './index.less';
const renderTotal = (total) => { const renderTotal = total => {
let totalDom; let totalDom;
switch (typeof total) { switch (typeof total) {
case undefined: case undefined:
@ -33,7 +33,9 @@ const ChartCard = ({
const content = ( const content = (
<div className={styles.chartCard}> <div className={styles.chartCard}>
<div <div
className={classNames(styles.chartTop, { [styles.chartTopMargin]: !children && !footer })} className={classNames(styles.chartTop, {
[styles.chartTopMargin]: !children && !footer,
})}
> >
<div className={styles.avatar}>{avatar}</div> <div className={styles.avatar}>{avatar}</div>
<div className={styles.metaWrap}> <div className={styles.metaWrap}>
@ -50,7 +52,11 @@ const ChartCard = ({
</div> </div>
)} )}
{footer && ( {footer && (
<div className={classNames(styles.footer, { [styles.footerMargin]: !children })}> <div
className={classNames(styles.footer, {
[styles.footerMargin]: !children,
})}
>
{footer} {footer}
</div> </div>
)} )}

2
src/components/Charts/ChartCard/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.chartCard { .chartCard {
position: relative; position: relative;

2
src/components/Charts/Field/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IFieldProps { export interface IFieldProps {
label: React.ReactNode; label: React.ReactNode;
value: React.ReactNode; value: React.ReactNode;

2
src/components/Charts/Field/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.field { .field {
white-space: nowrap; white-space: nowrap;

2
src/components/Charts/Gauge/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IGaugeProps { export interface IGaugeProps {
title: React.ReactNode; title: React.ReactNode;
color?: string; color?: string;

2
src/components/Charts/Gauge/index.js

@ -4,7 +4,7 @@ import autoHeight from '../autoHeight';
const { Arc, Html, Line } = Guide; const { Arc, Html, Line } = Guide;
const defaultFormatter = (val) => { const defaultFormatter = val => {
switch (val) { switch (val) {
case '2': case '2':
return '差'; return '差';

7
src/components/Charts/MiniProgress/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IMiniProgressProps { export interface IMiniProgressProps {
target: number; target: number;
color?: string; color?: string;
@ -7,7 +7,4 @@ export interface IMiniProgressProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class MiniProgress extends React.Component< export default class MiniProgress extends React.Component<IMiniProgressProps, any> {}
IMiniProgressProps,
any
> {}

15
src/components/Charts/MiniProgress/index.js

@ -6,21 +6,18 @@ import styles from './index.less';
const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => ( const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => (
<div className={styles.miniProgress}> <div className={styles.miniProgress}>
<Tooltip title={`目标值: ${target}%`}> <Tooltip title={`目标值: ${target}%`}>
<div <div className={styles.target} style={{ left: target ? `${target}%` : null }}>
className={styles.target} <span style={{ backgroundColor: color || null }} />
style={{ left: (target ? `${target}%` : null) }} <span style={{ backgroundColor: color || null }} />
>
<span style={{ backgroundColor: (color || null) }} />
<span style={{ backgroundColor: (color || null) }} />
</div> </div>
</Tooltip> </Tooltip>
<div className={styles.progressWrap}> <div className={styles.progressWrap}>
<div <div
className={styles.progress} className={styles.progress}
style={{ style={{
backgroundColor: (color || null), backgroundColor: color || null,
width: (percent ? `${percent}%` : null), width: percent ? `${percent}%` : null,
height: (strokeWidth || null), height: strokeWidth || null,
}} }}
/> />
</div> </div>

4
src/components/Charts/MiniProgress/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.miniProgress { .miniProgress {
padding: 5px 0; padding: 5px 0;
@ -9,7 +9,7 @@
position: relative; position: relative;
} }
.progress { .progress {
transition: all .4s cubic-bezier(.08, .82, .17, 1) 0s; transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
border-radius: 1px 0 0 1px; border-radius: 1px 0 0 1px;
background-color: @primary-color; background-color: @primary-color;
width: 0; width: 0;

12
src/components/Charts/Pie/index.js

@ -44,7 +44,7 @@ export default class Pie extends Component {
this.resize.cancel(); this.resize.cancel();
} }
getG2Instance = (chart) => { getG2Instance = chart => {
this.chart = chart; this.chart = chart;
}; };
@ -54,7 +54,7 @@ export default class Pie extends Component {
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形 const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
const items = geom.get('dataArray') || []; // 获取图形对应的 const items = geom.get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item) => { const legendData = items.map(item => {
/* eslint no-underscore-dangle:0 */ /* eslint no-underscore-dangle:0 */
const origin = item[0]._origin; const origin = item[0]._origin;
origin.color = item[0].color; origin.color = item[0].color;
@ -89,7 +89,7 @@ export default class Pie extends Component {
} }
} }
handleRoot = (n) => { handleRoot = n => {
this.root = n; this.root = n;
}; };
@ -154,7 +154,7 @@ export default class Pie extends Component {
if (percent) { if (percent) {
selected = false; selected = false;
tooltip = false; tooltip = false;
formatColor = (value) => { formatColor = value => {
if (value === '占比') { if (value === '占比') {
return color || 'rgba(24, 144, 255, 0.85)'; return color || 'rgba(24, 144, 255, 0.85)';
} else { } else {
@ -235,7 +235,9 @@ export default class Pie extends Component {
<li key={item.x} onClick={() => this.handleLegendClick(item, i)}> <li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
<span <span
className={styles.dot} className={styles.dot}
style={{ backgroundColor: !item.checked ? '#aaa' : item.color }} style={{
backgroundColor: !item.checked ? '#aaa' : item.color,
}}
/> />
<span className={styles.legendTitle}>{item.x}</span> <span className={styles.legendTitle}>{item.x}</span>
<Divider type="vertical" /> <Divider type="vertical" />

4
src/components/Charts/Pie/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.pie { .pie {
position: relative; position: relative;
@ -6,7 +6,7 @@
position: relative; position: relative;
} }
&.hasLegend .chart { &.hasLegend .chart {
width: ~"calc(100% - 240px)"; width: ~'calc(100% - 240px)';
} }
.legend { .legend {
position: absolute; position: absolute;

2
src/components/Charts/Radar/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IRadarProps { export interface IRadarProps {
title?: React.ReactNode; title?: React.ReactNode;
height: number; height: number;

19
src/components/Charts/Radar/index.js

@ -21,7 +21,7 @@ export default class Radar extends Component {
} }
} }
getG2Instance = (chart) => { getG2Instance = chart => {
this.chart = chart; this.chart = chart;
}; };
@ -31,7 +31,7 @@ export default class Radar extends Component {
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形 const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
const items = geom.get('dataArray') || []; // 获取图形对应的 const items = geom.get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item) => { const legendData = items.map(item => {
// eslint-disable-next-line // eslint-disable-next-line
const origins = item.map(t => t._origin); const origins = item.map(t => t._origin);
const result = { const result = {
@ -49,7 +49,7 @@ export default class Radar extends Component {
}); });
}; };
handleRef = (n) => { handleRef = n => {
this.node = n; this.node = n;
}; };
@ -60,9 +60,7 @@ export default class Radar extends Component {
const { legendData } = this.state; const { legendData } = this.state;
legendData[i] = newItem; legendData[i] = newItem;
const filteredLegendData = legendData const filteredLegendData = legendData.filter(l => l.checked).map(l => l.name);
.filter(l => l.checked)
.map(l => l.name);
if (this.chart) { if (this.chart) {
this.chart.filter('name', val => filteredLegendData.indexOf(val) > -1); this.chart.filter('name', val => filteredLegendData.indexOf(val) > -1);
@ -143,12 +141,7 @@ export default class Radar extends Component {
}, },
}} }}
/> />
<Geom <Geom type="line" position="label*value" color={['name', colors]} size={1} />
type="line"
position="label*value"
color={['name', colors]}
size={1}
/>
<Geom <Geom
type="point" type="point"
position="label*value" position="label*value"
@ -178,7 +171,7 @@ export default class Radar extends Component {
<h6>{item.value}</h6> <h6>{item.value}</h6>
</div> </div>
</Col> </Col>
))} ))}
</Row> </Row>
)} )}
</div> </div>

2
src/components/Charts/Radar/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.radar { .radar {
.legend { .legend {

2
src/components/Charts/TagCloud/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface ITagCloudProps { export interface ITagCloudProps {
data: Array<{ data: Array<{
name: string; name: string;

4
src/components/Charts/TagCloud/index.js

@ -39,7 +39,7 @@ class TagCloud extends Component {
this.renderChart(); this.renderChart();
}; };
saveRootRef = (node) => { saveRootRef = node => {
this.root = node; this.root = node;
}; };
@ -77,7 +77,7 @@ class TagCloud extends Component {
@Bind() @Bind()
@Debounce(500) @Debounce(500)
renderChart = (nextProps) => { renderChart = nextProps => {
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C']; // const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const { data, height } = nextProps || this.props; const { data, height } = nextProps || this.props;

7
src/components/Charts/TimelineChart/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface ITimelineChartProps { export interface ITimelineChartProps {
data: Array<{ data: Array<{
x: string; x: string;
@ -11,7 +11,4 @@ export interface ITimelineChartProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class TimelineChart extends React.Component< export default class TimelineChart extends React.Component<ITimelineChartProps, any> {}
ITimelineChartProps,
any
> {}

2
src/components/Charts/TimelineChart/index.js

@ -48,7 +48,7 @@ export default class TimelineChart extends React.Component {
.source(data) .source(data)
.transform({ .transform({
type: 'filter', type: 'filter',
callback: (obj) => { callback: obj => {
const date = obj.x; const date = obj.x;
return date <= ds.state.end && date >= ds.state.start; return date <= ds.state.end && date >= ds.state.start;
}, },

4
src/components/Charts/WaterWave/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.waterWave { .waterWave {
display: inline-block; display: inline-block;
@ -22,7 +22,7 @@
} }
} }
.waterWaveCanvasWrapper { .waterWaveCanvasWrapper {
transform: scale(.5); transform: scale(0.5);
transform-origin: 0 0; transform-origin: 0 0;
} }
} }

4
src/components/Charts/autoHeight.js

@ -30,7 +30,7 @@ function getAutoHeight(n) {
return height; return height;
} }
const autoHeight = () => (WrappedComponent) => { const autoHeight = () => WrappedComponent => {
return class extends React.Component { return class extends React.Component {
state = { state = {
computedHeight: 0, computedHeight: 0,
@ -45,7 +45,7 @@ const autoHeight = () => (WrappedComponent) => {
} }
} }
handleRoot = (node) => { handleRoot = node => {
this.root = node; this.root = node;
}; };

2
src/components/CountDown/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface ICountDownProps { export interface ICountDownProps {
format?: (time: number) => void; format?: (time: number) => void;
target: Date | number; target: Date | number;

61
src/components/CountDown/index.js

@ -23,11 +23,14 @@ class CountDown extends Component {
if (this.props.target !== nextProps.target) { if (this.props.target !== nextProps.target) {
clearTimeout(this.timer); clearTimeout(this.timer);
const { lastTime } = this.initTime(nextProps); const { lastTime } = this.initTime(nextProps);
this.setState({ this.setState(
lastTime, {
}, () => { lastTime,
this.tick(); },
}); () => {
this.tick();
}
);
} }
} }
@ -37,7 +40,7 @@ class CountDown extends Component {
timer = 0; timer = 0;
interval = 1000; interval = 1000;
initTime = (props) => { initTime = props => {
let lastTime = 0; let lastTime = 0;
let targetTime = 0; let targetTime = 0;
try { try {
@ -54,21 +57,23 @@ class CountDown extends Component {
return { return {
lastTime: lastTime < 0 ? 0 : lastTime, lastTime: lastTime < 0 ? 0 : lastTime,
}; };
} };
// defaultFormat = time => ( // defaultFormat = time => (
// <span>{moment(time).format('hh:mm:ss')}</span> // <span>{moment(time).format('hh:mm:ss')}</span>
// ); // );
defaultFormat = (time) => { defaultFormat = time => {
const hours = 60 * 60 * 1000; const hours = 60 * 60 * 1000;
const minutes = 60 * 1000; const minutes = 60 * 1000;
const h = Math.floor(time / hours); const h = Math.floor(time / hours);
const m = Math.floor((time - (h * hours)) / minutes); const m = Math.floor((time - h * hours) / minutes);
const s = Math.floor((time - (h * hours) - (m * minutes)) / 1000); const s = Math.floor((time - h * hours - m * minutes) / 1000);
return ( return (
<span>{fixedZero(h)}:{fixedZero(m)}:{fixedZero(s)}</span> <span>
{fixedZero(h)}:{fixedZero(m)}:{fixedZero(s)}
</span>
); );
} };
tick = () => { tick = () => {
const { onEnd } = this.props; const { onEnd } = this.props;
let { lastTime } = this.state; let { lastTime } = this.state;
@ -76,30 +81,36 @@ class CountDown extends Component {
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
if (lastTime < this.interval) { if (lastTime < this.interval) {
clearTimeout(this.timer); clearTimeout(this.timer);
this.setState({ this.setState(
lastTime: 0, {
}, () => { lastTime: 0,
if (onEnd) { },
onEnd(); () => {
if (onEnd) {
onEnd();
}
} }
}); );
} else { } else {
lastTime -= this.interval; lastTime -= this.interval;
this.setState({ this.setState(
lastTime, {
}, () => { lastTime,
this.tick(); },
}); () => {
this.tick();
}
);
} }
}, this.interval); }, this.interval);
} };
render() { render() {
const { format = this.defaultFormat, onEnd, ...rest } = this.props; const { format = this.defaultFormat, onEnd, ...rest } = this.props;
const { lastTime } = this.state; const { lastTime } = this.state;
const result = format(lastTime); const result = format(lastTime);
return (<span {...rest}>{result}</span>); return <span {...rest}>{result}</span>;
} }
} }

12
src/components/DescriptionList/DescriptionList.js

@ -3,8 +3,16 @@ import classNames from 'classnames';
import { Row } from 'antd'; import { Row } from 'antd';
import styles from './index.less'; import styles from './index.less';
export default ({ className, title, col = 3, layout = 'horizontal', gutter = 32, export default ({
children, size, ...restProps }) => { className,
title,
col = 3,
layout = 'horizontal',
gutter = 32,
children,
size,
...restProps
}) => {
const clsString = classNames(styles.descriptionList, styles[layout], className, { const clsString = classNames(styles.descriptionList, styles[layout], className, {
[styles.small]: size === 'small', [styles.small]: size === 'small',
[styles.large]: size === 'large', [styles.large]: size === 'large',

5
src/components/DescriptionList/index.d.ts

@ -10,9 +10,6 @@ export interface IDescriptionListProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class DescriptionList extends React.Component< export default class DescriptionList extends React.Component<IDescriptionListProps, any> {
IDescriptionListProps,
any
> {
public static Description: typeof Description; public static Description: typeof Description;
} }

9
src/components/DescriptionList/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.descriptionList { .descriptionList {
// offset the padding-bottom of last row // offset the padding-bottom of last row
@ -25,10 +25,10 @@
display: table-cell; display: table-cell;
&:after { &:after {
content: ":"; content: ':';
margin: 0 8px 0 2px; margin: 0 8px 0 2px;
position: relative; position: relative;
top: -.5px; top: -0.5px;
} }
} }
@ -51,7 +51,8 @@
margin-bottom: 12px; margin-bottom: 12px;
color: @text-color; color: @text-color;
} }
.term, .detail { .term,
.detail {
padding-bottom: 8px; padding-bottom: 8px;
} }
} }

44
src/components/EditableItem/index.js

@ -7,48 +7,34 @@ export default class EditableItem extends PureComponent {
value: this.props.value, value: this.props.value,
editable: false, editable: false,
}; };
handleChange = (e) => { handleChange = e => {
const { value } = e.target; const { value } = e.target;
this.setState({ value }); this.setState({ value });
} };
check = () => { check = () => {
this.setState({ editable: false }); this.setState({ editable: false });
if (this.props.onChange) { if (this.props.onChange) {
this.props.onChange(this.state.value); this.props.onChange(this.state.value);
} }
} };
edit = () => { edit = () => {
this.setState({ editable: true }); this.setState({ editable: true });
} };
render() { render() {
const { value, editable } = this.state; const { value, editable } = this.state;
return ( return (
<div className={styles.editableItem}> <div className={styles.editableItem}>
{ {editable ? (
editable ? ( <div className={styles.wrapper}>
<div className={styles.wrapper}> <Input value={value} onChange={this.handleChange} onPressEnter={this.check} />
<Input <Icon type="check" className={styles.icon} onClick={this.check} />
value={value} </div>
onChange={this.handleChange} ) : (
onPressEnter={this.check} <div className={styles.wrapper}>
/> <span>{value || ' '}</span>
<Icon <Icon type="edit" className={styles.icon} onClick={this.edit} />
type="check" </div>
className={styles.icon} )}
onClick={this.check}
/>
</div>
) : (
<div className={styles.wrapper}>
<span>{value || ' '}</span>
<Icon
type="edit"
className={styles.icon}
onClick={this.edit}
/>
</div>
)
}
</div> </div>
); );
} }

2
src/components/EditableItem/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.editableItem { .editableItem {
line-height: @input-height-base; line-height: @input-height-base;

14
src/components/EditableLinkGroup/index.js

@ -22,15 +22,17 @@ class EditableLinkGroup extends PureComponent {
const { links, linkElement, onAdd } = this.props; const { links, linkElement, onAdd } = this.props;
return ( return (
<div className={styles.linkGroup}> <div className={styles.linkGroup}>
{ {links.map(link =>
links.map(link => ( createElement(
createElement(linkElement, { linkElement,
{
key: `linkGroup-item-${link.id || link.title}`, key: `linkGroup-item-${link.id || link.title}`,
to: link.href, to: link.href,
href: link.href, href: link.href,
}, link.title) },
)) link.title
} )
)}
{ {
<Button size="small" type="primary" ghost onClick={onAdd} icon="plus"> <Button size="small" type="primary" ghost onClick={onAdd} icon="plus">
添加 添加

2
src/components/EditableLinkGroup/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.linkGroup { .linkGroup {
padding: 20px 0 8px 24px; padding: 20px 0 8px 24px;

10
src/components/Ellipsis/index.js

@ -125,23 +125,23 @@ export default class Ellipsis extends Component {
} }
}; };
handleRoot = (n) => { handleRoot = n => {
this.root = n; this.root = n;
}; };
handleContent = (n) => { handleContent = n => {
this.content = n; this.content = n;
}; };
handleNode = (n) => { handleNode = n => {
this.node = n; this.node = n;
}; };
handleShadow = (n) => { handleShadow = n => {
this.shadow = n; this.shadow = n;
}; };
handleShadowChildren = (n) => { handleShadowChildren = n => {
this.shadowChildren = n; this.shadowChildren = n;
}; };

4
src/components/Exception/index.d.ts

@ -1,6 +1,6 @@
import * as React from "react"; import * as React from 'react';
export interface IExceptionProps { export interface IExceptionProps {
type?: "403" | "404" | "500"; type?: '403' | '404' | '500';
title?: React.ReactNode; title?: React.ReactNode;
desc?: React.ReactNode; desc?: React.ReactNode;
img?: string; img?: string;

12
src/components/Exception/index.js

@ -19,13 +19,15 @@ export default ({ className, linkElement = 'a', type, title, desc, img, actions,
<h1>{title || config[pageType].title}</h1> <h1>{title || config[pageType].title}</h1>
<div className={styles.desc}>{desc || config[pageType].desc}</div> <div className={styles.desc}>{desc || config[pageType].desc}</div>
<div className={styles.actions}> <div className={styles.actions}>
{ {actions ||
actions || createElement(
createElement(linkElement, { linkElement,
{
to: '/', to: '/',
href: '/', href: '/',
}, <Button type="primary">返回首页</Button>) },
} <Button type="primary">返回首页</Button>
)}
</div> </div>
</div> </div>
</div> </div>

4
src/components/Exception/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.exception { .exception {
display: flex; display: flex;
@ -12,7 +12,7 @@
zoom: 1; zoom: 1;
&:before, &:before,
&:after { &:after {
content: " "; content: ' ';
display: table; display: table;
} }
&:after { &:after {

5
src/components/FooterToolbar/index.d.ts

@ -4,7 +4,4 @@ export interface IFooterToolbarProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class FooterToolbar extends React.Component< export default class FooterToolbar extends React.Component<IFooterToolbarProps, any> {}
IFooterToolbarProps,
any
> {}

5
src/components/FooterToolbar/index.js

@ -6,10 +6,7 @@ export default class FooterToolbar extends Component {
render() { render() {
const { children, className, extra, ...restProps } = this.props; const { children, className, extra, ...restProps } = this.props;
return ( return (
<div <div className={classNames(className, styles.toolbar)} {...restProps}>
className={classNames(className, styles.toolbar)}
{...restProps}
>
<div className={styles.left}>{extra}</div> <div className={styles.left}>{extra}</div>
<div className={styles.right}>{children}</div> <div className={styles.right}>{children}</div>
</div> </div>

6
src/components/FooterToolbar/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.toolbar { .toolbar {
position: fixed; position: fixed;
@ -7,14 +7,14 @@
right: 0; right: 0;
height: 56px; height: 56px;
line-height: 56px; line-height: 56px;
box-shadow: 0 -1px 2px rgba(0, 0, 0, .03); box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
background: #fff; background: #fff;
border-top: 1px solid @border-color-split; border-top: 1px solid @border-color-split;
padding: 0 24px; padding: 0 24px;
z-index: 9; z-index: 9;
&:after { &:after {
content: ""; content: '';
display: block; display: block;
clear: both; clear: both;
} }

7
src/components/GlobalFooter/index.d.ts

@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
export interface IGlobalFooterProps { export interface IGlobalFooterProps {
links?: Array<{ links?: Array<{
title: React.ReactNode; title: React.ReactNode;
@ -9,7 +9,4 @@ export interface IGlobalFooterProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class GlobalFooter extends React.Component< export default class GlobalFooter extends React.Component<IGlobalFooterProps, any> {}
IGlobalFooterProps,
any
> {}

24
src/components/GlobalFooter/index.js

@ -6,21 +6,15 @@ export default ({ className, links, copyright }) => {
const clsString = classNames(styles.globalFooter, className); const clsString = classNames(styles.globalFooter, className);
return ( return (
<div className={clsString}> <div className={clsString}>
{ {links && (
links && ( <div className={styles.links}>
<div className={styles.links}> {links.map(link => (
{links.map(link => ( <a key={link.key} target={link.blankTarget ? '_blank' : '_self'} href={link.href}>
<a {link.title}
key={link.key} </a>
target={link.blankTarget ? '_blank' : '_self'} ))}
href={link.href} </div>
> )}
{link.title}
</a>
))}
</div>
)
}
{copyright && <div className={styles.copyright}>{copyright}</div>} {copyright && <div className={styles.copyright}>{copyright}</div>}
</div> </div>
); );

4
src/components/GlobalFooter/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.globalFooter { .globalFooter {
padding: 0 16px; padding: 0 16px;
@ -10,7 +10,7 @@
a { a {
color: @text-color-secondary; color: @text-color-secondary;
transition: all .3s; transition: all 0.3s;
&:not(:last-child) { &:not(:last-child) {
margin-right: 40px; margin-right: 40px;

5
src/components/GlobalHeader/index.js

@ -10,11 +10,12 @@ export default class GlobalHeader extends PureComponent {
this.triggerResizeEvent.cancel(); this.triggerResizeEvent.cancel();
} }
@Debounce(600) @Debounce(600)
triggerResizeEvent() { // eslint-disable-line triggerResizeEvent = () => {
// eslint-disable-line
const event = document.createEvent('HTMLEvents'); const event = document.createEvent('HTMLEvents');
event.initEvent('resize', true, false); event.initEvent('resize', true, false);
window.dispatchEvent(event); window.dispatchEvent(event);
} };
toggle = () => { toggle = () => {
const { collapsed, onCollapse } = this.props; const { collapsed, onCollapse } = this.props;
onCollapse(!collapsed); onCollapse(!collapsed);

5
src/components/HeaderSearch/index.d.ts

@ -8,7 +8,4 @@ export interface IHeaderSearchProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class HeaderSearch extends React.Component< export default class HeaderSearch extends React.Component<IHeaderSearchProps, any> {}
IHeaderSearchProps,
any
> {}

21
src/components/HeaderSearch/index.js

@ -28,42 +28,39 @@ export default class HeaderSearch extends PureComponent {
componentWillUnmount() { componentWillUnmount() {
clearTimeout(this.timeout); clearTimeout(this.timeout);
} }
onKeyDown = (e) => { onKeyDown = e => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this.props.onPressEnter(this.state.value); // Fix duplicate onPressEnter this.props.onPressEnter(this.state.value); // Fix duplicate onPressEnter
}, 0); }, 0);
} }
} };
onChange = (value) => { onChange = value => {
this.setState({ value }); this.setState({ value });
if (this.props.onChange) { if (this.props.onChange) {
this.props.onChange(); this.props.onChange();
} }
} };
enterSearchMode = () => { enterSearchMode = () => {
this.setState({ searchMode: true }, () => { this.setState({ searchMode: true }, () => {
if (this.state.searchMode) { if (this.state.searchMode) {
this.input.focus(); this.input.focus();
} }
}); });
} };
leaveSearchMode = () => { leaveSearchMode = () => {
this.setState({ this.setState({
searchMode: false, searchMode: false,
value: '', value: '',
}); });
} };
render() { render() {
const { className, placeholder, ...restProps } = this.props; const { className, placeholder, ...restProps } = this.props;
const inputClass = classNames(styles.input, { const inputClass = classNames(styles.input, {
[styles.show]: this.state.searchMode, [styles.show]: this.state.searchMode,
}); });
return ( return (
<span <span className={classNames(className, styles.headerSearch)} onClick={this.enterSearchMode}>
className={classNames(className, styles.headerSearch)}
onClick={this.enterSearchMode}
>
<Icon type="search" key="Icon" /> <Icon type="search" key="Icon" />
<AutoComplete <AutoComplete
key="AutoComplete" key="AutoComplete"
@ -74,7 +71,9 @@ export default class HeaderSearch extends PureComponent {
> >
<Input <Input
placeholder={placeholder} placeholder={placeholder}
ref={(node) => { this.input = node; }} ref={node => {
this.input = node;
}}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onBlur={this.leaveSearchMode} onBlur={this.leaveSearchMode}
/> />

4
src/components/HeaderSearch/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.headerSearch { .headerSearch {
:global(.anticon-search) { :global(.anticon-search) {
@ -6,7 +6,7 @@
font-size: 16px; font-size: 16px;
} }
.input { .input {
transition: width .3s, margin-left .3s; transition: width 0.3s, margin-left 0.3s;
width: 0; width: 0;
background: transparent; background: transparent;
border-radius: 0; border-radius: 0;

6
src/components/Login/LoginItem.js

@ -8,7 +8,7 @@ import map from './map';
const FormItem = Form.Item; const FormItem = Form.Item;
function generator({ defaultProps, defaultRules, type }) { function generator({ defaultProps, defaultRules, type }) {
return (WrappedComponent) => { return WrappedComponent => {
return class BasicComponent extends Component { return class BasicComponent extends Component {
static contextTypes = { static contextTypes = {
form: PropTypes.object, form: PropTypes.object,
@ -41,7 +41,7 @@ function generator({ defaultProps, defaultRules, type }) {
clearInterval(this.interval); clearInterval(this.interval);
} }
}, 1000); }, 1000);
} };
render() { render() {
const { getFieldDecorator } = this.context.form; const { getFieldDecorator } = this.context.form;
const options = {}; const options = {};
@ -93,7 +93,7 @@ function generator({ defaultProps, defaultRules, type }) {
} }
const LoginItem = {}; const LoginItem = {};
Object.keys(map).forEach((item) => { Object.keys(map).forEach(item => {
LoginItem[item] = generator({ LoginItem[item] = generator({
defaultProps: map[item].props, defaultProps: map[item].props,
defaultRules: map[item].rules, defaultRules: map[item].rules,

4
src/components/Login/index.d.ts

@ -1,5 +1,5 @@
import * as React from "react"; import * as React from 'react';
import Button from "antd/lib/button"; import Button from 'antd/lib/button';
export interface LoginProps { export interface LoginProps {
defaultActiveKey?: string; defaultActiveKey?: string;
onTabChange?: (key: string) => void; onTabChange?: (key: string) => void;

56
src/components/Login/index.js

@ -34,19 +34,19 @@ class Login extends Component {
getChildContext() { getChildContext() {
return { return {
tabUtil: { tabUtil: {
addTab: (id) => { addTab: id => {
this.setState({ this.setState({
tabs: [...this.state.tabs, id], tabs: [...this.state.tabs, id],
}); });
}, },
removeTab: (id) => { removeTab: id => {
this.setState({ this.setState({
tabs: this.state.tabs.filter(currentId => currentId !== id), tabs: this.state.tabs.filter(currentId => currentId !== id),
}); });
}, },
}, },
form: this.props.form, form: this.props.form,
updateActive: (activeItem) => { updateActive: activeItem => {
const { type, active } = this.state; const { type, active } = this.state;
if (active[type]) { if (active[type]) {
active[type].push(activeItem); active[type].push(activeItem);
@ -59,28 +59,26 @@ class Login extends Component {
}, },
}; };
} }
onSwitch = (type) => { onSwitch = type => {
this.setState({ this.setState({
type, type,
}); });
this.props.onTabChange(type); this.props.onTabChange(type);
} };
handleSubmit = (e) => { handleSubmit = e => {
e.preventDefault(); e.preventDefault();
const { active, type } = this.state; const { active, type } = this.state;
const activeFileds = active[type]; const activeFileds = active[type];
this.props.form.validateFields(activeFileds, { force: true }, this.props.form.validateFields(activeFileds, { force: true }, (err, values) => {
(err, values) => { this.props.onSubmit(err, values);
this.props.onSubmit(err, values); });
} };
);
}
render() { render() {
const { className, children } = this.props; const { className, children } = this.props;
const { type, tabs } = this.state; const { type, tabs } = this.state;
const TabChildren = []; const TabChildren = [];
const otherChildren = []; const otherChildren = [];
React.Children.forEach(children, (item) => { React.Children.forEach(children, item => {
if (!item) { if (!item) {
return; return;
} }
@ -94,21 +92,21 @@ class Login extends Component {
return ( return (
<div className={classNames(className, styles.login)}> <div className={classNames(className, styles.login)}>
<Form onSubmit={this.handleSubmit}> <Form onSubmit={this.handleSubmit}>
{ {tabs.length ? (
tabs.length ? ( <div>
<div> <Tabs
<Tabs animated={false}
animated={false} className={styles.tabs}
className={styles.tabs} activeKey={type}
activeKey={type} onChange={this.onSwitch}
onChange={this.onSwitch} >
> {TabChildren}
{TabChildren} </Tabs>
</Tabs> {otherChildren}
{otherChildren} </div>
</div> ) : (
) : [...children] [...children]
} )}
</Form> </Form>
</div> </div>
); );
@ -117,7 +115,7 @@ class Login extends Component {
Login.Tab = LoginTab; Login.Tab = LoginTab;
Login.Submit = LoginSubmit; Login.Submit = LoginSubmit;
Object.keys(LoginItem).forEach((item) => { Object.keys(LoginItem).forEach(item => {
Login[item] = LoginItem[item]; Login[item] = LoginItem[item];
}); });

3
src/components/Login/index.less

@ -1,7 +1,6 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.login { .login {
.tabs { .tabs {
padding: 0 2px; padding: 0 2px;
margin: 0 -2px; margin: 0 -2px;

42
src/components/Login/map.js

@ -10,9 +10,12 @@ const map = {
prefix: <Icon type="user" className={styles.prefixIcon} />, prefix: <Icon type="user" className={styles.prefixIcon} />,
placeholder: 'admin', placeholder: 'admin',
}, },
rules: [{ rules: [
required: true, message: 'Please enter username!', {
}], required: true,
message: 'Please enter username!',
},
],
}, },
Password: { Password: {
component: Input, component: Input,
@ -22,9 +25,12 @@ const map = {
type: 'password', type: 'password',
placeholder: '888888', placeholder: '888888',
}, },
rules: [{ rules: [
required: true, message: 'Please enter password!', {
}], required: true,
message: 'Please enter password!',
},
],
}, },
Mobile: { Mobile: {
component: Input, component: Input,
@ -33,11 +39,16 @@ const map = {
prefix: <Icon type="mobile" className={styles.prefixIcon} />, prefix: <Icon type="mobile" className={styles.prefixIcon} />,
placeholder: 'mobile number', placeholder: 'mobile number',
}, },
rules: [{ rules: [
required: true, message: 'Please enter mobile number!', {
}, { required: true,
pattern: /^1\d{10}$/, message: 'Wrong mobile number format!', message: 'Please enter mobile number!',
}], },
{
pattern: /^1\d{10}$/,
message: 'Wrong mobile number format!',
},
],
}, },
Captcha: { Captcha: {
component: Input, component: Input,
@ -46,9 +57,12 @@ const map = {
prefix: <Icon type="mail" className={styles.prefixIcon} />, prefix: <Icon type="mail" className={styles.prefixIcon} />,
placeholder: 'captcha', placeholder: 'captcha',
}, },
rules: [{ rules: [
required: true, message: 'Please enter Captcha!', {
}], required: true,
message: 'Please enter Captcha!',
},
],
}, },
}; };

5
src/components/NoticeIcon/NoticeIconTab.d.ts

@ -16,7 +16,4 @@ export interface INoticeIconTabProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class NoticeIconTab extends React.Component< export default class NoticeIconTab extends React.Component<INoticeIconTabProps, any> {}
INoticeIconTabProps,
any
> {}

15
src/components/NoticeIcon/NoticeList.js

@ -4,14 +4,18 @@ import classNames from 'classnames';
import styles from './NoticeList.less'; import styles from './NoticeList.less';
export default function NoticeList({ export default function NoticeList({
data = [], onClick, onClear, title, locale, emptyText, emptyImage, data = [],
onClick,
onClear,
title,
locale,
emptyText,
emptyImage,
}) { }) {
if (data.length === 0) { if (data.length === 0) {
return ( return (
<div className={styles.notFound}> <div className={styles.notFound}>
{emptyImage ? ( {emptyImage ? <img src={emptyImage} alt="not found" /> : null}
<img src={emptyImage} alt="not found" />
) : null}
<div>{emptyText || locale.emptyText}</div> <div>{emptyText || locale.emptyText}</div>
</div> </div>
); );
@ -48,7 +52,8 @@ export default function NoticeList({
})} })}
</List> </List>
<div className={styles.clear} onClick={onClear}> <div className={styles.clear} onClick={onClear}>
{locale.clear}{title} {locale.clear}
{title}
</div> </div>
</div> </div>
); );

8
src/components/NoticeIcon/NoticeList.less

@ -1,10 +1,10 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.list { .list {
max-height: 400px; max-height: 400px;
overflow: auto; overflow: auto;
.item { .item {
transition: all .3s; transition: all 0.3s;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
padding-left: 24px; padding-left: 24px;
@ -20,7 +20,7 @@
} }
&.read { &.read {
opacity: .4; opacity: 0.4;
} }
&:last-child { &:last-child {
border-bottom: 0; border-bottom: 0;
@ -69,7 +69,7 @@
color: @text-color; color: @text-color;
border-radius: 0 0 @border-radius-base @border-radius-base; border-radius: 0 0 @border-radius-base @border-radius-base;
border-top: 1px solid @border-color-split; border-top: 1px solid @border-color-split;
transition: all .3s; transition: all 0.3s;
cursor: pointer; cursor: pointer;
&:hover { &:hover {

14
src/components/NoticeIcon/index.js

@ -30,19 +30,21 @@ export default class NoticeIcon extends PureComponent {
onItemClick = (item, tabProps) => { onItemClick = (item, tabProps) => {
const { onItemClick } = this.props; const { onItemClick } = this.props;
onItemClick(item, tabProps); onItemClick(item, tabProps);
} };
onTabChange = (tabType) => { onTabChange = tabType => {
this.setState({ tabType }); this.setState({ tabType });
this.props.onTabChange(tabType); this.props.onTabChange(tabType);
} };
getNotificationBox() { getNotificationBox() {
const { children, loading, locale } = this.props; const { children, loading, locale } = this.props;
if (!children) { if (!children) {
return null; return null;
} }
const panes = React.Children.map(children, (child) => { const panes = React.Children.map(children, child => {
const title = child.props.list && child.props.list.length > 0 const title =
? `${child.props.title} (${child.props.list.length})` : child.props.title; child.props.list && child.props.list.length > 0
? `${child.props.title} (${child.props.list.length})`
: child.props.title;
return ( return (
<TabPane tab={title} key={child.props.title}> <TabPane tab={title} key={child.props.title}>
<List <List

4
src/components/NoticeIcon/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.popover { .popover {
width: 336px; width: 336px;
@ -10,7 +10,7 @@
.noticeButton { .noticeButton {
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
transition: all .3s; transition: all 0.3s;
} }
.icon { .icon {

5
src/components/NumberInfo/index.d.ts

@ -10,7 +10,4 @@ export interface INumberInfoProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class NumberInfo extends React.Component< export default class NumberInfo extends React.Component<INumberInfoProps, any> {}
INumberInfoProps,
any
> {}

26
src/components/NumberInfo/index.js

@ -3,15 +3,11 @@ import { Icon } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import styles from './index.less'; import styles from './index.less';
export default ({ export default ({ theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest }) => (
theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest
}) => (
<div <div
className={ className={classNames(styles.numberInfo, {
classNames(styles.numberInfo, { [styles[`numberInfo${theme}`]]: theme,
[styles[`numberInfo${theme}`]]: theme, })}
})
}
{...rest} {...rest}
> >
{title && <div className={styles.numberInfoTitle}>{title}</div>} {title && <div className={styles.numberInfoTitle}>{title}</div>}
@ -21,14 +17,12 @@ export default ({
{total} {total}
{suffix && <em className={styles.suffix}>{suffix}</em>} {suffix && <em className={styles.suffix}>{suffix}</em>}
</span> </span>
{ {(status || subTotal) && (
(status || subTotal) && ( <span className={styles.subTotal}>
<span className={styles.subTotal}> {subTotal}
{subTotal} {status && <Icon type={`caret-${status}`} />}
{status && <Icon type={`caret-${status}`} />} </span>
</span> )}
)
}
</div> </div>
</div> </div>
); );

4
src/components/NumberInfo/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.numberInfo { .numberInfo {
.suffix { .suffix {
@ -11,7 +11,7 @@
color: @text-color; color: @text-color;
font-size: @font-size-lg; font-size: @font-size-lg;
margin-bottom: 16px; margin-bottom: 16px;
transition: all .3s; transition: all 0.3s;
} }
.numberInfoSubTitle { .numberInfoSubTitle {
color: @text-color-secondary; color: @text-color-secondary;

39
src/components/PageHeader/index.js

@ -4,13 +4,13 @@ import pathToRegexp from 'path-to-regexp';
import { Breadcrumb, Tabs } from 'antd'; import { Breadcrumb, Tabs } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import styles from './index.less'; import styles from './index.less';
import { urlToList } from '../utils/pathTools'; import { urlToList } from '../_utils/pathTools';
const { TabPane } = Tabs; const { TabPane } = Tabs;
export function getBreadcrumb(breadcrumbNameMap, url) { export function getBreadcrumb(breadcrumbNameMap, url) {
let breadcrumb = breadcrumbNameMap[url]; let breadcrumb = breadcrumbNameMap[url];
if (!breadcrumb) { if (!breadcrumb) {
Object.keys(breadcrumbNameMap).forEach((item) => { Object.keys(breadcrumbNameMap).forEach(item => {
if (pathToRegexp(item).test(url)) { if (pathToRegexp(item).test(url)) {
breadcrumb = breadcrumbNameMap[item]; breadcrumb = breadcrumbNameMap[item];
} }
@ -26,7 +26,7 @@ export default class PageHeader extends PureComponent {
location: PropTypes.object, location: PropTypes.object,
breadcrumbNameMap: PropTypes.object, breadcrumbNameMap: PropTypes.object,
}; };
onChange = (key) => { onChange = key => {
if (this.props.onTabChange) { if (this.props.onTabChange) {
this.props.onTabChange(key); this.props.onTabChange(key);
} }
@ -36,17 +36,12 @@ export default class PageHeader extends PureComponent {
routes: this.props.routes || this.context.routes, routes: this.props.routes || this.context.routes,
params: this.props.params || this.context.params, params: this.props.params || this.context.params,
routerLocation: this.props.location || this.context.location, routerLocation: this.props.location || this.context.location,
breadcrumbNameMap: breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
}; };
}; };
// Generated according to props // Generated according to props
conversionFromProps = () => { conversionFromProps = () => {
const { const { breadcrumbList, breadcrumbSeparator, linkElement = 'a' } = this.props;
breadcrumbList,
breadcrumbSeparator,
linkElement = 'a',
} = this.props;
return ( return (
<Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}> <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
{breadcrumbList.map(item => ( {breadcrumbList.map(item => (
@ -57,7 +52,7 @@ export default class PageHeader extends PureComponent {
{ {
[linkElement === 'a' ? 'href' : 'to']: item.href, [linkElement === 'a' ? 'href' : 'to']: item.href,
}, },
item.title, item.title
) )
: item.title} : item.title}
</Breadcrumb.Item> </Breadcrumb.Item>
@ -72,14 +67,13 @@ export default class PageHeader extends PureComponent {
// Loop data mosaic routing // Loop data mosaic routing
const extraBreadcrumbItems = pathSnippets.map((url, index) => { const extraBreadcrumbItems = pathSnippets.map((url, index) => {
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url); const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
const isLinkable = const isLinkable = index !== pathSnippets.length - 1 && currentBreadcrumb.component;
index !== pathSnippets.length - 1 && currentBreadcrumb.component;
return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? ( return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
<Breadcrumb.Item key={url}> <Breadcrumb.Item key={url}>
{createElement( {createElement(
isLinkable ? linkElement : 'span', isLinkable ? linkElement : 'span',
{ [linkElement === 'a' ? 'href' : 'to']: url }, { [linkElement === 'a' ? 'href' : 'to']: url },
currentBreadcrumb.name, currentBreadcrumb.name
)} )}
</Breadcrumb.Item> </Breadcrumb.Item>
) : null; ) : null;
@ -92,9 +86,9 @@ export default class PageHeader extends PureComponent {
{ {
[linkElement === 'a' ? 'href' : 'to']: '/', [linkElement === 'a' ? 'href' : 'to']: '/',
}, },
'首页', '首页'
)} )}
</Breadcrumb.Item>, </Breadcrumb.Item>
); );
return ( return (
<Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}> <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
@ -108,12 +102,7 @@ export default class PageHeader extends PureComponent {
*/ */
conversionBreadcrumbList = () => { conversionBreadcrumbList = () => {
const { breadcrumbList, breadcrumbSeparator } = this.props; const { breadcrumbList, breadcrumbSeparator } = this.props;
const { const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
routes,
params,
routerLocation,
breadcrumbNameMap,
} = this.getBreadcrumbProps();
if (breadcrumbList && breadcrumbList.length) { if (breadcrumbList && breadcrumbList.length) {
return this.conversionFromProps(); return this.conversionFromProps();
} }
@ -151,7 +140,7 @@ export default class PageHeader extends PureComponent {
href: paths.join('/') || '/', href: paths.join('/') || '/',
to: paths.join('/') || '/', to: paths.join('/') || '/',
}, },
route.breadcrumbName, route.breadcrumbName
) )
); );
}; };
@ -191,9 +180,7 @@ export default class PageHeader extends PureComponent {
</div> </div>
<div className={styles.row}> <div className={styles.row}>
{content && <div className={styles.content}>{content}</div>} {content && <div className={styles.content}>{content}</div>}
{extraContent && ( {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
<div className={styles.extraContent}>{extraContent}</div>
)}
</div> </div>
</div> </div>
</div> </div>

24
src/components/PageHeader/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.pageHeader { .pageHeader {
background: @component-background; background: @component-background;
@ -61,19 +61,27 @@
} }
} }
.title, .action, .content, .extraContent, .main { .title,
.action,
.content,
.extraContent,
.main {
flex: auto; flex: auto;
} }
.title, .action { .title,
.action {
margin-bottom: 16px; margin-bottom: 16px;
} }
.logo, .content, .extraContent { .logo,
.content,
.extraContent {
margin-bottom: 16px; margin-bottom: 16px;
} }
.action, .extraContent { .action,
.extraContent {
text-align: right; text-align: right;
} }
@ -105,7 +113,8 @@
display: block; display: block;
} }
.action, .extraContent { .action,
.extraContent {
margin-left: 0; margin-left: 0;
text-align: left; text-align: left;
} }
@ -124,7 +133,8 @@
.pageHeader { .pageHeader {
.action { .action {
:global { :global {
.ant-btn-group, .ant-btn { .ant-btn-group,
.ant-btn {
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;
} }

20
src/components/PageHeader/index.test.js

@ -1,5 +1,5 @@
import { getBreadcrumb } from './index'; import { getBreadcrumb } from './index';
import { urlToList } from '../utils/pathTools'; import { urlToList } from '../_utils/pathTools';
const routerData = { const routerData = {
'/dashboard/analysis': { '/dashboard/analysis': {
@ -17,35 +17,29 @@ const routerData = {
}; };
describe('test getBreadcrumb', () => { describe('test getBreadcrumb', () => {
it('Simple url', () => { it('Simple url', () => {
expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual( expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual('分析页');
'分析页',
);
}); });
it('Parameters url', () => { it('Parameters url', () => {
expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual( expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual('用户信息');
'用户信息',
);
}); });
it('The middle parameter url', () => { it('The middle parameter url', () => {
expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual( expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual('收货订单');
'收货订单',
);
}); });
it('Loop through the parameters', () => { it('Loop through the parameters', () => {
const urlNameList = urlToList('/userinfo/2144/addr').map((url) => { const urlNameList = urlToList('/userinfo/2144/addr').map(url => {
return getBreadcrumb(routerData, url).name; return getBreadcrumb(routerData, url).name;
}); });
expect(urlNameList).toEqual(['用户列表', '用户信息', '收货订单']); expect(urlNameList).toEqual(['用户列表', '用户信息', '收货订单']);
}); });
it('a path', () => { it('a path', () => {
const urlNameList = urlToList('/userinfo').map((url) => { const urlNameList = urlToList('/userinfo').map(url => {
return getBreadcrumb(routerData, url).name; return getBreadcrumb(routerData, url).name;
}); });
expect(urlNameList).toEqual(['用户列表']); expect(urlNameList).toEqual(['用户列表']);
}); });
it('Secondary path', () => { it('Secondary path', () => {
const urlNameList = urlToList('/userinfo/2144').map((url) => { const urlNameList = urlToList('/userinfo/2144').map(url => {
return getBreadcrumb(routerData, url).name; return getBreadcrumb(routerData, url).name;
}); });
expect(urlNameList).toEqual(['用户列表', '用户信息']); expect(urlNameList).toEqual(['用户列表', '用户信息']);

8
src/components/Result/index.js

@ -4,7 +4,13 @@ import { Icon } from 'antd';
import styles from './index.less'; import styles from './index.less';
export default function Result({ export default function Result({
className, type, title, description, extra, actions, ...restProps className,
type,
title,
description,
extra,
actions,
...restProps
}) { }) {
const iconMap = { const iconMap = {
error: <Icon className={styles.error} type="close-circle" />, error: <Icon className={styles.error} type="close-circle" />,

2
src/components/Result/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.result { .result {
text-align: center; text-align: center;

26
src/components/SiderMenu/BaseMeun.js

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import { Menu, Icon } from 'antd'; import { Menu, Icon } from 'antd';
import { Link } from 'dva/router'; import { Link } from 'dva/router';
import pathToRegexp from 'path-to-regexp'; import pathToRegexp from 'path-to-regexp';
import { urlToList } from '../utils/pathTools'; import { urlToList } from '../_utils/pathTools';
import styles from './index.less'; import styles from './index.less';
const { SubMenu } = Menu; const { SubMenu } = Menu;
@ -11,7 +11,7 @@ const { SubMenu } = Menu;
// icon: 'setting', // icon: 'setting',
// icon: 'http://demo.com/icon.png', // icon: 'http://demo.com/icon.png',
// icon: <Icon type="setting" />, // icon: <Icon type="setting" />,
const getIcon = (icon) => { const getIcon = icon => {
if (typeof icon === 'string' && icon.indexOf('http') === 0) { if (typeof icon === 'string' && icon.indexOf('http') === 0) {
return <img src={icon} alt="icon" className={styles.icon} />; return <img src={icon} alt="icon" className={styles.icon} />;
} }
@ -22,7 +22,7 @@ const getIcon = (icon) => {
}; };
export const getMeunMatcheys = (flatMenuKeys, path) => { export const getMeunMatcheys = (flatMenuKeys, path) => {
return flatMenuKeys.filter((item) => { return flatMenuKeys.filter(item => {
return pathToRegexp(item).test(path); return pathToRegexp(item).test(path);
}); });
}; };
@ -40,7 +40,7 @@ export default class BaseMeun extends PureComponent {
*/ */
getFlatMenuKeys(menus) { getFlatMenuKeys(menus) {
let keys = []; let keys = [];
menus.forEach((item) => { menus.forEach(item => {
if (item.children) { if (item.children) {
keys = keys.concat(this.getFlatMenuKeys(item.children)); keys = keys.concat(this.getFlatMenuKeys(item.children));
} }
@ -52,13 +52,13 @@ export default class BaseMeun extends PureComponent {
* 获得菜单子节点 * 获得菜单子节点
* @memberof SiderMenu * @memberof SiderMenu
*/ */
getNavMenuItems = (menusData) => { getNavMenuItems = menusData => {
if (!menusData) { if (!menusData) {
return []; return [];
} }
return menusData return menusData
.filter(item => item.name && !item.hideInMenu) .filter(item => item.name && !item.hideInMenu)
.map((item) => { .map(item => {
// make dom // make dom
const ItemDom = this.getSubMenuOrItem(item); const ItemDom = this.getSubMenuOrItem(item);
return this.checkPermissionItem(item.authority, ItemDom); return this.checkPermissionItem(item.authority, ItemDom);
@ -68,14 +68,12 @@ export default class BaseMeun extends PureComponent {
// Get the currently selected menu // Get the currently selected menu
getSelectedMenuKeys = () => { getSelectedMenuKeys = () => {
const { location: { pathname } } = this.props; const { location: { pathname } } = this.props;
return urlToList(pathname).map(itemPath => return urlToList(pathname).map(itemPath => getMeunMatcheys(this.flatMenuKeys, itemPath).pop());
getMeunMatcheys(this.flatMenuKeys, itemPath).pop(),
);
}; };
/** /**
* get SubMenu or Item * get SubMenu or Item
*/ */
getSubMenuOrItem = (item) => { getSubMenuOrItem = item => {
if (item.children && item.children.some(child => child.name)) { if (item.children && item.children.some(child => child.name)) {
return ( return (
<SubMenu <SubMenu
@ -95,9 +93,7 @@ export default class BaseMeun extends PureComponent {
</SubMenu> </SubMenu>
); );
} else { } else {
return ( return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
<Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>
);
} }
}; };
/** /**
@ -105,7 +101,7 @@ export default class BaseMeun extends PureComponent {
* Judge whether it is http link.return a or Link * Judge whether it is http link.return a or Link
* @memberof SiderMenu * @memberof SiderMenu
*/ */
getMenuItemPath = (item) => { getMenuItemPath = item => {
const itemPath = this.conversionPath(item.path); const itemPath = this.conversionPath(item.path);
const icon = getIcon(item.icon); const icon = getIcon(item.icon);
const { target, name } = item; const { target, name } = item;
@ -144,7 +140,7 @@ export default class BaseMeun extends PureComponent {
} }
return ItemDom; return ItemDom;
}; };
conversionPath = (path) => { conversionPath = path => {
if (path && path.indexOf('http') === 0) { if (path && path.indexOf('http') === 0) {
return path; return path;
} else { } else {

32
src/components/SiderMenu/SiderMenu.js

@ -3,7 +3,7 @@ import { Layout, Menu, Icon } from 'antd';
import { Link } from 'dva/router'; import { Link } from 'dva/router';
import styles from './index.less'; import styles from './index.less';
import BaseMeun, { getMeunMatcheys } from './BaseMeun'; import BaseMeun, { getMeunMatcheys } from './BaseMeun';
import { urlToList } from '../utils/pathTools'; import { urlToList } from '../_utils/pathTools';
const { Sider } = Layout; const { Sider } = Layout;
const { SubMenu } = Menu; const { SubMenu } = Menu;
@ -12,7 +12,7 @@ const { SubMenu } = Menu;
// icon: 'setting', // icon: 'setting',
// icon: 'http://demo.com/icon.png', // icon: 'http://demo.com/icon.png',
// icon: <Icon type="setting" />, // icon: <Icon type="setting" />,
const getIcon = (icon) => { const getIcon = icon => {
if (typeof icon === 'string' && icon.indexOf('http') === 0) { if (typeof icon === 'string' && icon.indexOf('http') === 0) {
return <img src={icon} alt="icon" className={`${styles.icon} sider-menu-item-img`} />; return <img src={icon} alt="icon" className={`${styles.icon} sider-menu-item-img`} />;
} }
@ -22,7 +22,6 @@ const getIcon = (icon) => {
return icon; return icon;
}; };
export default class SiderMenu extends PureComponent { export default class SiderMenu extends PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
@ -46,7 +45,7 @@ export default class SiderMenu extends PureComponent {
*/ */
getFlatMenuKeys(menus) { getFlatMenuKeys(menus) {
let keys = []; let keys = [];
menus.forEach((item) => { menus.forEach(item => {
if (item.children) { if (item.children) {
keys = keys.concat(this.getFlatMenuKeys(item.children)); keys = keys.concat(this.getFlatMenuKeys(item.children));
} }
@ -62,7 +61,7 @@ export default class SiderMenu extends PureComponent {
* Judge whether it is http link.return a or Link * Judge whether it is http link.return a or Link
* @memberof SiderMenu * @memberof SiderMenu
*/ */
getMenuItemPath = (item) => { getMenuItemPath = item => {
const itemPath = this.conversionPath(item.path); const itemPath = this.conversionPath(item.path);
const icon = getIcon(item.icon); const icon = getIcon(item.icon);
const { target, name } = item; const { target, name } = item;
@ -96,7 +95,7 @@ export default class SiderMenu extends PureComponent {
/** /**
* get SubMenu or Item * get SubMenu or Item
*/ */
getSubMenuOrItem = (item) => { getSubMenuOrItem = item => {
if (item.children && item.children.some(child => child.name)) { if (item.children && item.children.some(child => child.name)) {
const childrenItems = this.getNavMenuItems(item.children); const childrenItems = this.getNavMenuItems(item.children);
// 当无子菜单时就不展示菜单 // 当无子菜单时就不展示菜单
@ -110,8 +109,8 @@ export default class SiderMenu extends PureComponent {
<span>{item.name}</span> <span>{item.name}</span>
</span> </span>
) : ( ) : (
item.name item.name
) )
} }
key={item.path} key={item.path}
> >
@ -121,9 +120,7 @@ export default class SiderMenu extends PureComponent {
} }
return null; return null;
} else { } else {
return ( return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
<Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>
);
} }
}; };
/** /**
@ -133,18 +130,15 @@ export default class SiderMenu extends PureComponent {
getDefaultCollapsedSubMenus(props) { getDefaultCollapsedSubMenus(props) {
const { location: { pathname } } = props || this.props; const { location: { pathname } } = props || this.props;
return urlToList(pathname) return urlToList(pathname)
.map((item) => { .map(item => {
return getMeunMatcheys(this.flatMenuKeys, item)[0]; return getMeunMatcheys(this.flatMenuKeys, item)[0];
}) })
.filter(item => item); .filter(item => item);
} }
isMainMenu = (key) => { isMainMenu = key => {
return this.menus.some( return this.menus.some(item => key && (item.key === key || item.path === key));
item => };
key && (item.key === key || item.path === key), handleOpenChange = openKeys => {
);
}
handleOpenChange = (openKeys) => {
const lastOpenKey = openKeys[openKeys.length - 1]; const lastOpenKey = openKeys[openKeys.length - 1];
const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1; const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
this.setState({ this.setState({

20
src/components/SiderMenu/SilderMenu.test.js

@ -1,12 +1,6 @@
import { getMeunMatcheys } from './BaseMeun'; import { getMeunMatcheys } from './BaseMeun';
const meun = [ const meun = ['/dashboard', '/userinfo', '/dashboard/name', '/userinfo/:id', '/userinfo/:id/info'];
'/dashboard',
'/userinfo',
'/dashboard/name',
'/userinfo/:id',
'/userinfo/:id/info',
];
describe('test meun match', () => { describe('test meun match', () => {
it('simple path', () => { it('simple path', () => {
@ -17,20 +11,14 @@ describe('test meun match', () => {
}); });
it('Secondary path', () => { it('Secondary path', () => {
expect(getMeunMatcheys(meun, '/dashboard/name')).toEqual([ expect(getMeunMatcheys(meun, '/dashboard/name')).toEqual(['/dashboard/name']);
'/dashboard/name',
]);
}); });
it('Parameter path', () => { it('Parameter path', () => {
expect(getMeunMatcheys(meun, '/userinfo/2144')).toEqual([ expect(getMeunMatcheys(meun, '/userinfo/2144')).toEqual(['/userinfo/:id']);
'/userinfo/:id',
]);
}); });
it('three parameter path', () => { it('three parameter path', () => {
expect(getMeunMatcheys(meun, '/userinfo/2144/info')).toEqual([ expect(getMeunMatcheys(meun, '/userinfo/2144/info')).toEqual(['/userinfo/:id/info']);
'/userinfo/:id/info',
]);
}); });
}); });

11
src/components/SiderMenu/index.js

@ -3,17 +3,20 @@ import React from 'react';
import DrawerMenu from 'rc-drawer-menu'; import DrawerMenu from 'rc-drawer-menu';
import SiderMenu from './SiderMenu'; import SiderMenu from './SiderMenu';
export default props => ( export default props =>
props.isMobile || props.fixSiderbar ? ( props.isMobile || props.fixSiderbar ? (
<DrawerMenu <DrawerMenu
parent={null} parent={null}
level={null} level={null}
iconChild={null} iconChild={null}
open={!props.collapsed} open={!props.collapsed}
onMaskClick={() => { props.onCollapse(true); }} onMaskClick={() => {
props.onCollapse(true);
}}
width="256px" width="256px"
> >
<SiderMenu {...props} collapsed={props.isMobile ? false : props.collapsed} /> <SiderMenu {...props} collapsed={props.isMobile ? false : props.collapsed} />
</DrawerMenu> </DrawerMenu>
) : <SiderMenu {...props} /> ) : (
); <SiderMenu {...props} />
);

9
src/components/SiderMenu/index.less

@ -51,7 +51,12 @@
} }
.ant-menu-inline-collapsed { .ant-menu-inline-collapsed {
& > .ant-menu-item .sider-menu-item-img + span, & > .ant-menu-item .sider-menu-item-img + span,
& > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item .sider-menu-item-img + span, &
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-item
.sider-menu-item-img
+ span,
& > .ant-menu-submenu > .ant-menu-submenu-title .sider-menu-item-img + span { & > .ant-menu-submenu > .ant-menu-submenu-title .sider-menu-item-img + span {
max-width: 0; max-width: 0;
display: inline-block; display: inline-block;
@ -60,7 +65,7 @@
} }
.ant-menu-item .sider-menu-item-img + span, .ant-menu-item .sider-menu-item-img + span,
.ant-menu-submenu-title .sider-menu-item-img + span { .ant-menu-submenu-title .sider-menu-item-img + span {
transition: opacity .3s @ease-in-out, width .3s @ease-in-out; transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
opacity: 1; opacity: 1;
} }
} }

16
src/components/StandardFormRow/index.js

@ -11,16 +11,12 @@ export default ({ title, children, last, block, grid, ...rest }) => {
return ( return (
<div className={cls} {...rest}> <div className={cls} {...rest}>
{ {title && (
title && ( <div className={styles.label}>
<div className={styles.label}> <span>{title}</span>
<span>{title}</span> </div>
</div> )}
) <div className={styles.content}>{children}</div>
}
<div className={styles.content}>
{children}
</div>
</div> </div>
); );
}; };

2
src/components/StandardFormRow/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.standardFormRow { .standardFormRow {
border-bottom: 1px dashed @border-color-split; border-bottom: 1px dashed @border-color-split;

34
src/components/StandardTable/index.js

@ -4,7 +4,7 @@ import styles from './index.less';
function initTotalList(columns) { function initTotalList(columns) {
const totalList = []; const totalList = [];
columns.forEach((column) => { columns.forEach(column => {
if (column.needTotal) { if (column.needTotal) {
totalList.push({ ...column, total: 0 }); totalList.push({ ...column, total: 0 });
} }
@ -37,7 +37,7 @@ class StandardTable extends PureComponent {
handleRowSelectChange = (selectedRowKeys, selectedRows) => { handleRowSelectChange = (selectedRowKeys, selectedRows) => {
let needTotalList = [...this.state.needTotalList]; let needTotalList = [...this.state.needTotalList];
needTotalList = needTotalList.map((item) => { needTotalList = needTotalList.map(item => {
return { return {
...item, ...item,
total: selectedRows.reduce((sum, val) => { total: selectedRows.reduce((sum, val) => {
@ -51,15 +51,15 @@ class StandardTable extends PureComponent {
} }
this.setState({ selectedRowKeys, needTotalList }); this.setState({ selectedRowKeys, needTotalList });
} };
handleTableChange = (pagination, filters, sorter) => { handleTableChange = (pagination, filters, sorter) => {
this.props.onChange(pagination, filters, sorter); this.props.onChange(pagination, filters, sorter);
} };
cleanSelectedKeys = () => { cleanSelectedKeys = () => {
this.handleRowSelectChange([], []); this.handleRowSelectChange([], []);
} };
render() { render() {
const { selectedRowKeys, needTotalList } = this.state; const { selectedRowKeys, needTotalList } = this.state;
@ -83,22 +83,22 @@ class StandardTable extends PureComponent {
<div className={styles.standardTable}> <div className={styles.standardTable}>
<div className={styles.tableAlert}> <div className={styles.tableAlert}>
<Alert <Alert
message={( message={
<Fragment> <Fragment>
已选择 <a style={{ fontWeight: 600 }}>{selectedRowKeys.length}</a> 项&nbsp;&nbsp; 已选择 <a style={{ fontWeight: 600 }}>{selectedRowKeys.length}</a> 项&nbsp;&nbsp;
{ {needTotalList.map(item => (
needTotalList.map(item => ( <span style={{ marginLeft: 8 }} key={item.dataIndex}>
<span style={{ marginLeft: 8 }} key={item.dataIndex}>{item.title}总计&nbsp; {item.title}总计&nbsp;
<span style={{ fontWeight: 600 }}> <span style={{ fontWeight: 600 }}>
{item.render ? item.render(item.total) : item.total} {item.render ? item.render(item.total) : item.total}
</span>
</span> </span>
) </span>
) ))}
} <a onClick={this.cleanSelectedKeys} style={{ marginLeft: 24 }}>
<a onClick={this.cleanSelectedKeys} style={{ marginLeft: 24 }}>清空</a> 清空
</a>
</Fragment> </Fragment>
)} }
type="info" type="info"
showIcon showIcon
/> />

2
src/components/StandardTable/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.standardTable { .standardTable {
:global { :global {

5
src/components/TagSelect/TagSelectOption.d.ts

@ -5,7 +5,4 @@ export interface ITagSelectOptionProps {
style?: React.CSSProperties; style?: React.CSSProperties;
} }
export default class TagSelectOption extends React.Component< export default class TagSelectOption extends React.Component<ITagSelectOptionProps, any> {}
ITagSelectOptionProps,
any
> {}

53
src/components/TagSelect/index.js

@ -7,11 +7,7 @@ import styles from './index.less';
const { CheckableTag } = Tag; const { CheckableTag } = Tag;
const TagSelectOption = ({ children, checked, onChange, value }) => ( const TagSelectOption = ({ children, checked, onChange, value }) => (
<CheckableTag <CheckableTag checked={checked} key={value} onChange={state => onChange(value, state)}>
checked={checked}
key={value}
onChange={state => onChange(value, state)}
>
{children} {children}
</CheckableTag> </CheckableTag>
); );
@ -29,7 +25,7 @@ class TagSelect extends Component {
} }
} }
onChange = (value) => { onChange = value => {
const { onChange } = this.props; const { onChange } = this.props;
if (!('value' in this.props)) { if (!('value' in this.props)) {
this.setState({ value }); this.setState({ value });
@ -37,15 +33,15 @@ class TagSelect extends Component {
if (onChange) { if (onChange) {
onChange(value); onChange(value);
} }
} };
onSelectAll = (checked) => { onSelectAll = checked => {
let checkedTags = []; let checkedTags = [];
if (checked) { if (checked) {
checkedTags = this.getAllTags(); checkedTags = this.getAllTags();
} }
this.onChange(checkedTags); this.onChange(checkedTags);
} };
getAllTags() { getAllTags() {
let { children } = this.props; let { children } = this.props;
@ -66,19 +62,21 @@ class TagSelect extends Component {
checkedTags.splice(index, 1); checkedTags.splice(index, 1);
} }
this.onChange(checkedTags); this.onChange(checkedTags);
} };
handleExpand = () => { handleExpand = () => {
this.setState({ this.setState({
expand: !this.state.expand, expand: !this.state.expand,
}); });
} };
isTagSelectOption = (node) => { isTagSelectOption = node => {
return node && node.type && ( return (
node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption' node &&
node.type &&
(node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption')
); );
} };
render() { render() {
const { value, expand } = this.state; const { value, expand } = this.state;
@ -92,15 +90,11 @@ class TagSelect extends Component {
}); });
return ( return (
<div className={cls} style={style}> <div className={cls} style={style}>
<CheckableTag <CheckableTag checked={checkedAll} key="tag-select-__all__" onChange={this.onSelectAll}>
checked={checkedAll}
key="tag-select-__all__"
onChange={this.onSelectAll}
>
全部 全部
</CheckableTag> </CheckableTag>
{ {value &&
value && React.Children.map(children, (child) => { React.Children.map(children, child => {
if (this.isTagSelectOption(child)) { if (this.isTagSelectOption(child)) {
return React.cloneElement(child, { return React.cloneElement(child, {
key: `tag-select-${child.props.value}`, key: `tag-select-${child.props.value}`,
@ -110,15 +104,12 @@ class TagSelect extends Component {
}); });
} }
return child; return child;
}) })}
} {expandable && (
{ <a className={styles.trigger} onClick={this.handleExpand}>
expandable && ( {expand ? '收起' : '展开'} <Icon type={expand ? 'up' : 'down'} />
<a className={styles.trigger} onClick={this.handleExpand}> </a>
{expand ? '收起' : '展开'} <Icon type={expand ? 'up' : 'down'} /> )}
</a>
)
}
</div> </div>
); );
} }

6
src/components/TagSelect/index.less

@ -1,4 +1,4 @@
@import "~antd/lib/style/themes/default.less"; @import '~antd/lib/style/themes/default.less';
.tagSelect { .tagSelect {
user-select: none; user-select: none;
@ -7,7 +7,7 @@
overflow: hidden; overflow: hidden;
max-height: 32px; max-height: 32px;
line-height: 32px; line-height: 32px;
transition: all .3s; transition: all 0.3s;
:global { :global {
.ant-tag { .ant-tag {
padding: 0 8px; padding: 0 8px;
@ -16,7 +16,7 @@
} }
} }
&.expanded { &.expanded {
transition: all .3s; transition: all 0.3s;
max-height: 200px; max-height: 200px;
} }
.trigger { .trigger {

22
src/components/Trend/index.js

@ -4,17 +4,21 @@ import classNames from 'classnames';
import styles from './index.less'; import styles from './index.less';
const Trend = ({ colorful = true, flag, children, className, ...rest }) => { const Trend = ({ colorful = true, flag, children, className, ...rest }) => {
const classString = classNames(styles.trendItem, { const classString = classNames(
[styles.trendItemGrey]: !colorful, styles.trendItem,
}, className); {
[styles.trendItemGrey]: !colorful,
},
className
);
return ( return (
<div <div {...rest} className={classString} title={typeof children === 'string' ? children : ''}>
{...rest}
className={classString}
title={typeof children === 'string' ? children : ''}
>
<span className={styles.value}>{children}</span> <span className={styles.value}>{children}</span>
{flag && <span className={styles[flag]}><Icon type={`caret-${flag}`} /></span>} {flag && (
<span className={styles[flag]}>
<Icon type={`caret-${flag}`} />
</span>
)}
</div> </div>
); );
}; };

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save