Compare commits

..
300 Commits
Author SHA1 Message Date
zuiidea 3fa7ee9e01 update README 2017-04-14 19:38:28 +08:00
zuiidea b0f790ffaf update theme 2017-04-14 18:55:19 +08:00
zuiidea 4cf25852da userlogin 2017-04-14 11:45:00 +08:00
zuiidea 688d411dc1 request demo 2017-04-14 10:16:37 +08:00
zuiidea d5e763febc underline 2017-04-13 19:06:00 +08:00
zuiidea b03f2ac89c users 2017-04-13 19:00:32 +08:00
zuiidea 28efa1a67b mock 2017-04-13 18:31:48 +08:00
zuiidea a9ff576958 login 2017-04-10 23:33:10 +08:00
zuiidea 63e4fea0ca update roadhog 2017-04-10 21:06:31 +08:00
zuiidea 45d7abfbf4 fix bug 2017-04-01 16:46:32 +08:00
zuiidea c2c0ce0267 adjust 2017-04-01 15:25:45 +08:00
zuiidea d25b1cb5dc mock login 2017-04-01 14:47:42 +08:00
zuiidea 83f71c7d3e roadhog first commit 2017-04-01 12:04:03 +08:00
zuiidea 0be85a93b9 Merge pull request #158 from zuiidea/4.0-dev
user permissions
2017-03-31 20:31:50 +08:00
zuiidea 6c4733ff82 user permissions 2017-03-31 20:28:34 +08:00
zuiidea 6c4e347299 Merge pull request #152 from pmg1989/master
fix modal form
2017-03-30 11:47:44 +08:00
zuiidea f121b8884c Update skin.less 2017-03-30 11:47:26 +08:00
zuiidea 42c536b958 Update package.json 2017-03-30 11:47:05 +08:00
pmg1989 73bcc3901a fix modal form bug 2017-03-29 15:52:14 +08:00
pmg1989 870efa0771 Merge branch 'master' of github.com:pmg1989/antd-admin 2017-03-29 15:47:00 +08:00
pmg1989 d5e2af2e46 fix package.json 2017-03-29 15:46:35 +08:00
pmg1989 a3429133c3 fix package.json 2017-03-29 15:46:07 +08:00
zuiidea 71065c3922 Merge pull request #151 from zuiidea/4.0-dev
fix some bugs
2017-03-29 10:11:58 +08:00
zuiidea a5cb3c6fce fix some bugs 2017-03-28 20:50:02 +08:00
zuiidea cb1a955723 Merge pull request #147 from zuiidea/4.0-dev
4.0 dev
2017-03-27 20:28:41 +08:00
zuiidea e1a6f41192 dashboard 2017-03-27 20:27:58 +08:00
zuiidea e23cd6726f adjust dasheboard 2017-03-27 15:25:41 +08:00
zuiidea a85277ea82 adjust components DataTable 2017-03-27 15:09:36 +08:00
zuiidea efdf65a1f9 Merge pull request #144 from zuiidea/4.0-dev
adjust request
2017-03-27 14:42:48 +08:00
zuiidea 47c1fdcaff adjust request 2017-03-27 14:41:01 +08:00
zuiidea f649247a0e Merge pull request #142 from zuiidea/4.0-dev
4.0 dev
2017-03-24 20:34:56 +08:00
zuiidea f595f04a53 modify request 2017-03-24 20:32:27 +08:00
zuiidea d3c4912df3 requestPage 2017-03-24 19:25:17 +08:00
zuiidea 22c6365129 update request 2017-03-24 14:49:23 +08:00
zuiidea e5a184a568 Merge pull request #141 from zuiidea/4.0-dev
4.0 dev
2017-03-24 12:09:33 +08:00
zuiidea 3474b35966 Merge pull request #140 from zuiidea/master
Merge pull request #139 from zuiidea/4.0-dev
2017-03-24 12:07:00 +08:00
zuiidea 8227e61b64 delete extra layout filefold 2017-03-24 12:04:40 +08:00
zuiidea 1a11abb2f3 handle fileName 2017-03-24 11:57:57 +08:00
zuiidea 930c8427e0 handle FileName 2017-03-24 11:56:26 +08:00
zuiidea 8d4762fd0c Merge pull request #139 from zuiidea/4.0-dev
4.0 dev
2017-03-24 11:25:42 +08:00
zuiidea f0f9deb89a Merge branch 'master' into 4.0-dev 2017-03-24 11:25:36 +08:00
zuiidea 76d0af964f fileName fist word toLocaleUpperCase 2017-03-24 11:20:27 +08:00
zuiidea 4425a289e7 switch requst form robe-ajax to axios 2017-03-24 11:14:49 +08:00
zuiidea de68881f32 adjust layout 2017-03-23 19:30:00 +08:00
zuiidea 5c790b4215 Merge pull request #138 from xland/patch-1
delete useless code
2017-03-23 15:45:25 +08:00
liulun 2b24bcecf1 这几行代码没什么用 2017-03-23 15:21:02 +08:00
zuiidea 82351e60a6 Merge pull request #137 from zuiidea/4.0-dev
4.0 dev
2017-03-23 15:17:17 +08:00
zuiidea d71d66c80a add recharts 2017-03-23 15:14:13 +08:00
zuiidea a88f230071 add pre-commit 2017-03-23 11:11:36 +08:00
zuiidea 10bb0098d4 add UI Components: charts 2017-03-23 10:44:26 +08:00
zuiidea 9d011b9c75 add components:Editor 2017-03-23 09:57:31 +08:00
zuiidea c6227ba583 add Helmet 2017-03-22 10:49:16 +08:00
zuiidea 56ca4407ed Merge pull request #134 from zuiidea/4.0-dev
sorry,rebuild folder structure,😂
2017-03-21 16:44:55 +08:00
zuiidea 3585852fff sorry,rebuild folder structure,cry.. 2017-03-21 16:43:14 +08:00
zuiidea 30141661c2 update components DataTable 2017-03-20 17:45:08 +08:00
zuiidea d8ca570348 fix dataTable other fetch params 2017-03-20 10:58:26 +08:00
zuiidea 19d5c3c4bd fix request.js 2017-03-20 10:09:17 +08:00
zuiidea ec8f808b4d add components DataTable 2017-03-17 19:30:39 +08:00
zuiidea 741ba9e67a add eslint style for mock-folder 2017-03-17 10:27:52 +08:00
zuiidea ac6fdc5fba fix react-countup version 2017-03-16 16:59:18 +08:00
zuiidea 4dbbce5de7 Merge pull request #126 from pmg1989/master
fix menu style
2017-03-16 15:00:53 +08:00
pmg1989 6ab878061d 修复左侧栏菜单在key相同时,会同时选中bug 2017-03-16 14:58:20 +08:00
pmg1989 df1b53130f fix menu style 2017-03-16 14:36:58 +08:00
zuiidea f8a7eadf33 Merge pull request #125 from zuiidea/4.0-dev
fix sider-menu style
2017-03-16 13:36:32 +08:00
zuiidea 03539b3cc5 fix sider-menu style 2017-03-16 13:09:42 +08:00
zuiidea 8b9690fc69 Merge pull request #122 from zuiidea/4.0-dev
4.0 dev
2017-03-15 17:46:33 +08:00
zuiidea 4847dcb335 Merge branch 'master' into 4.0-dev 2017-03-15 17:46:28 +08:00
zuiidea 73abfd7009 Merge pull request #120 from catest/patch-1
Update src/components/users/modal.js
2017-03-15 17:41:09 +08:00
zuiidea e1b543969d Merge pull request #121 from catest/patch-2
修改git clone
2017-03-15 17:40:59 +08:00
zuiidea 8c9e9179e0 add: standard.md 2017-03-15 17:40:45 +08:00
catest 7869326836 修改git clone
https://github.com/zuiidea/antd-admin.git
2017-03-15 17:24:36 +08:00
catest a3cf837e5c Update src/components/users/modal.js
<Form horizontal>已弃用,改成<Form layout="horizontal">
2017-03-15 17:20:24 +08:00
zuiidea 9a89b7f4cd For static build 2017-03-15 16:42:35 +08:00
zuiidea 8bb1e1f964 updateReademe 2017-03-15 16:36:45 +08:00
zuiidea a6f2a2a67a add: ui-component layer 2017-03-15 16:31:43 +08:00
zuiidea b715b72041 Ajust router.js 2017-03-14 20:14:58 +08:00
zuiidea c43910f235 add gitignore 2017-03-14 20:04:48 +08:00
zuiidea 47e7d580bb eslint apply all 2017-03-14 20:03:21 +08:00
zuiidea 23540e4394 add eslintrc and aplly to src/ui 2017-03-14 16:44:54 +08:00
zuiidea db5105e482 add:standard-js 2017-03-14 14:25:17 +08:00
zuiidea 66ee718374 Adjust theme font-family 2017-03-14 11:44:59 +08:00
zuiidea 9492aa6d86 add: dva-loading 2017-03-13 18:11:57 +08:00
zuiidea 0e8434e32b add: dva-loading 2017-03-13 18:03:28 +08:00
zuiidea cdc607ba07 add:ui-components 2017-03-13 16:15:28 +08:00
zuiidea 41184c549a 'add:babel-polyfill' 2017-03-12 22:32:13 +08:00
zuiidea ed5125a27b fix request data 2017-03-09 15:22:21 +08:00
zuiidea c0600658e5 add readme 2017-03-07 15:29:46 +08:00
zuiidea 1f796a9a23 add demo : local-image as background 2017-03-07 15:13:51 +08:00
zuiidea 48e343450f add ajax param 2017-03-07 15:01:59 +08:00
zuiidea 0d9127fda2 propTypes 2017-03-01 20:09:59 +08:00
zuiidea 60c046b200 fix build 2017-02-28 17:29:03 +08:00
zuiidea 85cf4af52c Merge pull request #91 from pmg1989/3.0
publicPath from webpack.config.js to package.json
2017-02-28 12:45:33 +08:00
pmg1989 cc77167efb publicPath from webpack.config.js to package.json 2017-02-28 12:09:15 +08:00
zuiidea 6466276d0b chunkName 2017-02-27 22:29:45 +08:00
zuiidea c5dc892300 add user-IsMotion 2017-02-27 22:15:13 +08:00
zuiidea f303d0ae3c fix 移动端模式下菜单不可见 2017-02-26 22:04:12 +08:00
zuiidea 86de7f1658 fix 侧边收展时跳白块 2017-02-26 21:11:23 +08:00
zuiidea ff6e601777 code style 2017-02-25 22:08:01 +08:00
zuiidea 9bc01e4170 Merge pull request #83 from pmg1989/3.0
列表动画提取TableBodyWrapper.js组件
2017-02-25 21:50:36 +08:00
pmg1989 547811ced9 列表动画提取TableBodyWrapper.js组件 2017-02-25 21:40:02 +08:00
zuiidea 3106b2488e Merge pull request #82 from pmg1989/3.0
hashHistory to browserHistory
2017-02-25 20:48:56 +08:00
pmg1989 e03316423f hashHistory to browserHistory 2017-02-25 15:19:27 +08:00
zuiidea a07b2a6b0e update antd 2017-02-23 14:02:12 +08:00
zuiidea 86f08fb914 option style 2017-02-20 22:15:34 +08:00
zuiidea 702b3cc196 js format 2017-02-14 21:45:55 +08:00
zuiidea b9f8912c86 i dont know 2017-02-14 21:06:33 +08:00
zuiidea b8d98947e6 fix:单行动画 2017-02-13 23:03:57 +08:00
zuiidea 7716b24933 Merge pull request #73 from pmg1989/3.0
users-motion rc-tween-one 优化
2017-02-13 11:21:58 +08:00
felixpmg 26bff8fbb7 users-motion rc-tween-one 优化 2017-02-12 14:49:47 +08:00
zuiidea 136b61e9e3 Merge branch '3.0' of github.com:zuiidea/antd-admin into 3.0 2017-02-12 09:45:05 +08:00
zuiidea 11977625d2 Merge pull request #71 from pmg1989/3.0
优化侧边栏导航刷新后依旧能展开选中当前地址的菜单项
2017-02-12 09:41:54 +08:00
felixpmg ceabac1a14 优化 users-motion 2017-02-12 00:26:38 +08:00
felixpmg d1ffb7e3e9 优化侧边栏导航刷新后依旧能展开选中当前地址的菜单项 2017-02-10 22:25:11 +08:00
zuiidea 64c152fb2d 修改操作样式 2017-02-10 16:20:39 +08:00
zuiidea 46ff98b8af add:users-motion 2017-02-10 15:27:31 +08:00
zuiidea c97cf3e4f5 add:user-motion 2017-02-09 23:37:23 +08:00
zuiidea 4def8c5d70 add:userMotion 2017-02-09 22:33:32 +08:00
zuiidea 803d8ac72c add:badge 2017-02-06 11:20:10 +08:00
zuiidea 2df2b36d33 fix:sider zIndex;fix:search repeat request data 2017-02-05 22:05:53 +08:00
zuiidea 0700bcd700 fix:Table's rowKey;antd upgrade 2017-02-05 22:00:06 +08:00
zuiidea 184613d003 menu overflow-x hidden 2017-02-03 19:47:28 +08:00
zuiidea a1642fa671 menu overflow-x hidden 2017-02-03 19:01:03 +08:00
zuiidea a2abe411c4 fix:sidebar scroll and fold overflow visible 2017-01-30 23:38:16 +08:00
zuiidea ff84e3df7d Merge branch '3.0' of github.com:zuiidea/antd-admin into 3.0 2017-01-24 19:33:46 +08:00
zuiidea 3b4040e481 browser row-key 2017-01-23 20:48:39 +08:00
zuiidea 570bc90d6a Merge pull request #47 from zhongjie-chen/3.0
users page modal title
2017-01-22 16:06:57 +08:00
Jason e440013cee 标题区分(新建用户 修改用户) 2017-01-22 15:45:04 +08:00
zuiidea 964e702b6d Merge branch '3.0' of github.com:zuiidea/antd-admin into 3.0 2017-01-22 10:04:32 +08:00
zuiidea e2b27696c8 Merge pull request #45 from pmg1989/3.0
新增了页面刷新后,首次请求数据可以同时带关键字搜索与页码过滤功能
2017-01-21 17:27:26 +08:00
zuiidea d6dbde5382 pagination 2017-01-21 17:26:22 +08:00
felixpan 91b86a3b6c 修复了带关键字搜索时分页跳转bug;新增了页面刷新后,首次请求数据可以同时带关键字搜索与页码过滤功能 2017-01-21 00:47:28 +08:00
felixpan 26fba238a0 修复嵌套路由注销失败bug 2017-01-21 00:13:15 +08:00
zuiidea eb78f6c226 request 2017-01-18 22:10:13 +08:00
zuiidea ff2005d262 fix upgrad dva@1.2 2017-01-17 22:00:30 +08:00
zuiidea 60ff1ec0bb fix:menu vertical 2017-01-13 22:42:54 +08:00
zuiidea f41f4bcf29 Merge branch '3.0' of github.com:zuiidea/antd-admin into 3.0 2017-01-12 21:57:02 +08:00
zuiidea 78eab2b15c add demo.gif 2017-01-12 21:54:44 +08:00
zuiidea 4a68e64e81 logout loading 2017-01-12 21:39:35 +08:00
zuiidea 5e5a8f86eb page 2017-01-12 13:38:45 +08:00
zuiidea bcf9307b22 Merge pull request #29 from 0neSe7en/dev
feat(Search): add "onPressEnter" callback to search input.
2017-01-11 21:26:23 +08:00
wsy 428bfcb3a5 feat(Search): add "onPressEnter" callback to search input. 2017-01-11 16:17:58 +08:00
zuiidea 142b059fc3 fix:#28登录后点击回车跳到登录页 2017-01-10 18:26:05 +08:00
zuiidea 9452df8e0e siderMenu scrollbar 2017-01-10 14:27:34 +08:00
zuiidea 4944844522 Merge branch '3.0' of github.com:zuiidea/antd-admin into 3.0 2017-01-10 14:14:12 +08:00
zuiidea 59b40a2286 Merge pull request #26 from paranoidjk/3.0
fix: 修复侧边栏菜单超长无法滚动的问题
2017-01-10 14:13:00 +08:00
zuiidea e2f0c9b5cb Merge branch '3.0' of github.com:zuiidea/antd-admin into 3.0 2017-01-10 14:12:08 +08:00
zuiidea 7a98a485ab Merge pull request #23 from YehanZhou/patch-1
Update app.js
2017-01-10 14:11:17 +08:00
paranoidjk c1f5384d41 fix: 修复侧边栏菜单超长无法滚动的问题 2017-01-10 12:17:15 +08:00
zuiidea e78a487984 table width 2017-01-09 23:14:43 +08:00
zuiidea 9963e753d4 Search Doc 2017-01-09 23:02:21 +08:00
zuiidea 41ed8233b7 switchMenuPopover 2017-01-09 22:19:41 +08:00
zuiidea bf9e720500 less styles 2017-01-09 21:57:01 +08:00
zuiidea d0974b22bd search components 2017-01-08 22:56:29 +08:00
zuiidea bb1b7e9d4b search 2017-01-07 21:30:48 +08:00
zuiidea 0e7c34a8d4 search 2017-01-05 18:45:52 +08:00
Yehan Zhou 03db3a3bcf Update app.js 2017-01-05 17:16:20 +08:00
zuiidea dd3b4a8a80 theme 2017-01-04 14:24:12 +08:00
zuiidea 44037be442 antd upgrad 2017-01-04 10:33:55 +08:00
zuiidea 27ac1b0583 login 2017-01-03 16:34:43 +08:00
zuiidea 87af30c4b6 😪 js-standard 2017-01-03 16:15:38 +08:00
zuiidea 49d700a5c8 table 2016-12-30 20:18:03 +08:00
zuiidea 960ab30a33 navbar 2016-12-29 23:38:25 +08:00
zuiidea 75884305a0 😪 2016-12-29 22:34:05 +08:00
zuiidea e4bda2c3ae letter 2016-12-29 22:18:03 +08:00
zuiidea 61861789da letter 2016-12-29 22:17:33 +08:00
zuiidea 85b0069199 readme 2016-12-28 19:18:32 +08:00
zuiidea 9c5b3fdc68 dy-router 2016-12-28 00:16:25 +08:00
zuiidea 6af4dc9303 dy-router 2016-12-28 00:10:24 +08:00
zuiidea 6b108497cc dy-router 2016-12-28 00:03:59 +08:00
zuiidea 5c0a9c0e08 disableNpmInstall 2016-12-27 22:57:28 +08:00
zuiidea 8e678bbae6 dva-loading 2016-12-27 22:06:08 +08:00
zuiidea 5aca96bde8 user 2016-12-26 19:40:00 +08:00
zuiidea cebb9c5180 cpu.less 2016-12-26 18:03:39 +08:00
zuiidea 586e716380 cpu 2016-12-26 17:56:52 +08:00
zuiidea 0a7bfcd478 props 2016-12-26 09:51:02 +08:00
zuiidea fdba67e613 ico 2016-12-25 18:57:57 +08:00
zuiidea a79139fbc9 ico 2016-12-25 18:15:07 +08:00
zuiidea 7e521d5f05 ico 2016-12-24 23:19:32 +08:00
zuiidea ffdde1815b readme 2016-12-24 22:30:34 +08:00
zuiidea 35dbf3772b config 2016-12-23 14:45:19 +08:00
zuiidea bd01b7e777 browser 2016-12-21 22:47:59 +08:00
zuiidea a9f3b3338d COMPLETED 2016-12-21 21:57:34 +08:00
zuiidea 4500218a1f commentd 2016-12-21 21:26:41 +08:00
zuiidea 8ce7a5d48b comments 2016-12-21 21:19:33 +08:00
zuiidea 2b267a086b add demo2.gif 2016-12-20 22:58:15 +08:00
zuiidea f325dfc79d recentsales 2016-12-20 22:46:15 +08:00
zuiidea 0e7c89c885 rencentsales 2016-12-20 22:04:48 +08:00
zuiidea e3b4ecc491 avatar 2016-12-15 23:02:34 +08:00
zuiidea 6c9fb19770 dashbord mockdata 2016-12-15 22:57:02 +08:00
zuiidea 7a1d157893 dashbord mock 2016-12-15 22:37:53 +08:00
zuiidea f49ed470bd weathe - [x] r 2016-12-15 00:07:18 +08:00
zuiidea 0e3dcb2c67 weather 2016-12-14 23:46:47 +08:00
zuiidea e9c361a8a9 weather 2016-12-14 23:11:56 +08:00
zuiidea 67dd886130 666 2016-12-14 14:05:36 +08:00
zuiidea 7fd8bcdcc6 mockStorge 2016-12-14 00:19:41 +08:00
zuiidea 5030c91f47 prefix 2016-12-13 23:37:48 +08:00
zuiidea 48894e63aa prefix 2016-12-13 23:34:04 +08:00
zuiidea ad0050ee54 mock storge 2016-12-13 09:31:29 +08:00
zuiidea ef61551341 get data 2016-12-12 23:36:59 +08:00
zuiidea f0ca3f172c Weather 2016-12-12 22:53:01 +08:00
zuiidea 83eebe75e5 weather 2016-12-12 22:27:02 +08:00
zuiidea 50ceb8c825 readme 2016-12-08 09:56:29 +08:00
zuiidea b2c675cc7a readme 2016-12-07 09:33:19 +08:00
zuiidea 79e1608ece readme 2016-12-06 23:34:11 +08:00
zuiidea 8021306124 Quote 2016-12-06 22:51:21 +08:00
zuiidea f27aca76b2 countup 2016-12-06 22:00:04 +08:00
zuiidea dd1dd9390a readme 2016-12-06 21:41:52 +08:00
zuiidea 5c196033d9 LICENSE 2016-12-06 10:30:26 +08:00
zuiidea f8f3b78aa1 delete dist 2016-12-05 22:37:44 +08:00
zuiidea 0c78dd3f96 ignore 2016-12-05 22:35:03 +08:00
zuiidea 9c2a906b58 dist 2016-12-05 22:17:08 +08:00
zuiidea c011849708 siderconfig 2016-12-05 21:18:45 +08:00
zuiidea a77817673e demo.gif 2016-12-04 00:13:41 +08:00
zuiidea f8e64d2dc6 logs 2016-12-03 23:59:50 +08:00
zuiidea 5258670e86 url active menu 2016-12-03 23:46:22 +08:00
zuiidea 3d8a952f5f enter login 2016-12-03 23:38:47 +08:00
zuiidea ff7c26ced6 update 2016-12-03 23:10:47 +08:00
zuiidea 63d214e45a light theme 2016-12-03 22:38:07 +08:00
zuiidea 186f19cb65 sider 2016-11-30 18:57:19 +08:00
zuiidea e5bdd3b986 header 2016-11-30 15:18:43 +08:00
zuiidea 9838392c43 sider fold 2016-11-30 10:30:58 +08:00
zuiidea 3178f2d864 side gold 2016-11-29 23:05:26 +08:00
zuiidea 82e98b5838 side fold 2016-11-29 23:04:01 +08:00
zuiidea 3fdeb2fa0b sider fold 2016-11-29 22:53:04 +08:00
zuiidea 56f8ffe389 side fold 2016-11-29 22:06:35 +08:00
zuiidea b781d9d4c8 switchSIder 2016-11-28 23:59:53 +08:00
zuiidea 6a54b616ec dashbord layout 2016-11-28 23:31:15 +08:00
zuiidea 7f0e81d8b9 sales charts 2016-11-28 22:46:16 +08:00
zuiidea 6c50086d92 dashbord chart 2016-11-25 23:02:13 +08:00
zuiidea 4b55dbccb9 theme 2016-11-24 23:58:27 +08:00
zuiidea 2320fe5af5 add theme 2016-11-24 23:08:58 +08:00
zuiidea ace8c2568f dashbord 2016-11-24 14:19:23 +08:00
zuiidea 5cf8923442 number card 2016-11-23 23:51:09 +08:00
zuiidea 7f1bd1c08a numbercard 2016-11-23 23:16:53 +08:00
zuiidea 5150f5f84b yarn and assets 2016-11-22 22:18:18 +08:00
zuiidea dce274a8a1 spin 2016-11-07 23:50:33 +08:00
zuiidea c45d6c969b update antd 2016-11-07 23:36:05 +08:00
zuiidea bdb6142776 readme 2016-11-04 21:07:19 +08:00
zuiidea ca92904714 loading 2016-11-03 23:39:27 +08:00
zuiidea f4a108cfc1 logout 2016-11-02 22:35:07 +08:00
zuiidea bf6724f7f1 loginOut 2016-11-02 22:27:39 +08:00
zuiidea 5e8c9875d0 getUserInfo 2016-11-01 00:44:03 +08:00
zuiidea 113c81a627 login 2016-10-31 23:52:30 +08:00
zuiidea 7332df42c5 login 2016-10-29 22:13:22 +08:00
zuiidea 8960d4666e login 2016-10-28 00:29:49 +08:00
zuiidea 4642ba700c login 2016-10-27 22:27:45 +08:00
zuiidea 3081a9d4b9 login.js 2016-10-25 21:09:50 +08:00
zuiidea d20c0ddb26 fixed data type 2016-10-24 14:05:01 +08:00
zuiidea e94c26f06f userdatalist 2016-10-23 21:56:39 +08:00
zuiidea 912eb205b7 666 2016-10-22 23:29:54 +08:00
zuiidea ee5e064573 README 2016-10-21 17:14:05 +08:00
zuiidea a8643a6e55 update README 2016-10-20 21:40:56 +08:00
zuiidea 00a28000e4 users 2016-10-20 21:33:30 +08:00
zuiidea 6307e31366 local mock data 2016-10-19 17:25:46 +08:00
zuiidea 1918e132e7 update packge.json 2016-10-19 15:07:42 +08:00
zuiidea 9bd3d71ea9 local mock data 2016-10-19 15:06:23 +08:00
zuiidea e50cd23c81 loacl mosk 2016-10-18 22:54:33 +08:00
zuiidea 7797bf1487 build 2016-10-17 23:23:20 +08:00
zuiidea 042b4c728e user datas 2016-10-17 20:40:36 +08:00
zuiidea 23b32b332e error page 2016-10-16 23:05:18 +08:00
zuiidea 0255f197f4 bread 2016-10-15 01:53:58 +08:00
zuiidea 8c12c1690d bread 2016-10-15 01:25:55 +08:00
zuiidea c95eb1c888 sider menu color 2016-10-14 22:03:30 +08:00
zuiidea 2a750ea65b sider menu 2016-10-14 21:21:24 +08:00
zuiidea 1a3ea0f244 main layout 2016-10-14 00:26:36 +08:00
zuiidea 0119dfdc2a layout 2016-10-14 00:02:33 +08:00
zuiidea 019074d42c remove ; 2016-10-13 01:12:44 +08:00
zuiidea f2176ba3a7 update pakge.json 2016-10-13 00:57:29 +08:00
zuiidea 8b158a29e3 users 2016-10-12 20:37:43 +08:00
zuiidea 24998e990f user-dashbord 2016-10-11 22:16:28 +08:00
zuiidea 34ccb19f4c user 2016-10-10 22:25:56 +08:00
zuiidea fda47b362c 66 2016-10-10 22:11:11 +08:00
zuiidea f4c670bb20 readme 2016-10-10 21:53:42 +08:00
zuiidea 4f666a6a6d dva-init 2016-10-08 22:42:16 +08:00
zuiidea 62cccd4154 dva 2016-10-08 22:31:19 +08:00
zuiidea ad92f03f2d repair breadcrumb and routers 2016-09-19 23:09:41 +08:00
zuiidea 62f37008d3 listdata 2016-09-19 19:58:17 +08:00
zuiidea edd028b1bb datalist 2016-09-17 23:36:15 +08:00
zuiidea c204c95bba rewrite ajax 2016-09-17 16:17:20 +08:00
zuiidea 0740f644a4 some clean 2016-09-17 14:45:49 +08:00
zuiidea c576487d22 add fetchjs 2016-09-16 23:37:43 +08:00
zuiidea a0c6554d46 production config 2016-09-16 18:33:04 +08:00
zuiidea eaa1f283ac list page 2016-09-13 23:57:20 +08:00
zuiidea 9889415034 list page 2016-09-13 23:50:47 +08:00
zuiidea 1d368d724d add login loading 2016-09-12 23:27:11 +08:00
zuiidea 4ded5dbf91 main layout 2016-09-12 00:24:41 +08:00
zuiidea 02babf74f7 some layout 2016-09-11 22:55:25 +08:00
zuiidea 8a4d961124 css-loder 2016-09-11 19:01:59 +08:00
zuiidea 85e350107d some config 2016-09-11 18:08:34 +08:00
zuiidea 0daeffe463 eslint view 2016-09-11 17:12:05 +08:00
zuiidea c64478239a add demo address 2016-09-11 16:32:47 +08:00
zuiidea da50a1ac70 signIn 2016-09-11 01:31:53 +08:00
zuiidea cbab25a4fe signIn 2016-09-11 00:30:35 +08:00
zuiidea 4ec30d9cd7 signIn 2016-09-10 23:42:56 +08:00
zuiidea da3a989b29 new App 2016-09-10 23:25:21 +08:00
zuiidea 6a03018ab5 readme 2016-09-09 22:56:40 +08:00
zuiidea 07ebd3d862 6666 2016-09-09 22:54:55 +08:00
zuiidea 9077b5ff30 reinit 2016-09-08 21:49:18 +08:00
zuiidea 9838869517 admin-demo 2016-01-27 16:56:51 +08:00
zuiidea c42024fbed ant-demo
ant-demo
2016-01-25 15:45:58 +08:00
141 changed files with 6571 additions and 353 deletions
+16
View File
@@ -0,0 +1,16 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
+1
View File
@@ -0,0 +1 @@
src/**/*-test.js
+34
View File
@@ -0,0 +1,34 @@
{
"extends": "airbnb",
"rules": {
"semi": [2, "never"],
"no-console": 0,
"comma-dangle": [2, "always-multiline"],
"max-len": 0,
"react/jsx-first-prop-new-line": 0,
"space-before-function-paren": [2, "always"],
"no-unused-expressions": [0, {
"allowShortCircuit": true,
"allowTernary": true
}],
"arrow-body-style": [0, "never"],
"func-names": 0,
"prefer-const": 0,
"no-extend-native": 0,
"no-param-reassign": 0,
"no-restricted-syntax": 0,
"no-eval": 0,
"react/jsx-no-bind": 0,
"no-unused-vars": [2, { "ignoreRestSiblings": true }],
"no-underscore-dangle": 0,
},
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 8,
"ecmaFeatures": {
"jsx": true,
"experimentalObjectRestSpread": true
}
}
}
+2 -5
View File
@@ -1,7 +1,4 @@
*.log
.cache
.DS_Store
.idea
build
dist
node_modules
yarn.lock
npm-debug.log
+26
View File
@@ -0,0 +1,26 @@
const path = require('path')
const svgSpriteDirs = [
path.resolve(__dirname, 'src/svg/'),
]
export default {
entry: 'src/index.js',
svgSpriteLoaderDirs: svgSpriteDirs,
"theme": "./theme.config.js",
"env": {
"development": {
"extraBabelPlugins": [
"dva-hmr",
"transform-runtime",
["import", { "libraryName": "antd", "style": true }]
]
},
"production": {
"extraBabelPlugins": [
"transform-runtime",
["import", { "libraryName": "antd", "style": true}]
]
}
}
}
+5
View File
@@ -0,0 +1,5 @@
const mock = {}
require('fs').readdirSync(require('path').join(__dirname + '/src/mock')).forEach(function(file) {
Object.assign(mock, require('./src/mock/' + file))
})
module.exports = mock
+22
View File
@@ -0,0 +1,22 @@
MIT LICENSE
Copyright (c) 2016 zuiidea (zuiiidea@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+113
View File
@@ -0,0 +1,113 @@
# Antd Admin
[![React Native](https://img.shields.io/badge/react-^15.4.1-brightgreen.svg?style=flat-square)](https://github.com/facebook/react)
[![Ant Design](https://img.shields.io/badge/ant--design-^2.7.3-yellowgreen.svg?style=flat-square)](https://github.com/ant-design/ant-design)
[![dva](https://img.shields.io/badge/dva-^1.1.0-orange.svg?style=flat-square)](https://github.com/dvajs/dva)
[![GitHub issues](https://img.shields.io/github/issues/zuiidea/antd-admin.svg?style=flat-square)](https://github.com/zuiidea/antd-admin)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/zuiidea/antd-admin/pulls)
[![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com)
演示地址 <http://zuiidea.github.io/antd-admin/>
## 特性
- 基于[react](https://github.com/facebook/react),[ant-design](https://github.com/ant-design/ant-design),[dva](https://github.com/dvajs/dva),[Mock](https://github.com/nuysoft/Mock) 企业级后台管理系统最佳实践
- 基于[Mock](https://github.com/nuysoft/Mock)实现脱离后端独立开发
- 基于Antd UI 设计语言,提供后台管理系统常见使用场景
- 基于[dva](https://github.com/dvajs/dva)动态加载 Model 和路由,按需加载
- 浅度响应式设计
## 更新日志
### 4.1
`2017-04-14`
- 升级开发工具为[roadhog](https://github.com/sorrycc/roadhog)
- 使用`roadhog`的mock功能
- 增强`utils/request.js`跨域处理能力
## 开发构建
### 目录结构
```bash
├── /mock/ # 数据mock的接口文件
├── /dist/ # 项目输出目录
├── /src/ # 项目源码目录
│ ├── /components/ # UI组件及UI相关方法
│ │ ├── skin.less # 全局样式
│ │ └── vars.less # 全局样式变量
│ ├── /routes/ # 路由组件
│ │ └── app.js # 路由入口
│ ├── /models/ # 数据模型
│ ├── /services/ # 数据接口
│ ├── /utils/ # 工具函数
│ │ ├── config.js # 项目常规配置
│ │ ├── menu.js # 侧边菜单配置
│ │ ├── mock.js # 数据拦截配置
│ │ ├── config.js # 项目常规配置
│ │ ├── request.js # 异步请求函数
│ │ └── theme.js # 项目需要在js中使用到样式变量
│ ├── route.js # 路由配置
│ ├── index.js # 入口文件
│ └── index.html
├── package.json # 项目信息
└── proxy.config.js # 数据mock配置
```
文件夹命名说明:
- components:组件(方法)为单位以文件夹保存,文件夹名组件首字母大写(如`DataTable`),方法首字母小写(如`layer`),文件夹内主文件与文件夹同名,多文件以`index.js`导出对象(如`./src/components/Layout`)
- routes:页面为单位以文件夹保存,文件夹名首字母小写(特殊除外,如`UIElement`),文件夹内主文件以`index.js`导出,多文件时可建立`components`文件夹(如`./src/routes/dashboard`),如果有子路由,依次按照路由层次建立文件夹(如`./src/routes/UIElement`)
### 快速开始
克隆项目文件:
git clone https://github.com/zuiidea/antd-admin.git
进入目录安装依赖:
npm i 或者 yarn install
npm i roadhog@0.6.0-beta.3 -g
开发:
```bash
npm run dev # 使用mock拦截请求,数据存储在localStroge里
打开 http://localhost:8000
```
构建:
```bash
npm run build
将会生成dist目录
```
代码检测:
```bash
npm run lint
```
## 参考
用户列表:<https://github.com/dvajs/dva/tree/master/examples/user-dashboard>
dashboard设计稿:<https://dribbble.com/shots/3108122-Dashboard-Admin> (已征得作者同意)
## 截屏
web
![](assets/demo4.gif)
移动
![](assets/demo3.gif)
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
webpackJsonp([10],{49:126,55:function(e,t,l){"use strict";function a(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var n=l(9),d=a(n),r=l(19),u=a(r),c=l(1),s=a(c),o=l(3),i=a(o),f=function(e,t){var l={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&t.indexOf(a)<0&&(l[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,a=Object.getOwnPropertySymbols(e);n<a.length;n++)t.indexOf(a[n])<0&&(l[a[n]]=e[a[n]]);return l};t["default"]=function(e){var t,l=e.prefixCls,a=void 0===l?"ant-card":l,n=e.className,r=e.extra,c=e.bodyStyle,o=e.title,m=e.loading,p=e.bordered,y=void 0===p||p,E=f(e,["prefixCls","className","extra","bodyStyle","title","loading","bordered"]),b=e.children,g=(0,i["default"])(a,n,(t={},(0,u["default"])(t,a+"-loading",m),(0,u["default"])(t,a+"-bordered",y),t));m&&(b=s["default"].createElement("div",null,s["default"].createElement("p",{className:a+"-loading-block",style:{width:"94%"}}),s["default"].createElement("p",null,s["default"].createElement("span",{className:a+"-loading-block",style:{width:"28%"}}),s["default"].createElement("span",{className:a+"-loading-block",style:{width:"62%"}})),s["default"].createElement("p",null,s["default"].createElement("span",{className:a+"-loading-block",style:{width:"22%"}}),s["default"].createElement("span",{className:a+"-loading-block",style:{width:"66%"}})),s["default"].createElement("p",null,s["default"].createElement("span",{className:a+"-loading-block",style:{width:"56%"}}),s["default"].createElement("span",{className:a+"-loading-block",style:{width:"39%"}})),s["default"].createElement("p",null,s["default"].createElement("span",{className:a+"-loading-block",style:{width:"21%"}}),s["default"].createElement("span",{className:a+"-loading-block",style:{width:"15%"}}),s["default"].createElement("span",{className:a+"-loading-block",style:{width:"40%"}}))));var k=void 0;return k=o?"string"==typeof o?s["default"].createElement("div",{className:a+"-head"},s["default"].createElement("h3",{className:a+"-head-title"},o)):s["default"].createElement("div",{className:a+"-head"},s["default"].createElement("div",{className:a+"-head-title"},o)):null,s["default"].createElement("div",(0,d["default"])({},E,{className:g}),k,r?s["default"].createElement("div",{className:a+"-extra"},r):null,s["default"].createElement("div",{className:a+"-body",style:c},b))},e.exports=t["default"]},56:[1570,63],63:20,1454:function(e,t,l){"use strict";function a(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var n=(l(196),l(195)),d=a(n),r=(l(126),l(115)),u=a(r),c=(l(753),l(752)),s=a(c),o=(l(49),l(125)),i=a(o),f=(l(56),l(55)),m=a(f),p=l(1),y=a(p),E=l(207),b=function(){return y["default"].createElement("div",{className:"content-inner"},y["default"].createElement(u["default"],{gutter:32},y["default"].createElement(i["default"],{lg:8,md:12},y["default"].createElement(m["default"],{title:"\u9ed8\u8ba4"},y["default"].createElement(E.DropOption,{menuOptions:[{key:"1",name:"\u7f16\u8f91"},{key:"2",name:"\u5220\u9664"}]}))),y["default"].createElement(i["default"],{lg:8,md:12},y["default"].createElement(m["default"],{title:"\u6837\u5f0f"},y["default"].createElement(E.DropOption,{menuOptions:[{key:"1",name:"\u7f16\u8f91"},{key:"2",name:"\u5220\u9664"}],buttonStyle:{border:"solid 1px #eee",width:60}}))),y["default"].createElement(i["default"],{lg:8,md:12},y["default"].createElement(m["default"],{title:"\u4e8b\u4ef6"},y["default"].createElement(E.DropOption,{menuOptions:[{key:"1",name:"\u7f16\u8f91"},{key:"2",name:"\u5220\u9664"}],buttonStyle:{border:"solid 1px #eee",width:60},onMenuClick:function(e){var t=e.key;switch(t){case"1":s["default"].success("\u70b9\u51fb\u4e86\u7f16\u8f91");break;case"2":s["default"].success("\u70b9\u51fb\u4e86\u5220\u9664")}}})))),y["default"].createElement("h2",{style:{margin:"16px 0"}},"Props"),y["default"].createElement(u["default"],null,y["default"].createElement(i["default"],{lg:18,md:24},y["default"].createElement(d["default"],{rowKey:function(e,t){return t},pagination:!1,bordered:!0,scroll:{x:800},columns:[{title:"\u53c2\u6570",dataIndex:"props"},{title:"\u8bf4\u660e",dataIndex:"desciption"},{title:"\u7c7b\u578b",dataIndex:"type"},{title:"\u9ed8\u8ba4\u503c",dataIndex:"default"}],dataSource:[{props:"menuOptions",desciption:"\u4e0b\u62c9\u64cd\u4f5c\u7684\u9009\u9879\uff0c\u683c\u5f0f\u4e3a[{name:string,key:string}]",type:"Array","default":"\u5fc5\u9009"},{props:"onMenuClick",desciption:"\u70b9\u51fb menuitem \u8c03\u7528\u6b64\u51fd\u6570\uff0c\u53c2\u6570\u4e3a {item, key, keyPath}",type:"Function","default":"-"},{props:"buttonStyle",desciption:"\u6309\u94ae\u7684\u6837\u5f0f",type:"Object","default":"-"},{props:"dropdownProps",desciption:"\u4e0b\u62c9\u83dc\u5355\u7684\u53c2\u6570\uff0c\u53ef\u53c2\u8003antd\u7684\u3010Dropdown\u3011\u7ec4\u4ef6",type:"Object","default":"-"}]}))))};t["default"]=b,e.exports=t["default"]}});
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
webpackJsonp([12],{49:126,928:function(e,t){e.exports={list:"list___NsVSt",name:"name___2l5wL"}},1456:function(e,t,a){"use strict";function l(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var n=(a(126),a(115)),d=l(n),r=(a(49),a(125)),u=l(r),c=(a(196),a(195)),s=l(c),f=a(1),o=l(f),i=a(207),p=a(928),m=l(p),_=["Cherry","Cheese","Bread","Beer","Beet","Bacon","Banana","Asparagus","Apple"],y=function(){return o["default"].createElement("div",{className:"content-inner"},o["default"].createElement("ul",{className:m["default"].list},_.map(function(e){return o["default"].createElement("li",{key:e},o["default"].createElement(i.Iconfont,{className:m["default"].icon,type:e}),o["default"].createElement("span",{className:m["default"].name},e))})),o["default"].createElement("h2",{style:{margin:"16px 0"}},"Props"),o["default"].createElement(d["default"],null,o["default"].createElement(u["default"],{lg:18,md:24},o["default"].createElement(s["default"],{rowKey:function(e,t){return t},pagination:!1,bordered:!0,scroll:{x:800},columns:[{title:"\u53c2\u6570",dataIndex:"props"},{title:"\u8bf4\u660e",dataIndex:"desciption"},{title:"\u7c7b\u578b",dataIndex:"type"},{title:"\u9ed8\u8ba4\u503c",dataIndex:"default"}],dataSource:[{props:"type",desciption:"\u56fe\u6807\u7c7b\u578b",type:"String","default":"-"}]}))))};t["default"]=y,e.exports=t["default"]}});
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
webpackJsonp([8],{49:126,55:function(e,t,l){"use strict";function a(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var d=l(9),n=a(d),r=l(19),c=a(r),s=l(1),u=a(s),o=l(3),i=a(o),f=function(e,t){var l={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&t.indexOf(a)<0&&(l[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var d=0,a=Object.getOwnPropertySymbols(e);d<a.length;d++)t.indexOf(a[d])<0&&(l[a[d]]=e[a[d]]);return l};t["default"]=function(e){var t,l=e.prefixCls,a=void 0===l?"ant-card":l,d=e.className,r=e.extra,s=e.bodyStyle,o=e.title,m=e.loading,p=e.bordered,E=void 0===p||p,y=f(e,["prefixCls","className","extra","bodyStyle","title","loading","bordered"]),g=e.children,b=(0,i["default"])(a,d,(t={},(0,c["default"])(t,a+"-loading",m),(0,c["default"])(t,a+"-bordered",E),t));m&&(g=u["default"].createElement("div",null,u["default"].createElement("p",{className:a+"-loading-block",style:{width:"94%"}}),u["default"].createElement("p",null,u["default"].createElement("span",{className:a+"-loading-block",style:{width:"28%"}}),u["default"].createElement("span",{className:a+"-loading-block",style:{width:"62%"}})),u["default"].createElement("p",null,u["default"].createElement("span",{className:a+"-loading-block",style:{width:"22%"}}),u["default"].createElement("span",{className:a+"-loading-block",style:{width:"66%"}})),u["default"].createElement("p",null,u["default"].createElement("span",{className:a+"-loading-block",style:{width:"56%"}}),u["default"].createElement("span",{className:a+"-loading-block",style:{width:"39%"}})),u["default"].createElement("p",null,u["default"].createElement("span",{className:a+"-loading-block",style:{width:"21%"}}),u["default"].createElement("span",{className:a+"-loading-block",style:{width:"15%"}}),u["default"].createElement("span",{className:a+"-loading-block",style:{width:"40%"}}))));var h=void 0;return h=o?"string"==typeof o?u["default"].createElement("div",{className:a+"-head"},u["default"].createElement("h3",{className:a+"-head-title"},o)):u["default"].createElement("div",{className:a+"-head"},u["default"].createElement("div",{className:a+"-head-title"},o)):null,u["default"].createElement("div",(0,n["default"])({},y,{className:b}),h,r?u["default"].createElement("div",{className:a+"-extra"},r):null,u["default"].createElement("div",{className:a+"-body",style:s},g))},e.exports=t["default"]},56:[1570,63],63:20,1458:function(e,t,l){"use strict";function a(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var d=(l(196),l(195)),n=a(d),r=(l(126),l(115)),c=a(r),s=(l(49),l(125)),u=a(s),o=(l(56),l(55)),i=a(o),f=l(1),m=a(f),p=l(207),E=function(){return m["default"].createElement("div",{className:"content-inner"},m["default"].createElement(c["default"],{gutter:32},m["default"].createElement(u["default"],{lg:8,md:12},m["default"].createElement(i["default"],{title:"\u9ed8\u8ba4"},m["default"].createElement(p.Search,null))),m["default"].createElement(u["default"],{lg:8,md:12},m["default"].createElement(i["default"],{title:"\u9644\u5e26\u9009\u62e9"},m["default"].createElement(p.Search,{select:!0,selectOptions:[{value:"components",name:"\u7ec4\u4ef6"},{value:"page",name:"\u9875\u9762"}],selectProps:{defaultValue:"components"}}))),m["default"].createElement(u["default"],{lg:8,md:12},m["default"].createElement(i["default"],{title:"\u5927\u5c0f"},m["default"].createElement(p.Search,{size:"large",style:{marginBottom:16}}),m["default"].createElement(p.Search,{size:"small"})))),m["default"].createElement("h2",{style:{margin:"16px 0"}},"Props"),m["default"].createElement(c["default"],null,m["default"].createElement(u["default"],{lg:18,md:24},m["default"].createElement(n["default"],{rowKey:function(e,t){return t},pagination:!1,bordered:!0,scroll:{x:800},columns:[{title:"\u53c2\u6570",dataIndex:"props"},{title:"\u8bf4\u660e",dataIndex:"desciption"},{title:"\u7c7b\u578b",dataIndex:"type"},{title:"\u9ed8\u8ba4\u503c",dataIndex:"default"}],dataSource:[{props:"size",desciption:"\u8bbe\u7f6eSearch\u5927\u5c0f\uff0c\u53ef\u9009\u503c\u4e3a \u3010small\u3011 \u3010large\u3011 \u6216\u8005\u4e0d\u8bbe",type:"String","default":"-"},{props:"select",desciption:"\u8bbe\u7f6e\u662f\u5426\u6709\u9009\u62e9\u5668",type:"Boolean","default":"false"},{props:"selectOptions",desciption:'\u9009\u62e9\u5668\u7684\u9009\u9879\uff0c\u683c\u5f0f\u4e3a[{name:"",value:""}]\u6216\u8005[{value:""}]',type:"Array","default":"-"},{props:"selectProps",desciption:"\u9009\u62e9\u5668\u7684\u5c5e\u6027\uff0c\u53ef\u53c2\u8003antd\u7684\u3010Select\u3011\u7ec4\u4ef6",type:"Object","default":"-"},{props:"onSearch",desciption:"\u70b9\u51fb\u641c\u7d22\u6309\u94ae, \u6309Enter\u952e\u6216\u8005\u70b9\u51fb\u6e05\u9664\u65f6\u7684\u56de\u8c03",type:"Function({keyword:string,field:string})","default":"-"}]}))))};t["default"]=E,e.exports=t["default"]}});
Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

+35
View File
@@ -0,0 +1,35 @@
### 开发环境
- OS:Windows
- 编辑器:Atom
- cmd:Cmder
### Atom 插件
- [atom-beautify](https://atom.io/packages/atom-beautify)
#### Less
- [x] Beautify On Save
#### Javascript
- [ ] Disable Beautifying Language
#### Markdown
- [x] Beautify On Save
- [x] Default Beautifier:Remark
#### HTML
- [x] Beautify On Save
- [linter](https://atom.io/packages/linter)
- [ ] Lint on Change
- [linter-eslint](https://atom.io/packages/linter-eslint)
- [x] Fix errors on save
### Cmder 主题
- [Panda-Theme-Cmder](https://github.com/HamidFaraji/Panda-Theme-Cmder)
File diff suppressed because one or more lines are too long
-14
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-15
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
webpackJsonp([13],{940:function(e,t){e.exports={error:"error___2u_Y6"}},1474:function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var u=(r(213),r(80)),a=n(u),l=r(1),o=n(l),d=r(940),f=n(d),c=function(){return o["default"].createElement("div",{className:"content-inner"},o["default"].createElement("div",{className:f["default"].error},o["default"].createElement(a["default"],{type:"frown-o"}),o["default"].createElement("h1",null,"404 Not Found")))};t["default"]=c,e.exports=t["default"]}});
-1
View File
File diff suppressed because one or more lines are too long
-12
View File
@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ant Design Admin</title>
<script language="javascript" type="text/javascript">
window.location.href="http://47.92.30.98:8000";
</script>
</head>
</html>
-123
View File
File diff suppressed because one or more lines are too long
+56
View File
@@ -0,0 +1,56 @@
{
"private": true,
"dependencies": {
"antd": "^2.9.1",
"classnames": "^2.2.5",
"draftjs-to-markdown": "^0.4.0",
"dva": "^1.1.0",
"dva-loading": "^0.2.0",
"lodash": "^4.17.4",
"qs": "^6.2.0",
"react": "^15.4.1",
"react-countup": "1.3.0",
"react-dom": "^15.4.1",
"react-draft-wysiwyg": "^1.8.1",
"react-helmet": "^5.0.0",
"recharts": "^0.19.0"
},
"devDependencies": {
"antd": "^2.9.1",
"axios": "^0.15.3",
"babel-eslint": "^6.1.2",
"babel-plugin-dev-expression": "^0.2.1",
"babel-plugin-dva-hmr": "^0.3.2",
"babel-plugin-import": "^1.1.1",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-polyfill": "^6.23.0",
"babel-runtime": "^6.9.2",
"draftjs-to-html": "^0.7.0",
"eslint": "^3.17.1",
"eslint-config-airbnb": "^9.0.1",
"eslint-plugin-import": "^1.16.0",
"eslint-plugin-jsx-a11y": "^1.4.2",
"eslint-plugin-react": "^5.1.1",
"jsonp": "^0.2.1",
"less-vars-to-js": "^1.1.2",
"mockjs": "^1.0.1-beta3",
"rc-tween-one": "^1.0.0",
"redbox-react": "^1.2.10"
},
"pre-commit": [
"lint"
],
"scripts": {
"dev": "set BROWSER=none&&roadhog server",
"lint": "eslint --fix --ext .js src",
"build": "roadhog build"
},
"theme": "./src/theme.js",
"standard": {
"parser": "babel-eslint",
"globals": [
"location",
"localStorage"
]
}
}
-1
View File
File diff suppressed because one or more lines are too long
+66
View File
@@ -0,0 +1,66 @@
import React, { PropTypes } from 'react'
import { TweenOneGroup } from 'rc-tween-one'
const enterAnim = [
{
opacity: 0,
x: 30,
backgroundColor: '#fffeee',
duration: 0,
}, {
height: 0,
duration: 200,
type: 'from',
delay: 250,
ease: 'easeOutQuad',
onComplete: (e) => {
e.target.style.height = 'auto'
},
}, {
opacity: 1,
x: 0,
duration: 250,
ease: 'easeOutQuad',
}, {
delay: 1000,
backgroundColor: '#fff',
},
]
const leaveAnim = [
{
duration: 250,
x: -30,
opacity: 0,
}, {
height: 0,
duration: 200,
ease: 'easeOutQuad',
},
]
const AnimTableBody = ({ body, page = 1, current }) => {
if (current !== +page) {
return body
}
return (
<TweenOneGroup
component="tbody"
className={body.props.className}
enter={enterAnim}
leave={leaveAnim}
appear={false}
>
{body.props.children}
</TweenOneGroup>
)
}
AnimTableBody.propTypes = {
body: PropTypes.element,
page: PropTypes.any,
current: PropTypes.number.isRequired,
}
export default AnimTableBody
+111
View File
@@ -0,0 +1,111 @@
import React, { PropTypes } from 'react'
import { Table } from 'antd'
import { request } from '../../utils'
import lodash from 'lodash'
import './DataTable.less'
class DataTable extends React.Component {
constructor (props) {
super(props)
const { dataSource, pagination = {
showSizeChanger: true,
showQuickJumper: true,
showTotal: total => `共 ${total} 条`,
current: 1,
total: 100 },
} = props
this.state = {
loading: false,
dataSource,
fetchData: {},
pagination,
}
}
componentDidMount () {
if (this.props.fetch) {
this.fetch()
}
}
componentWillReceiveProps (nextProps) {
const staticNextProps = lodash.cloneDeep(nextProps)
delete staticNextProps.columns
const { columns, ...otherProps } = this.props
if (!lodash.isEqual(staticNextProps, otherProps)) {
this.props = nextProps
this.fetch()
}
}
handleTableChange = (pagination, filters, sorter) => {
const pager = this.state.pagination
pager.current = pagination.current
this.setState({
pagination: pager,
fetchData: {
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
},
}, () => {
this.fetch()
})
}
fetch = () => {
const { fetch: { url, data, dataKey } } = this.props
const { fetchData } = this.state
this.setState({ loading: true })
this.promise = request({
url,
data: {
...data,
...fetchData,
},
}).then((result) => {
if (!this.refs.DataTable) {
return
}
const { pagination } = this.state
pagination.total = result.total || pagination.total
this.setState({
loading: false,
dataSource: dataKey ? result[dataKey] : result.data,
pagination,
})
})
}
render () {
const { fetch, ...tableProps } = this.props
const { loading, dataSource, pagination } = this.state
return (<Table
ref="DataTable"
bordered
loading={loading}
onChange={this.handleTableChange}
{...tableProps}
pagination={pagination}
dataSource={dataSource}
/>)
}
}
DataTable.propTypes = {
fetch: PropTypes.object,
rowKey: PropTypes.string,
pagination: React.PropTypes.oneOfType([
React.PropTypes.bool,
React.PropTypes.object,
]),
columns: PropTypes.array,
dataSource: PropTypes.array,
}
export default DataTable
+6
View File
@@ -0,0 +1,6 @@
{
"name": "DataTable",
"version": "0.0.0",
"private": true,
"main": "./DataTable.js"
}
+24
View File
@@ -0,0 +1,24 @@
import React, { PropTypes } from 'react'
import { Dropdown, Button, Icon, Menu } from 'antd'
const DropOption = ({ onMenuClick, menuOptions = [], buttonStyle, dropdownProps }) => {
const menu = menuOptions.map(item => <Menu.Item key={item.key}>{item.name}</Menu.Item>)
return (<Dropdown
overlay={<Menu onClick={onMenuClick}>{menu}</Menu>}
{...dropdownProps}
>
<Button style={{ border: 'none', ...buttonStyle }}>
<Icon style={{ marginRight: 2 }} type="bars" />
<Icon type="down" />
</Button>
</Dropdown>)
}
DropOption.propTypes = {
onMenuClick: PropTypes.func,
menuOptions: PropTypes.array.isRequired,
buttonStyle: PropTypes.object,
dropdownProps: PropTypes.object,
}
export default DropOption
+6
View File
@@ -0,0 +1,6 @@
{
"name": "DropOption",
"version": "0.0.0",
"private": true,
"main": "./DropOption.js"
}
+11
View File
@@ -0,0 +1,11 @@
import React from 'react'
import { Editor } from 'react-draft-wysiwyg'
import styles from './Editor.less'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
const DraftEditor = (props) => {
return (<Editor toolbarClassName={styles.toolbar} wrapperClassName={styles.wrapper} editorClassName={styles.editor} {...props} />)
}
export default DraftEditor
+111
View File
@@ -0,0 +1,111 @@
.wrapper {
height: 500px;
:global {
.rdw-dropdownoption-default {
padding: 6px;
}
.rdw-dropdown-optionwrapper {
box-sizing: content-box;
width: 100%;
border-radius: 0 0 2px 2px;
}
.rdw-inline-wrapper {
flex-wrap: wrap;
margin-bottom: 0;
.rdw-option-wrapper {
margin-bottom: 6px;
}
}
.rdw-option-active {
box-shadow: 1px 1px 0 #e8e8e8 inset;
}
.rdw-dropdown-optionwrapper {
&:hover {
box-shadow: none;
}
}
.rdw-colorpicker-option {
box-shadow: none;
}
.rdw-colorpicker-modal,
.rdw-embedded-modal,
.rdw-emoji-modal,
.rdw-image-modal,
.rdw-link-modal {
box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05);
}
.rdw-colorpicker-modal,
.rdw-embedded-modal,
.rdw-link-modal {
height: auto;
}
.rdw-emoji-modal {
width: 214px;
}
.rdw-colorpicker-modal {
width: auto;
}
.rdw-embedded-modal-btn,
.rdw-image-modal-btn,
.rdw-link-modal-btn {
height: 32px;
margin-top: 12px;
}
.rdw-embedded-modal-input,
.rdw-embedded-modal-size-input,
.rdw-link-modal-input {
padding: 2px 6px;
height: 32px;
}
.rdw-dropdown-selectedtext {
color: #000;
}
.rdw-dropdown-wrapper,
.rdw-option-wrapper {
min-width: 36px;
transition: all 0.2s ease;
height: 30px;
&:active {
box-shadow: 1px 1px 0 #e8e8e8 inset;
}
&:hover {
box-shadow: 1px 1px 0 #e8e8e8;
}
}
.rdw-dropdown-wrapper {
min-width: 60px;
}
.rdw-editor-main {
box-sizing: border-box;
}
}
.editor {
border: 1px solid #F1F1F1;
padding: 5px;
border-radius: 2px;
height: auto;
min-height: 200px;
}
.toolbar {}
}
+6
View File
@@ -0,0 +1,6 @@
{
"name": "Editor",
"version": "0.0.0",
"private": true,
"main": "./Editor.js"
}
+14
View File
@@ -0,0 +1,14 @@
import React, { PropTypes } from 'react'
import './iconfont.less'
const Iconfont = ({ type }) => <span
dangerouslySetInnerHTML={{
__html: `<svg class="iconfont" aria-hidden="true"><use xlink:href="#anticon-${type}"></use></svg>`,
}}
/>
Iconfont.propTypes = {
type: PropTypes.string,
}
export default Iconfont
+8
View File
@@ -0,0 +1,8 @@
:global .iconfont {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
font-size: 16px;
}
+6
View File
@@ -0,0 +1,6 @@
{
"name": "Iconfont",
"version": "0.0.0",
"private": true,
"main": "./Iconfont.js"
}
+71
View File
@@ -0,0 +1,71 @@
import React, { PropTypes } from 'react'
import { Breadcrumb, Icon } from 'antd'
import { Link } from 'dva/router'
import styles from './Bread.less'
import { menu } from '../../utils'
let pathSet = []
const getPathSet = function (menuArray, parentPath) {
parentPath = parentPath || '/'
menuArray.forEach(item => {
pathSet[(parentPath + item.key).replace(/\//g, '-').hyphenToHump()] = {
path: parentPath + item.key,
name: item.name,
icon: item.icon || '',
clickable: item.clickable === undefined,
}
if (item.child) {
getPathSet(item.child, `${parentPath}${item.key}/`)
}
})
}
getPathSet(menu)
function Bread ({ location }) {
let pathNames = []
location.pathname.substr(1).split('/').forEach((item, key) => {
if (key > 0) {
pathNames.push((`${pathNames[key - 1]}-${item}`).hyphenToHump())
} else {
pathNames.push((`-${item}`).hyphenToHump())
}
})
const breadsArray = pathNames.filter(item => (item in pathSet))
const breads = breadsArray.map((item, key) => {
const content = (
<span>{pathSet[item].icon
? <Icon type={pathSet[item].icon} style={{ marginRight: 4 }} />
: ''}{pathSet[item].name}</span>
)
return (
<Breadcrumb.Item key={key}>
{((breadsArray.length - 1) !== key && pathSet[item].clickable)
? <Link to={pathSet[item].path}>
{content}
</Link>
: content}
</Breadcrumb.Item>
)
})
return (
<div className={styles.bread}>
<Breadcrumb>
<Breadcrumb.Item >
<Link to="/dashboard">
<Icon type="home" />
<span>Home</span>
</Link>
</Breadcrumb.Item>
{breads}
</Breadcrumb>
</div>
)
}
Bread.propTypes = {
location: PropTypes.object,
}
export default Bread
+8
View File
@@ -0,0 +1,8 @@
@import "../../themes/vars.less";
.bread {
height: 64px;
line-height: 64px;
padding: 0 24px;
margin-bottom: -24px;
}
+9
View File
@@ -0,0 +1,9 @@
import React from 'react'
import styles from './Footer.less'
import { config } from '../../utils'
const Footer = () => <div className={styles.footer}>
{config.footerText}
</div>
export default Footer
+12
View File
@@ -0,0 +1,12 @@
@import "../../themes/vars.less";
.footer {
height: 48px;
line-height: 48px;
text-align: center;
font-size: 12px;
color: #999;
background: #fff;
box-shadow: @shadow-2;
width: 100%;
}
+63
View File
@@ -0,0 +1,63 @@
import React, { PropTypes } from 'react'
import { Menu, Icon, Popover } from 'antd'
import styles from './Header.less'
import Menus from './Menu'
const SubMenu = Menu.SubMenu
function Header ({ user, logout, switchSider, siderFold, isNavbar, menuPopoverVisible, location, switchMenuPopover, navOpenKeys, changeOpenKeys }) {
let handleClickMenu = e => e.key === 'logout' && logout()
const menusProps = {
siderFold: false,
darkTheme: false,
isNavbar,
handleClickNavMenu: switchMenuPopover,
location,
navOpenKeys,
changeOpenKeys,
}
return (
<div className={styles.header}>
{isNavbar
? <Popover placement="bottomLeft" onVisibleChange={switchMenuPopover} visible={menuPopoverVisible} overlayClassName={styles.popovermenu} trigger="click" content={<Menus {...menusProps} />}>
<div className={styles.button}>
<Icon type="bars" />
</div>
</Popover>
: <div className={styles.button} onClick={switchSider}>
<Icon type={siderFold ? 'menu-unfold' : 'menu-fold'} />
</div>}
<div className={styles.rightWarpper}>
<div className={styles.button}>
<Icon type="mail" />
</div>
<Menu mode="horizontal" onClick={handleClickMenu}>
<SubMenu style={{
float: 'right',
}} title={< span > <Icon type="user" />
{user.username} < /span>}
>
<Menu.Item key="logout">
<a>注销</a>
</Menu.Item>
</SubMenu>
</Menu>
</div>
</div>
)
}
Header.propTypes = {
user: PropTypes.object,
logout: PropTypes.func,
switchSider: PropTypes.func,
siderFold: PropTypes.bool,
isNavbar: PropTypes.bool,
menuPopoverVisible: PropTypes.bool,
location: PropTypes.object,
switchMenuPopover: PropTypes.func,
navOpenKeys: PropTypes.array,
changeOpenKeys: PropTypes.func,
}
export default Header
+57
View File
@@ -0,0 +1,57 @@
@import "../../themes/vars.less";
.header {
:global .ant-menu-horizontal {
& > .ant-menu-submenu {
float: right;
}
border: none;
display: inline-block;
}
box-shadow: @shadow-2;
position: relative;
display: flex;
justify-content: space-between;
height: 47px;
background-color: #fff;
.rightWarpper {
display: flex;
}
.button {
width: 47px;
height: 47px;
line-height: 47px;
text-align: center;
font-size: 18px;
cursor: pointer;
transition: @transition-ease-in;
&:hover {
color: @primary-color;
background-color: fade(@primary-color, 15%);
}
}
}
.popovermenu {
width: 280px;
margin-left: 6px;
:global .ant-popover-inner-content {
padding: 0;
.ant-menu-inline .ant-menu-item,
.ant-menu-vertical .ant-menu-item {
border-right: 0;
height: 48px;
line-height: 48px;
}
.ant-menu-inline .ant-menu-item-selected,
.ant-menu-inline .ant-menu-selected {
border-right: 0;
}
}
}
+293
View File
@@ -0,0 +1,293 @@
@import "../../themes/vars.less";
.layout {
position: relative;
height: 100vh;
&.withnavbar {
.main {
margin-left: 0;
}
}
&.fold {
.sider {
width: 42px;
.logo {
img {
width: 28px;
margin: 6px 7px;
}
}
:global {
.ant-menu-root {
width: 100%;
overflow: visible;
& > .ant-menu-item {
padding: 0;
text-align: center;
.anticon {
font-size: 14px;
margin-right: 0;
}
}
& > .ant-menu-submenu {
& > .ant-menu-submenu-title {
padding: 0;
text-align: center;
.anticon {
font-size: 14px;
margin-right: 0;
}
&::after {
display: none;
}
}
}
}
}
}
.main {
margin-left: 42px;
}
}
.sider {
width: 224px;
background: #3e3e3e;
position: absolute;
overflow: visible;
padding-bottom: 24px;
height: 100vh;
transition: @transition-ease-out;
box-shadow: @shadow-1;
z-index: 520;
color: #999;
&.light {
background: #fff;
.switchtheme {
background: #fff;
border-top: solid 1px #f8f8f8;
}
}
.logo {
text-align: center;
height: 40px;
line-height: 40px;
cursor: pointer;
margin: 28px 0;
transition: @transition-ease-out;
overflow: hidden;
img {
width: 40px;
margin-right: 8px;
transition: @transition-ease-out;
}
span {
vertical-align: text-bottom;
font-size: 16px;
text-transform: uppercase;
display: inline-block;
}
.anticon {
transition: @transition-ease-out;
}
}
.switchtheme {
width: 100%;
position: absolute;
bottom: 0;
height: 48px;
background-color: @dark-half;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 16px 0 24px;
overflow: hidden;
z-index: 1000;
span {
white-space: nowrap;
overflow: hidden;
}
:global {
.anticon {
min-width: 14px;
margin-right: 8px;
font-size: 14px;
}
}
}
:global {
.ant-menu-dark,
.ant-menu-dark .ant-menu-sub {
color: #999;
}
.ant-menu-root {
height: ~"calc(100vh - 144px)";
overflow-x: hidden;
border-right: none;
&::-webkit-scrollbar-thumb {
background-color: transparent;
}
&:hover {
&::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
}
}
&.ant-menu-vertical {
.ant-menu-sub {
height: 0;
opacity: 0;
transition: none;
}
.ant-menu-submenu-active {
.ant-menu-sub {
height: auto;
opacity: 1;
}
}
}
}
.ant-menu {
transition: @transition-ease-out;
.ant-menu-item,
.ant-menu-submenu-title {
overflow: hidden;
white-space: normal;
}
}
}
}
.main {
margin-left: 224px;
overflow: auto;
height: 100vh;
transition: @transition-ease-out;
:global {
.content-inner {
background: #fff;
padding: 24px;
box-shadow: @shadow-1;
min-height: e("calc(100vh - 184px)");
}
}
.container {
margin: 24px;
}
.content {
min-height: e("calc(100vh - 184px)");
position: relative;
}
}
}
:global .ant-menu-dark {
color: #999;
background-color: #3e3e3e;
.ant-menu-submenu-title:hover,
.ant-menu-submenu:hover {
color: @primary-color;
}
.ant-menu-sub {
color: #999;
}
.ant-menu-submenu-selected {
color: @primary-color;
&:not(.ant-menu-submenu-open) {
background-color: @dark-half;
}
}
.ant-menu-item,
.ant-menu-submenu-title {
& > a {
color: #999;
}
&:hover > a {
color: @primary-color;
}
}
&.ant-menu-inline {
.ant-menu-item-selected {
background-color: @dark-half;
}
}
.ant-menu-item-selected {
color: @primary-color;
background-color: @dark-half;
& > a {
color: @primary-color;
}
&:hover {
background-color: @dark-half;
}
}
}
.spin {
:global .ant-spin-container {
height: 100vh;
}
}
@media (max-width: 767px) {
.layout {
.main {
.container {
margin: 12px;
}
.bread {
padding: 0 12px;
}
.content {
min-height: e("calc(100vh - 160px)");
}
:global {
.content-inner {
padding: 12px;
min-height: e("calc(100vh - 160px)");
}
}
}
}
}
+79
View File
@@ -0,0 +1,79 @@
import React, { PropTypes } from 'react'
import { Menu, Icon } from 'antd'
import { Link } from 'dva/router'
import { menu } from '../../utils'
const topMenus = menu.map(item => item.key)
const getMenus = function (menuArray, siderFold, parentPath = '/') {
return menuArray.map(item => {
const linkTo = parentPath + item.key
if (item.child) {
return (
<Menu.SubMenu key={linkTo} title={<span>{item.icon ? <Icon type={item.icon} /> : ''}{siderFold && topMenus.indexOf(item.key) >= 0 ? '' : item.name}</span>}>
{getMenus(item.child, siderFold, `${linkTo}/`)}
</Menu.SubMenu>
)
}
return (
<Menu.Item key={linkTo}>
<Link to={linkTo}>
{item.icon ? <Icon type={item.icon} /> : ''}
{siderFold && topMenus.indexOf(item.key) >= 0 ? '' : item.name}
</Link>
</Menu.Item>
)
})
}
function Menus ({ siderFold, darkTheme, location, handleClickNavMenu, navOpenKeys, changeOpenKeys }) {
const menuItems = getMenus(menu, siderFold)
const getAncestorKeys = (key) => {
const map = {
'/navigation/navigation2': ['/navigation'],
}
return map[key] || []
}
const onOpenChange = (openKeys) => {
const latestOpenKey = openKeys.find(key => !(navOpenKeys.indexOf(key) > -1))
const latestCloseKey = navOpenKeys.find(key => !(openKeys.indexOf(key) > -1))
let nextOpenKeys = []
if (latestOpenKey) {
nextOpenKeys = getAncestorKeys(latestOpenKey).concat(latestOpenKey)
}
if (latestCloseKey) {
nextOpenKeys = getAncestorKeys(latestCloseKey)
}
changeOpenKeys(nextOpenKeys)
}
let menuProps = !siderFold ? {
onOpenChange,
openKeys: navOpenKeys,
} : {}
return (
<Menu
{...menuProps}
mode={siderFold ? 'vertical' : 'inline'}
theme={darkTheme ? 'dark' : 'light'}
onClick={handleClickNavMenu}
defaultSelectedKeys={[location.pathname !== '/' ? location.pathname : '/dashboard']}
>
{menuItems}
</Menu>
)
}
Menus.propTypes = {
siderFold: PropTypes.bool,
darkTheme: PropTypes.bool,
location: PropTypes.object,
isNavbar: PropTypes.bool,
handleClickNavMenu: PropTypes.func,
navOpenKeys: PropTypes.array,
changeOpenKeys: PropTypes.func,
}
export default Menus
+39
View File
@@ -0,0 +1,39 @@
import React, { PropTypes } from 'react'
import { Icon, Switch } from 'antd'
import styles from './Layout.less'
import { config } from '../../utils'
import Menus from './Menu'
function Sider ({ siderFold, darkTheme, location, changeTheme, navOpenKeys, changeOpenKeys }) {
const menusProps = {
siderFold,
darkTheme,
location,
navOpenKeys,
changeOpenKeys,
}
return (
<div>
<div className={styles.logo}>
<img alt={'logo'} src={config.logo} />
{siderFold ? '' : <span>{config.name}</span>}
</div>
<Menus {...menusProps} />
{!siderFold ? <div className={styles.switchtheme}>
<span><Icon type="bulb" />切换主题</span>
<Switch onChange={changeTheme} defaultChecked={darkTheme} checkedChildren="黑" unCheckedChildren="白" />
</div> : ''}
</div>
)
}
Sider.propTypes = {
siderFold: PropTypes.bool,
darkTheme: PropTypes.bool,
location: PropTypes.object,
changeTheme: PropTypes.func,
navOpenKeys: PropTypes.array,
changeOpenKeys: PropTypes.func,
}
export default Sider
+1
View File
@@ -0,0 +1 @@
@import "../../themes/vars.less";
+8
View File
@@ -0,0 +1,8 @@
import Header from './Header'
import Menu from './Menu'
import Bread from './Bread'
import Sider from './Sider'
import Footer from './Footer'
import styles from './Layout.less'
export { Header, Menu, Bread, Sider, Footer, styles }
+66
View File
@@ -0,0 +1,66 @@
import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import styles from './Search.less'
import { Input, Select, Button, Icon } from 'antd'
class Search extends React.Component {
state = {
clearVisible: false,
selectValue: (this.props.select && this.props.selectProps) ? this.props.selectProps.defaultValue : '',
}
handleSearch = () => {
const data = {
keyword: ReactDOM.findDOMNode(this.refs.searchInput).value,
}
if (this.props.select) {
data.field = this.state.selectValue
}
if (this.props.onSearch) this.props.onSearch(data)
}
handleInputChange = e => {
this.setState({
...this.state,
clearVisible: e.target.value !== '',
})
}
handeleSelectChange = value => {
this.setState({
...this.state,
selectValue: value,
})
}
handleClearInput = () => {
ReactDOM.findDOMNode(this.refs.searchInput).value = ''
this.setState({
clearVisible: false,
})
this.handleSearch()
}
render () {
const { size, select, selectOptions, selectProps, style, keyword } = this.props
const { clearVisible } = this.state
return (
<Input.Group compact size={size} className={styles.search} style={style}>
{select && <Select ref="searchSelect" onChange={this.handeleSelectChange} size={size} {...selectProps}>
{selectOptions && selectOptions.map((item, key) => <Select.Option value={item.value} key={key}>{item.name || item.value}</Select.Option>)}
</Select>}
<Input ref="searchInput" size={size} onChange={this.handleInputChange} onPressEnter={this.handleSearch} defaultValue={keyword} />
<Button size={size} type="primary" onClick={this.handleSearch}>搜索</Button>
{clearVisible && <Icon type="cross" onClick={this.handleClearInput} />}
</Input.Group>
)
}
}
Search.propTypes = {
size: PropTypes.string,
select: PropTypes.bool,
selectProps: PropTypes.object,
onSearch: PropTypes.func,
selectOptions: PropTypes.array,
style: PropTypes.object,
keyword: PropTypes.string,
}
export default Search
+55
View File
@@ -0,0 +1,55 @@
.no-highlight() {
border-color: #e5e5e5;
box-shadow: none;
}
.search {
display: flex;
width: 100%;
position: relative;
:global {
.anticon-cross {
position: absolute;
width: 18px;
height: 18px;
right: 84px;
cursor: pointer;
color: #fff;
line-height: 18px;
border-radius: 50%!important;
background-color: rgba(0, 0, 0, .16);
top: 7px;
// opacity:
}
.ant-select {
width: 80px;
flex-shrink: 0;
flex-grow: 0;
&.ant-select-focused .ant-select-selection,
&.ant-select-open .ant-select-selection,
.ant-select-selection:active,
.ant-select-selection:focus,
.ant-select-selection:hover {
.no-highlight();
}
}
.ant-input {
&:focus,
&:hover {
.no-highlight();
}
flex-shrink: 1;
flex-grow: 1;
}
.ant-btn {
width: 80px;
flex-shrink: 0;
flex-grow: 0;
}
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"name": "Search",
"version": "0.0.0",
"private": true,
"main": "./Search.js"
}
+18
View File
@@ -0,0 +1,18 @@
import DataTable from './DataTable'
import DropOption from './DropOption'
import Iconfont from './Iconfont'
import Search from './Search'
import Editor from './Editor'
import * as Layout from './Layout/index.js'
import layer from './layer'
export {
Layout,
DataTable,
DropOption,
Iconfont,
Search,
Editor,
layer,
}
+83
View File
@@ -0,0 +1,83 @@
import { Modal, message } from 'antd'
import React from 'react'
import ReactDOM from 'react-dom'
import classnames from 'classnames'
import styles from './layer.less'
const { info, success, error, warning, confirm } = Modal
const layer = {
prefixCls: 'ant-layer',
index: 1,
info,
success,
error,
warning,
confirm,
}
layer.close = (index) => new Promise((resolve, reject) => {
const { prefixCls } = layer
let div = document.getElementById(`${prefixCls}-reference-${index}`)
if (index === undefined) {
const references = document.querySelectorAll(`.${prefixCls}-reference`)
div = references[references.length - 1]
}
if (!div) {
message.error('关闭失败,未找到Dom')
return
}
const unmountResult = ReactDOM.unmountComponentAtNode(div)
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div)
resolve(index)
} else {
reject(index)
}
})
layer.closeAll = () => {
const { prefixCls } = layer
const references = document.querySelectorAll(`.${prefixCls}-reference`)
let i = 0
while (i < references.length) {
layer.close()
i++
}
}
layer.open = (config) => {
const props = Object.assign({}, config)
const { content, ...modalProps } = props
const { className, wrapClassName = '', verticalCenter = true } = modalProps
const { prefixCls } = layer
const index = layer.index++
let div = document.createElement('div')
div.id = `${prefixCls}-reference-${index}`
div.className = `${prefixCls}-reference`
document.body.appendChild(div)
ReactDOM.render(
<Modal
visible
title="Title"
transitionName="zoom"
maskTransitionName="fade"
onCancel={() => {
layer.close(index)
}}
onOk={() => {
layer.close(index)
}}
{...modalProps}
wrapClassName={classnames({ [styles.verticalCenter]: verticalCenter, [wrapClassName]: true })}
className={classnames(prefixCls, className, [`${prefixCls}-${index}`])}
>
<div className={`${prefixCls}-body-wrapper`} style={{ maxHeight: document.body.clientHeight - 256 }}>
{content}
</div>
</Modal>, div)
return index
}
export default layer
+14
View File
@@ -0,0 +1,14 @@
.verticalCenter {
display: flex;
align-items: center;
justify-content: center;
:global .ant-modal {
top: 0;
padding-bottom: 0;
.ant-modal-body {
overflow-y: auto;
}
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"name": "layer",
"version": "0.0.0",
"private": true,
"main": "./layer.js"
}
+2 -2
View File
@@ -5,7 +5,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="index.css" />
<link rel="stylesheet" href="/index.css" />
<!--[if lte IE 10]>
<script
src="https://as.alipayobjects.com/g/component/??console-polyfill/0.2.2/index.js,media-match/2.0.2/media.match.min.js"></script>
@@ -14,7 +14,7 @@
<body>
<div id="root"></div>
<script src="index.js"></script>
<script src="/index.js"></script>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
import './index.html'
import 'babel-polyfill'
import dva from 'dva'
import createLoading from 'dva-loading'
import { browserHistory } from 'dva/router'
// 1. Initialize
const app = dva({
...createLoading(),
history: browserHistory,
onError (error) {
console.error('app onError -- ', error)
},
})
// 2. Model
app.model(require('./models/app'))
// 3. Router
app.router(require('./router'))
// 4. Start
app.start('#root')
+130
View File
@@ -0,0 +1,130 @@
import { color } from '../utils/theme'
const Mock = require('mockjs')
const config = require('../utils/config')
const { apiPrefix } = config
const Dashboard = Mock.mock({
'sales|8': [
{
'name|+1': 2008,
'Clothes|200-500': 1,
'Food|180-400': 1,
'Electronics|300-550': 1,
},
],
cpu: {
'usage|50-600': 1,
space: 825,
'cpu|40-90': 1,
'data|20': [
{
'cpu|20-80': 1,
},
],
},
browser: [
{
name: 'Google Chrome',
percent: 43.3,
status: 1,
},
{
name: 'Mozilla Firefox',
percent: 33.4,
status: 2,
},
{
name: 'Apple Safari',
percent: 34.6,
status: 3,
},
{
name: 'Internet Explorer',
percent: 12.3,
status: 4,
},
{
name: 'Opera Mini',
percent: 3.3,
status: 1,
},
{
name: 'Chromium',
percent: 2.53,
status: 1,
},
],
user: {
name: 'zuiidea',
email: 'zuiiidea@.gmail.com',
sales: 3241,
sold: 3556,
avatar: 'http://tva4.sinaimg.cn/crop.0.0.996.996.180/6ee6a3a3jw8f0ks5pk7btj20ro0rodi0.jpg',
},
'completed|12': [
{
'name|+1': 2008,
'Task complete|200-1000': 1,
'Cards Complete|200-1000': 1,
},
],
'comments|5': [
{
name: '@last',
'status|1-3': 1,
content: '@sentence',
avatar () {
return Mock.Random.image('48x48', Mock.Random.color(), '#757575', 'png', this.name.substr(0, 1))
},
date () {
return `2016-${Mock.Random.date('MM-dd')} ${Mock.Random.time('HH:mm:ss')}`
},
},
],
'recentSales|36': [
{
'id|+1': 1,
name: '@last',
'status|1-4': 1,
date () {
return `${Mock.Random.integer(2015, 2016)}-${Mock.Random.date('MM-dd')} ${Mock.Random.time('HH:mm:ss')}`
},
'price|10-200.1-2': 1,
},
],
quote: {
name: 'Joho Doe',
title: 'Graphic Designer',
content: 'I\'m selfish, impatient and a little insecure. I make mistakes, I am out of control and at times hard to handle. But if you can\'t handle me at my worst, then you sure as hell don\'t deserve me at my best.',
avatar: 'http://img.hb.aicdn.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236',
},
numbers: [
{
icon: 'pay-circle-o',
color: color.green,
title: 'Online Review',
number: 2781,
}, {
icon: 'team',
color: color.blue,
title: 'New Customers',
number: 3241,
}, {
icon: 'message',
color: color.purple,
title: 'Active Projects',
number: 253,
}, {
icon: 'shopping-cart',
color: color.red,
title: 'Referrals',
number: 4324,
},
],
})
module.exports = {
[`GET ${apiPrefix}/dashboard`] (req, res) {
res.json(Dashboard)
},
}
+186
View File
@@ -0,0 +1,186 @@
const qs = require('qs')
const Mock = require('mockjs')
const config = require('../utils/config')
const { apiPrefix } = config
let usersListData = Mock.mock({
'data|100': [
{
'id|+1': 1,
name: '@cname',
nickName: '@last',
phone: /^1[34578]\d{9}$/,
'age|11-99': 1,
address: '@county(true)',
isMale: '@boolean',
email: '@email',
createTime: '@datetime',
avatar () {
return Mock.Random.image('100x100', Mock.Random.color(), '#757575', 'png', this.nickName.substr(0, 1))
},
},
],
page: {
total: 100,
current: 1,
},
})
const userPermission = {
DEFAULT: [
'dashboard', 'chart',
],
ADMIN: [
'dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart',
],
DEVELOPER: ['dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart'],
}
const adminUsers = [
{
id: 0,
username: 'admin',
password: 'admin',
permissions: userPermission.ADMIN,
}, {
id: 1,
username: 'guest',
password: 'guest',
permissions: userPermission.DEFAULT,
}, {
id: 2,
username: '吴彦祖',
password: '123456',
permissions: userPermission.DEVELOPER,
},
]
module.exports = {
[`POST ${apiPrefix}/user/login`] (req, res) {
const { username, password } = req.body
const user = adminUsers.filter((item) => item.username === username)
if (user && user[0].password === password) {
const now = new Date()
now.setDate(now.getDate() + 1)
res.cookie('token', JSON.stringify({ id: user[0].id, deadline: now.getTime() }), {
maxAge: 900000,
httpOnly: true,
})
res.json({ success: true, message: 'Ok' })
} else {
res.status(400).send({ message: 'Bad Request' })
}
},
[`GET ${apiPrefix}/user/logout`] (req, res) {
res.clearCookie('token')
res.status(200).send({ message: 'OK' })
},
[`GET ${apiPrefix}/userInfo`] (req, res) {
const cookies = qs.parse(req.headers.cookie, { delimiter: ';' })
const response = {}
const user = {}
if (!cookies.token) {
res.status(200).send({ message: 'Not Login' })
return
}
const token = JSON.parse(cookies.token)
if (token) {
response.success = token.deadline > new Date().getTime()
}
if (response.success) {
const userItem = adminUsers.filter(_ => _.id === token.id)
if (userItem.length > 0) {
user.permissions = userItem[0].permissions
user.username = userItem[0].username
user.id = userItem[0].id
}
}
response.user = user
res.json(response)
},
[`GET ${apiPrefix}/users`] (req, res) {
const page = req.query
const pageSize = page.pageSize || 10
const currentPage = page.page || 1
let data
let newPage
let newData = usersListData.data.concat()
if (page.field) {
const d = newData.filter((item) => {
return item[page.field].indexOf(decodeURI(page.keyword)) > -1
})
data = d.slice((currentPage - 1) * pageSize, currentPage * pageSize)
newPage = {
current: currentPage * 1,
total: d.length,
}
} else {
data = usersListData.data.slice((currentPage - 1) * pageSize, currentPage * pageSize)
usersListData.page.current = currentPage * 1
newPage = usersListData.page
}
res.json({
success: true,
data,
page: {
...newPage,
pageSize: Number(pageSize),
},
})
},
[`POST ${apiPrefix}/users`] (req, res) {
const newData = req.body
newData.createTime = Mock.mock('@now')
newData.avatar = Mock.Random.image('100x100', Mock.Random.color(), '#757575', 'png', newData.nickName.substr(0, 1))
newData.id = usersListData.data.length + 1
usersListData.data.unshift(newData)
usersListData.page.total = usersListData.data.length
usersListData.page.current = 1
res.json({ success: true, data: usersListData.data, page: usersListData.page })
},
[`DELETE ${apiPrefix}/users`] (req, res) {
const deleteItem = req.body
usersListData.data = usersListData.data.filter((item) => {
if (item.id === deleteItem.id) {
return false
}
return true
})
usersListData.page.total = usersListData.data.length
res.json({ success: true, data: usersListData.data, page: usersListData.page })
},
[`PUT ${apiPrefix}/users`] (req, res) {
const editItem = req.body
editItem.createTime = Mock.mock('@now')
editItem.avatar = Mock.Random.image('100x100', Mock.Random.color(), '#757575', 'png', editItem.nickName.substr(0, 1))
usersListData.data = usersListData.data.map((item) => {
if (item.id === editItem.id) {
return editItem
}
return item
})
res.json({ success: true, data: usersListData.data, page: usersListData.page })
},
}
+143
View File
@@ -0,0 +1,143 @@
import { getUserInfo, logout } from '../services/app'
import { routerRedux } from 'dva/router'
import { parse } from 'qs'
import { config } from '../utils'
const { prefix } = config
export default {
namespace: 'app',
state: {
user: {},
loginButtonLoading: false,
menuPopoverVisible: false,
siderFold: localStorage.getItem(`${prefix}siderFold`) === 'true',
darkTheme: localStorage.getItem(`${prefix}darkTheme`) === 'true',
isNavbar: document.body.clientWidth < 769,
navOpenKeys: [],
},
subscriptions: {
setup ({ dispatch }) {
dispatch({ type: 'queryUser' })
window.onresize = () => {
dispatch({ type: 'changeNavbar' })
}
},
},
effects: {
*queryUser ({
payload,
}, { call, put }) {
const data = yield call(getUserInfo, parse(payload))
if (data.success && data.user) {
yield put({
type: 'queryUserSuccess',
payload: data.user,
})
if (location.pathname === '/login') {
yield put(routerRedux.push('/dashboard'))
}
} else {
if (location.pathname !== '/login') {
let from = location.pathname
if (location.pathname === '/dashboard') {
from = '/dashboard'
}
window.location = `${location.origin}/login?from=${from}`
}
}
},
*logout ({
payload,
}, { call, put }) {
const data = yield call(logout, parse(payload))
if (data.success) {
yield put({ type: 'queryUser' })
} else {
throw (data)
}
},
*switchSider ({
payload,
}, { put }) {
yield put({
type: 'handleSwitchSider',
})
},
*changeTheme ({
payload,
}, { put }) {
yield put({
type: 'handleChangeTheme',
})
},
*changeNavbar ({
payload,
}, { put }) {
if (document.body.clientWidth < 769) {
yield put({ type: 'showNavbar' })
} else {
yield put({ type: 'hideNavbar' })
}
},
*switchMenuPopver ({
payload,
}, { put }) {
yield put({
type: 'handleSwitchMenuPopver',
})
},
},
reducers: {
queryUserSuccess (state, { payload: user }) {
return {
...state,
user,
}
},
showLoginButtonLoading (state) {
return {
...state,
loginButtonLoading: true,
}
},
handleSwitchSider (state) {
localStorage.setItem(`${prefix}siderFold`, !state.siderFold)
return {
...state,
siderFold: !state.siderFold,
}
},
handleChangeTheme (state) {
localStorage.setItem(`${prefix}darkTheme`, !state.darkTheme)
return {
...state,
darkTheme: !state.darkTheme,
}
},
showNavbar (state) {
return {
...state,
isNavbar: true,
}
},
hideNavbar (state) {
return {
...state,
isNavbar: false,
}
},
handleSwitchMenuPopver (state) {
return {
...state,
menuPopoverVisible: !state.menuPopoverVisible,
}
},
handleNavOpenKeys (state, { payload: navOpenKeys }) {
console.log(navOpenKeys)
return {
...state,
...navOpenKeys,
}
},
},
}
+229
View File
@@ -0,0 +1,229 @@
import { myCity, queryWeather, query } from '../services/dashboard'
import { parse } from 'qs'
// zuimei 摘自 http://www.zuimeitianqi.com/res/js/index.js
let zuimei = {
parseActualData (actual) {
let weather = {
icon: `http://www.zuimeitianqi.com/res/icon/${zuimei.getIconName(actual.wea, 'big')}`,
name: zuimei.getWeatherName(actual.wea),
temperature: actual.tmp,
dateTime: new Date(actual.PTm).format('MM-dd hh:mm'),
}
return weather
},
getIconName (wea, flg) {
let myDate = new Date()
let hour = myDate.getHours()
let num = 0
if (wea.indexOf('/') !== -1) {
let weas = wea.split('/')
if (hour < 12) {
num = zuimei.replaceIcon(weas[0])
if (num < 6) {
num = `${num}_${flg}_night.png`
} else {
num = `${num}_${flg}.png`
}
} else if (hour >= 12) {
num = zuimei.replaceIcon(weas[1])
if (hour >= 18) {
num = `${num}_${flg}_night.png`
} else {
num = `${num}_${flg}.png`
}
}
} else {
if ((hour >= 18 && hour <= 23) || (hour >= 0 && hour <= 6)) {
num = `${num}_${flg}_night.png`
} else {
num = `${num}_${flg}.png`
}
}
return num
},
replaceIcon (num) {
if (num === 21) {
num = 7
} else if (num === 22) {
num = 8
} else if (num === 10 || num === 11 || num === 12 || num === 23 || num === 24 || num === 25) {
num = 9
} else if (num === 13 || num === 15 || num === 26 || num === 27 || num === 34) {
num = 14
} else if (num === 17 || num === 28) {
num = 16
} else if (num === 35) {
num = 18
} else if (num === 31 || num === 32 || num === 33) {
num = 20
} else if (num === 30) {
num = 29
}
return num
},
getWeatherName (wea) {
let name = ''
if (wea.indexOf('/') !== -1) {
let weas = wea.split('/')
name = `${zuimei.getWeatherByCode(weas[0])}转${zuimei.getWeatherByCode(weas[1])}`
} else {
name = zuimei.getWeatherByCode(wea)
}
return name
},
getWeatherByCode (num) {
let wea = ''
if (num === 0) {
wea = '晴'
} else if (num === 1) {
wea = '多云'
} else if (num === 2) {
wea = '阴'
} else if (num === 3) {
wea = '阵雨'
} else if (num === 4) {
wea = '雷阵雨'
} else if (num === 5) {
wea = '雷阵雨并伴有冰雹'
} else if (num === 6) {
wea = '雨夹雪'
} else if (num === 7) {
wea = '小雨'
} else if (num === 8) {
wea = '中雨'
} else if (num === 9) {
wea = '大雨'
} else if (num === 10) {
wea = '暴雨'
} else if (num === 11) {
wea = '大暴雨'
} else if (num === 12) {
wea = '特大暴雨'
} else if (num === 13) {
wea = '阵雪'
} else if (num === 14) {
wea = '小雪'
} else if (num === 15) {
wea = '中雪'
} else if (num === 16) {
wea = '大雪'
} else if (num === 17) {
wea = '暴雪'
} else if (num === 18) {
wea = '雾'
} else if (num === 19) {
wea = '冻雨'
} else if (num === 20) {
wea = '沙尘暴'
} else if (num === 21) {
wea = '小雨-中雨'
} else if (num === 22) {
wea = '中雨-大雨'
} else if (num === 23) {
wea = '大雨-暴雨'
} else if (num === 24) {
wea = '暴雨-大暴雨'
} else if (num === 25) {
wea = '大暴雨-特大暴雨'
} else if (num === 26) {
wea = '小雪-中雪'
} else if (num === 27) {
wea = '中雪-大雪'
} else if (num === 28) {
wea = '大雪-暴雪'
} else if (num === 29) {
wea = '浮沉'
} else if (num === 30) {
wea = '扬沙'
} else if (num === 31) {
wea = '强沙尘暴'
} else if (num === 32) {
wea = '飑'
} else if (num === 33) {
wea = '龙卷风'
} else if (num === 34) {
wea = '若高吹雪'
} else if (num === 35) {
wea = '轻雾'
} else if (num === 53) {
wea = '霾'
} else if (num === 99) {
wea = '未知'
}
return wea
},
}
export default {
namespace: 'dashboard',
state: {
weather: {
city: '成都',
temperature: '5',
name: '晴',
icon: 'http://www.zuimeitianqi.com/res/icon/0_big.png',
dateTime: new Date().format('MM-dd hh:mm'),
},
sales: [],
quote: {
avatar: 'http://img.hb.aicdn.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236',
},
numbers: [],
recentSales: [],
comments: [],
completed: [],
browser: [],
cpu: {},
user: {
avatar: 'http://img.hb.aicdn.com/bc442cf0cc6f7940dcc567e465048d1a8d634493198c4-sPx5BR_fw236',
},
},
subscriptions: {
setup ({ dispatch }) {
dispatch({ type: 'query' })
dispatch({ type: 'queryWeather' })
},
},
effects: {
*query ({
payload,
}, { call, put }) {
const data = yield call(query, parse(payload))
yield put({ type: 'queryWeather', payload: { ...data } })
},
*queryWeather ({
payload,
}, { call, put }) {
const myCityResult = yield call(myCity, { flg: 0 })
const result = yield call(queryWeather, { cityCode: myCityResult.selectCityCode })
const weather = zuimei.parseActualData(result.data.actual)
weather.city = myCityResult.selectCityName
yield put({ type: 'queryWeatherSuccess', payload: {
weather,
} })
},
},
reducers: {
queryWeatherSuccess (state, action) {
return {
...state,
...action.payload,
}
},
queryWeather (state, action) {
return {
...state,
...action.payload,
}
},
},
}
+45
View File
@@ -0,0 +1,45 @@
import { login } from '../services/login'
import { routerRedux } from 'dva/router'
import { queryURL } from '../utils'
export default {
namespace: 'login',
state: {
loginLoading: false,
},
effects: {
*login ({
payload,
}, { put, call }) {
yield put({ type: 'showLoginLoading' })
const data = yield call(login, payload)
yield put({ type: 'hideLoginLoading' })
if (data.success) {
const from = queryURL('from')
yield put({ type: 'app/queryUser' })
if (from) {
yield put(routerRedux.push(from))
} else {
yield put(routerRedux.push('/dashboard'))
}
} else {
throw data
}
},
},
reducers: {
showLoginLoading (state) {
return {
...state,
loginLoading: true,
}
},
hideLoginLoading (state) {
return {
...state,
loginLoading: false,
}
},
},
}
+129
View File
@@ -0,0 +1,129 @@
import { create, remove, update, query } from '../services/users'
import { parse } from 'qs'
export default {
namespace: 'users',
state: {
list: [],
currentItem: {},
modalVisible: false,
modalType: 'create',
isMotion: localStorage.getItem('antdAdminUserIsMotion') === 'true',
pagination: {
showSizeChanger: true,
showQuickJumper: true,
showTotal: total => `共 ${total} 条`,
current: 1,
total: null,
},
},
subscriptions: {
setup ({ dispatch, history }) {
history.listen(location => {
if (location.pathname === '/users') {
dispatch({
type: 'query',
payload: location.query,
})
}
})
},
},
effects: {
*query ({ payload }, { call, put }) {
const data = yield call(query, parse(payload))
if (data) {
yield put({
type: 'querySuccess',
payload: {
list: data.data,
pagination: data.page,
},
})
}
},
*'delete' ({ payload }, { call, put }) {
const data = yield call(remove, { id: payload })
if (data && data.success) {
yield put({
type: 'querySuccess',
payload: {
list: data.data,
pagination: {
total: data.page.total,
current: data.page.current,
},
},
})
}
},
*create ({ payload }, { call, put }) {
yield put({ type: 'hideModal' })
const data = yield call(create, payload)
if (data && data.success) {
yield put({
type: 'querySuccess',
payload: {
list: data.data,
pagination: {
total: data.page.total,
current: data.page.current,
},
},
})
}
},
*update ({ payload }, { select, call, put }) {
yield put({ type: 'hideModal' })
const id = yield select(({ users }) => users.currentItem.id)
const newUser = { ...payload, id }
const data = yield call(update, newUser)
if (data && data.success) {
yield put({
type: 'querySuccess',
payload: {
list: data.data,
pagination: {
total: data.page.total,
current: data.page.current,
},
},
})
}
},
*switchIsMotion ({
payload,
}, { put }) {
yield put({
type: 'handleSwitchIsMotion',
})
},
},
reducers: {
querySuccess (state, action) {
const { list, pagination } = action.payload
return { ...state,
list,
pagination: {
...state.pagination,
...pagination,
} }
},
showModal (state, action) {
return { ...state, ...action.payload, modalVisible: true }
},
hideModal (state) {
return { ...state, modalVisible: false }
},
handleSwitchIsMotion (state) {
localStorage.setItem('antdAdminUserIsMotion', !state.isMotion)
return { ...state, isMotion: !state.isMotion }
},
},
}
+139
View File
@@ -0,0 +1,139 @@
import React, { PropTypes } from 'react'
import { Router } from 'dva/router'
import App from './routes/app'
const cached = {}
const registerModel = (app, model) => {
if (!cached[model.namespace]) {
app.model(model)
cached[model.namespace] = 1
}
}
const Routers = function ({ history, app }) {
const routes = [
{
path: '/',
component: App,
getIndexRoute (nextState, cb) {
require.ensure([], require => {
registerModel(app, require('./models/dashboard'))
cb(null, { component: require('./routes/dashboard/') })
}, 'dashboard')
},
childRoutes: [
{
path: 'dashboard',
getComponent (nextState, cb) {
require.ensure([], require => {
registerModel(app, require('./models/dashboard'))
cb(null, require('./routes/dashboard/'))
}, 'dashboard')
},
}, {
path: 'users',
getComponent (nextState, cb) {
require.ensure([], require => {
registerModel(app, require('./models/users'))
cb(null, require('./routes/users/'))
}, 'users')
},
}, {
path: 'login',
getComponent (nextState, cb) {
require.ensure([], require => {
registerModel(app, require('./models/login'))
cb(null, require('./routes/login/'))
}, 'login')
},
}, {
path: 'request',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/request/'))
}, 'request')
},
}, {
path: 'UIElement/iconfont',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/UIElement/iconfont/'))
}, 'UIElement-iconfont')
},
}, {
path: 'UIElement/search',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/UIElement/search/'))
}, 'UIElement-search')
},
}, {
path: 'UIElement/dropOption',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/UIElement/dropOption/'))
}, 'UIElement-dropOption')
},
}, {
path: 'UIElement/layer',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/UIElement/layer/'))
}, 'UIElement-layer')
},
}, {
path: 'UIElement/dataTable',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/UIElement/dataTable/'))
}, 'UIElement-dataTable')
},
}, {
path: 'UIElement/editor',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/UIElement/editor/'))
}, 'UIElement-editor')
},
}, {
path: 'chart/lineChart',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/chart/lineChart/'))
}, 'chart-lineChart')
},
}, {
path: 'chart/barChart',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/chart/barChart/'))
}, 'chart-barChart')
},
}, {
path: 'chart/areaChart',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/chart/areaChart/'))
}, 'chart-areaChart')
},
}, {
path: '*',
getComponent (nextState, cb) {
require.ensure([], require => {
cb(null, require('./routes/error/'))
}, 'error')
},
},
],
},
]
return <Router history={history} routes={routes} />
}
Routers.propTypes = {
history: PropTypes.object,
app: PropTypes.object,
}
export default Routers
+137
View File
@@ -0,0 +1,137 @@
import React from 'react'
import { DataTable } from '../../../components'
import { Table, Row, Col, Card, Select } from 'antd'
class DataTablePage extends React.Component {
constructor (props) {
super(props)
this.state = { filterCase: {
gender: '',
} }
}
handleSelectChange = (gender) => {
this.setState({
filterCase: {
gender,
},
})
}
render () {
const { filterCase } = this.state
const staticDataTableProps = {
dataSource: [{ key: '1', name: 'John Brown', age: 24, address: 'New York' }, { key: '2', name: 'Jim Green', age: 23, address: 'London' }],
columns: [{ title: 'name', dataIndex: 'name' }, { title: 'Age', dataIndex: 'age' }, { title: 'Address', dataIndex: 'address' }],
pagination: false,
}
const fetchDataTableProps = {
fetch: {
url: 'https://randomuser.me/api',
data: {
results: 10,
testPrams: 'test',
},
dataKey: 'results',
},
columns: [
{ title: 'Name', dataIndex: 'name', render: (text) => `${text.first} ${text.last}` },
{ title: 'Phone', dataIndex: 'phone' },
{ title: 'Gender', dataIndex: 'gender' },
],
rowKey: 'registered',
}
const caseChangeDataTableProps = {
fetch: {
url: 'https://randomuser.me/api',
data: {
results: 10,
testPrams: 'test',
...filterCase,
},
dataKey: 'results',
},
columns: [
{ title: 'Name', dataIndex: 'name', render: (text) => `${text.first} ${text.last}` },
{ title: 'Phone', dataIndex: 'phone' },
{ title: 'Gender', dataIndex: 'gender' },
],
rowKey: 'registered',
}
return (<div className="content-inner">
<Row gutter={32}>
<Col lg={12} md={24}>
<Card title="默认">
<DataTable pagination={false} />
</Card>
</Col>
<Col lg={12} md={24}>
<Card title="静态数据">
<DataTable
{...staticDataTableProps}
/>
</Card>
</Col>
<Col lg={12} md={24}>
<Card title="远程数据">
<DataTable
{...fetchDataTableProps}
/>
</Card>
</Col>
<Col lg={12} md={24}>
<Card title="参数变化">
<Select placeholder="Please select gender" allowClear onChange={this.handleSelectChange} style={{ width: 200, marginBottom: 16 }}>
<Select.Option value="male">Male</Select.Option>
<Select.Option value="female">Female</Select.Option>
</Select>
<DataTable
{...caseChangeDataTableProps}
/>
</Card>
</Col>
</Row>
<h2 style={{ margin: '16px 0' }}>Props</h2>
<Row>
<Col lg={18} md={24}>
<Table
rowKey={(record, key) => key}
pagination={false}
bordered
scroll={{ x: 800 }}
columns={[
{
title: '参数',
dataIndex: 'props',
},
{
title: '说明',
dataIndex: 'desciption',
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '默认值',
dataIndex: 'default',
},
]}
dataSource={[
{
props: 'fetch',
desciption: '远程获取数据的参数',
type: 'Object',
default: '后面有空加上',
}]}
/>
</Col>
</Row>
</div>)
}
}
export default DataTablePage
+95
View File
@@ -0,0 +1,95 @@
import React from 'react'
import { DropOption } from '../../../components'
import { Table, Row, Col, Card, message } from 'antd'
const DropOptionPage = () => <div className="content-inner">
<Row gutter={32}>
<Col lg={8} md={12}>
<Card title="默认">
<DropOption menuOptions={[{ key: '1', name: '编辑' }, { key: '2', name: '删除' }]} />
</Card>
</Col>
<Col lg={8} md={12}>
<Card title="样式">
<DropOption menuOptions={[{ key: '1', name: '编辑' }, { key: '2', name: '删除' }]} buttonStyle={{ border: 'solid 1px #eee', width: 60 }} />
</Card>
</Col>
<Col lg={8} md={12}>
<Card title="事件">
<DropOption
menuOptions={[{ key: '1', name: '编辑' }, { key: '2', name: '删除' }]}
buttonStyle={{ border: 'solid 1px #eee', width: 60 }}
onMenuClick={({ key }) => {
switch (key) {
case '1':
message.success('点击了编辑')
break
case '2':
message.success('点击了删除')
break
default:
break
}
}}
/>
</Card>
</Col>
</Row>
<h2 style={{ margin: '16px 0' }}>Props</h2>
<Row>
<Col lg={18} md={24}>
<Table
rowKey={(record, key) => key}
pagination={false}
bordered
scroll={{ x: 800 }}
columns={[
{
title: '参数',
dataIndex: 'props',
},
{
title: '说明',
dataIndex: 'desciption',
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '默认值',
dataIndex: 'default',
},
]}
dataSource={[
{
props: 'menuOptions',
desciption: '下拉操作的选项,格式为[{name:string,key:string}]',
type: 'Array',
default: '必选',
},
{
props: 'onMenuClick',
desciption: '点击 menuitem 调用此函数,参数为 {item, key, keyPath}',
type: 'Function',
default: '-',
},
{
props: 'buttonStyle',
desciption: '按钮的样式',
type: 'Object',
default: '-',
},
{
props: 'dropdownProps',
desciption: '下拉菜单的参数,可参考antd的【Dropdown】组件',
type: 'Object',
default: '-',
},
]}
/>
</Col>
</Row>
</div>
export default DropOptionPage
+80
View File
@@ -0,0 +1,80 @@
import React from 'react'
import { Editor } from '../../../components'
import { convertToRaw } from 'draft-js'
import { Row, Col, Card } from 'antd'
import draftToHtml from 'draftjs-to-html'
import draftToMarkdown from 'draftjs-to-markdown'
// https://github.com/jpuri/react-draft-wysiwyg/blob/master/docs/src/components/Demo/index.js
export default class EditorPage extends React.Component {
constructor (props) {
super(props)
this.state = {
editorContent: null,
}
}
onEditorStateChange = (editorContent) => {
this.setState({
editorContent,
})
}
render () {
const { editorContent } = this.state
const colProps = {
lg: 12,
md: 24,
}
const textareaStyle = {
minHeight: 496,
width: '100%',
background: '#f7f7f7',
borderColor: '#F1F1F1',
padding: '16px 8px',
}
return (<div className="content-inner">
<Row gutter={32}>
<Col {...colProps}>
<Card title="Editor" style={{ overflow: 'visible' }}>
<Editor
wrapperStyle={{
minHeight: 500,
}}
editorStyle={{
minHeight: 376,
}}
editorState={editorContent}
onEditorStateChange={this.onEditorStateChange}
/>
</Card>
</Col>
<Col {...colProps}>
<Card title="HTML">
<textarea
style={textareaStyle}
disabled
value={editorContent ? draftToHtml(convertToRaw(editorContent.getCurrentContent())) : ''}
/>
</Card>
</Col>
<Col {...colProps}>
<Card title="Markdown">
<textarea
style={textareaStyle}
disabled
value={editorContent ? draftToMarkdown(convertToRaw(editorContent.getCurrentContent())) : ''}
/>
</Card>
</Col>
<Col {...colProps}>
<Card title="JSON">
<textarea
style={textareaStyle}
disabled
value={editorContent ? JSON.stringify(convertToRaw(editorContent.getCurrentContent())) : ''}
/>
</Card>
</Col>
</Row>
</div>)
}
}
+50
View File
@@ -0,0 +1,50 @@
import React from 'react'
import { Iconfont } from '../../../components'
import { Table, Row, Col } from 'antd'
import styles from './index.less'
const iconlist = ['Cherry', 'Cheese', 'Bread', 'Beer', 'Beet', 'Bacon', 'Banana', 'Asparagus', 'Apple']
const IcoPage = () => <div className="content-inner">
<ul className={styles.list}>
{iconlist.map(item => <li key={item}><Iconfont className={styles.icon} type={item} /><span className={styles.name}>{item}</span></li>)}
</ul>
<h2 style={{ margin: '16px 0' }}>Props</h2>
<Row>
<Col lg={18} md={24}>
<Table
rowKey={(record, key) => key}
pagination={false}
bordered
scroll={{ x: 800 }}
columns={[
{
title: '参数',
dataIndex: 'props',
},
{
title: '说明',
dataIndex: 'desciption',
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '默认值',
dataIndex: 'default',
},
]}
dataSource={[
{
props: 'type',
desciption: '图标类型',
type: 'String',
default: '-',
}]}
/>
</Col>
</Row>
</div>
export default IcoPage
+33
View File
@@ -0,0 +1,33 @@
.list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin-right: -24px;
li {
width: 160px;
text-align: center;
cursor: pointer;
height: 100px;
transition: all 0.5s ease;
display: flex;
flex-direction: column;
align-items: center;
margin-right: 24px;
margin-bottom: 24px;
box-sizing: border-box;
border: solid 1px transparent;
&:hover {
border: solid 1px #f5f5f5;
}
}
.name {
margin-top: 16px;
}
:global .iconfont {
font-size: 48px;
}
}
+145
View File
@@ -0,0 +1,145 @@
import React from 'react'
import { layer } from '../../../components'
import { Table, Row, Col, Button, Card } from 'antd'
let Enum = {
default: 1,
}
const IcoPage = () => {
const handleButtonClick = (key) => {
if (key === Enum.default) {
layer.open({
title: '默认弹层',
content: <div style={{ height: 360 }}>弹层内容</div>,
})
}
}
return (<div className="content-inner">
<Row gutter={32}>
<Col lg={8} md={12}>
<Card title="默认">
<Button type="primary" onClick={handleButtonClick.bind(null, Enum.default)}>打开一个Layer</Button>
</Card>
</Col>
</Row>
<h2 style={{ margin: '16px 0' }}>API</h2>
<div style={{ margin: '16px 0' }}>
<h2 style={{ margin: '4px 0' }}>layer.open(config)</h2>
config对象与<a href="https://ant.design/components/modal-cn/#API" target="_blank">Modal</a>的参数基本一致,config属性如下。
注意:1.visible属性一般不需要设置;2.afterClose无效,layer.close()可代替;3.layer.open()返回一个唯一的layer Id
</div>
<Row>
<Col lg={18} md={24}>
<Table
rowKey={(record, key) => key}
pagination={false}
bordered
scroll={{ x: 800 }}
columns={[
{
title: '参数',
dataIndex: 'props',
},
{
title: '说明',
dataIndex: 'desciption',
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '默认值',
dataIndex: 'default',
},
]}
dataSource={[
{
props: 'content',
desciption: '内容',
type: 'string|ReactNode',
default: '无',
},
{
props: 'title',
desciption: '标题',
type: 'string|ReactNode',
default: '标题',
},
{
props: 'confirmLoading',
desciption: '确定按钮 loading ',
type: 'boolean',
default: '无',
},
{
props: 'closable',
desciption: '是否显示右上角的关闭按钮',
type: 'boolean',
default: '无',
},
{
props: 'onOk',
desciption: '点击确定回调',
type: 'function(e)',
default: '无',
},
{
props: 'onCancel',
desciption: '点击遮罩层或右上角叉或取消按钮的回调',
type: 'function(e)',
default: '"无"',
},
{
props: 'width',
desciption: '宽度',
type: 'string|number',
default: '520',
},
{
props: 'okText',
desciption: '确认按钮文字',
type: 'string',
default: '无',
},
{
props: 'cancelText',
desciption: '取消按钮文字',
type: 'string',
default: '无',
},
{
props: 'maskClosable',
desciption: '点击蒙层是否允许关闭',
type: 'boolean',
default: 'true',
},
{
props: 'style',
desciption: '可用于设置浮层的样式,调整浮层位置等',
type: 'object',
default: '-',
},
{
props: 'wrapClassName',
desciption: '对话框外层容器的类名',
type: 'string',
default: '-',
},
]}
/>
</Col>
</Row>
<div style={{ margin: '16px 0' }}>
<h2 style={{ margin: '4px 0' }}>layer.close(index)</h2>
当index有值时,关闭指定Id的layer;当index无值时,关闭最顶层layer
</div>
<div style={{ margin: '16px 0' }}>
<h2 style={{ margin: '4px 0' }}>layer.closeAll()</h2>
关闭所有的layer
</div>
</div>)
}
export default IcoPage
+98
View File
@@ -0,0 +1,98 @@
import React from 'react'
import { Search } from '../../../components'
import { Table, Row, Col, Card } from 'antd'
const SearchPage = () => <div className="content-inner">
<Row gutter={32}>
<Col lg={8} md={12}>
<Card title="默认">
<Search />
</Card>
</Col>
<Col lg={8} md={12}>
<Card title="附带选择">
<Search
{...{
select: true,
selectOptions: [
{ value: 'components', name: '组件' },
{ value: 'page', name: '页面' },
],
selectProps: {
defaultValue: 'components',
},
}}
/>
</Card>
</Col>
<Col lg={8} md={12}>
<Card title="大小">
<Search size="large" style={{ marginBottom: 16 }} />
<Search size="small" />
</Card>
</Col>
</Row>
<h2 style={{ margin: '16px 0' }}>Props</h2>
<Row>
<Col lg={18} md={24}>
<Table
rowKey={(record, key) => key}
pagination={false}
bordered
scroll={{ x: 800 }}
columns={[
{
title: '参数',
dataIndex: 'props',
},
{
title: '说明',
dataIndex: 'desciption',
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '默认值',
dataIndex: 'default',
},
]}
dataSource={[
{
props: 'size',
desciption: '设置Search大小,可选值为 【small】 【large】 或者不设',
type: 'String',
default: '-',
},
{
props: 'select',
desciption: '设置是否有选择器',
type: 'Boolean',
default: 'false',
},
{
props: 'selectOptions',
desciption: '选择器的选项,格式为[{name:"",value:""}]或者[{value:""}]',
type: 'Array',
default: '-',
},
{
props: 'selectProps',
desciption: '选择器的属性,可参考antd的【Select】组件',
type: 'Object',
default: '-',
},
{
props: 'onSearch',
desciption: '点击搜索按钮, 按Enter键或者点击清除时的回调',
type: 'Function({keyword:string,field:string})',
default: '-',
},
]}
/>
</Col>
</Row>
</div>
export default SearchPage
+86
View File
@@ -0,0 +1,86 @@
import React, { PropTypes } from 'react'
import { connect } from 'dva'
import { Layout } from '../components'
import { classnames, config } from '../utils'
import { Helmet } from 'react-helmet'
import '../themes/index.less'
const { Header, Bread, Footer, Sider, styles } = Layout
const App = ({ children, location, dispatch, app }) => {
const { user, siderFold, darkTheme, isNavbar, menuPopoverVisible, navOpenKeys } = app
const headerProps = {
user,
siderFold,
location,
isNavbar,
menuPopoverVisible,
navOpenKeys,
switchMenuPopover () {
dispatch({ type: 'app/switchMenuPopver' })
},
logout () {
dispatch({ type: 'app/logout' })
},
switchSider () {
dispatch({ type: 'app/switchSider' })
},
changeOpenKeys (openKeys) {
dispatch({ type: 'app/handleNavOpenKeys', payload: { navOpenKeys: openKeys } })
},
}
const siderProps = {
siderFold,
darkTheme,
location,
navOpenKeys,
changeTheme () {
dispatch({ type: 'app/changeTheme' })
},
changeOpenKeys (openKeys) {
localStorage.setItem('navOpenKeys', JSON.stringify(openKeys))
dispatch({ type: 'app/handleNavOpenKeys', payload: { navOpenKeys: openKeys } })
},
}
if (config.openPages && config.openPages.indexOf(location.pathname) > -1) {
return <div>{children}</div>
}
return (
<div>
<Helmet>
<title>ANTD ADMIN</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href={config.logoSrc} type="image/x-icon" />
{config.iconFontUrl ? <script src={config.iconFontUrl}></script> : ''}
</Helmet>
<div className={classnames(styles.layout, { [styles.fold]: isNavbar ? false : siderFold }, { [styles.withnavbar]: isNavbar })}>
{!isNavbar ? <aside className={classnames(styles.sider, { [styles.light]: !darkTheme })}>
<Sider {...siderProps} />
</aside> : ''}
<div className={styles.main}>
<Header {...headerProps} />
<Bread location={location} />
<div className={styles.container}>
<div className={styles.content}>
{children}
</div>
</div>
<Footer />
</div>
</div>
</div>
)
}
App.propTypes = {
children: PropTypes.element.isRequired,
location: PropTypes.object,
dispatch: PropTypes.func,
app: PropTypes.object,
}
export default connect(({ app }) => ({ app }))(App)
+21
View File
@@ -0,0 +1,21 @@
import React, { PropTypes } from 'react'
import styles from './Container.less'
import { ResponsiveContainer } from 'recharts'
const Container = ({ children, ratio = 5 / 2, minHeight = 250, maxHeight = 350 }) => <div className={styles.container} style={{ minHeight, maxHeight }}>
<div style={{ marginTop: `${100 / ratio}%` || '100%' }}></div>
<div className={styles.content} style={{ minHeight, maxHeight }}>
<ResponsiveContainer>
{children}
</ResponsiveContainer>
</div>
</div>
Container.propTypes = {
children: PropTypes.element.isRequired,
ratio: PropTypes.number,
minHeight: PropTypes.number,
maxHeight: PropTypes.number,
}
export default Container
+20
View File
@@ -0,0 +1,20 @@
.container {
width: 100%;
position: relative;
display: inline-block;
:global {
.recharts-responsive-container {
width: e("calc(100% + 56px)")!important;
margin-left: -32px;
}
}
}
.content {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
+279
View File
@@ -0,0 +1,279 @@
import React from 'react'
import { Row, Col, Card, Button } from 'antd'
import Container from '../Container'
import * as d3 from 'd3-shape'
import {
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
} from 'recharts'
const data = [
{
name: 'Page A',
uv: 4000,
pv: 2400,
amt: 2400,
}, {
name: 'Page B',
uv: 3000,
pv: 1398,
amt: 2210,
}, {
name: 'Page C',
uv: 2000,
pv: 9800,
amt: 2290,
}, {
name: 'Page D',
uv: 2780,
pv: 3908,
amt: 2000,
}, {
name: 'Page E',
uv: 1890,
pv: 4800,
amt: 2181,
}, {
name: 'Page F',
uv: 2390,
pv: 3800,
amt: 2500,
}, {
name: 'Page G',
uv: 3490,
pv: 4300,
amt: 2100,
},
]
const mixData = [
{
name: 'Page A',
uv: 4000,
pv: 2400,
amt: 2400,
}, {
name: 'Page B',
uv: 3000,
pv: 1398,
amt: 2210,
}, {
name: 'Page C',
uv: 2000,
pv: 9800,
amt: 2290,
}, {
name: 'Page D',
uv: 2780,
pv: 3908,
amt: 2000,
}, {
name: 'Page E',
uv: 1890,
pv: 4800,
amt: 2181,
}, {
name: 'Page F',
uv: 2390,
pv: 3800,
amt: 2500,
}, {
name: 'Page G',
uv: 3490,
pv: 4300,
amt: 2100,
},
]
const percentData = [
{
month: '2015.01',
a: 4000,
b: 2400,
c: 2400,
}, {
month: '2015.02',
a: 3000,
b: 1398,
c: 2210,
}, {
month: '2015.03',
a: 2000,
b: 9800,
c: 2290,
}, {
month: '2015.04',
a: 2780,
b: 3908,
c: 2000,
}, {
month: '2015.05',
a: 1890,
b: 4800,
c: 2181,
}, {
month: '2015.06',
a: 2390,
b: 3800,
c: 2500,
}, {
month: '2015.07',
a: 3490,
b: 4300,
c: 2100,
},
]
const colProps = {
lg: 12,
md: 24,
}
const SimpleAreaChart = () => (
<Container>
<AreaChart data={data} margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Area type="monotone" dataKey="uv" stroke="#8884d8" fill="#8884d8" />
</AreaChart>
</Container>
)
const StackedAreaChart = () => (
<Container>
<AreaChart data={mixData} margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Area type="monotone" dataKey="uv" stackId="1" stroke="#8884d8" fill="#8884d8" />
<Area type="monotone" dataKey="pv" stackId="1" stroke="#82ca9d" fill="#82ca9d" />
<Area type="monotone" dataKey="amt" stackId="1" stroke="#ffc658" fill="#ffc658" />
</AreaChart>
</Container>
)
// StackedAreaChart
const toPercent = (decimal, fixed = 0) => {
return `${(decimal * 100).toFixed(fixed)}%`
}
const getPercent = (value, total) => {
const ratio = total > 0
? value / total
: 0
return toPercent(ratio, 2)
}
const renderTooltipContent = (o) => {
const { payload, label } = o
const total = payload.reduce((result, entry) => (result + entry.value), 0)
return (
<div className="customized-tooltip-content">
<p className="total">{`${label} (Total: ${total})`}</p>
<ul className="list">
{payload.map((entry, index) => (
<li key={`item-${index}`} style={{
color: entry.color,
}}>
{`${entry.name}: ${entry.value}(${getPercent(entry.value, total)})`}
</li>
))}
</ul>
</div>
)
}
const PercentAreaChart = () => (
<Container>
<AreaChart data={percentData} stackOffset="expand" margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}>
<XAxis dataKey="month" />
<YAxis tickFormatter={toPercent} />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip content={renderTooltipContent} />
<Area type="monotone" dataKey="a" stackId="1" stroke="#8884d8" fill="#8884d8" />
<Area type="monotone" dataKey="b" stackId="1" stroke="#82ca9d" fill="#82ca9d" />
<Area type="monotone" dataKey="c" stackId="1" stroke="#ffc658" fill="#ffc658" />
</AreaChart>
</Container>
)
// CardinalAreaChart
const cardinal = d3.curveCardinal.tension(0.2)
const CardinalAreaChart = () => (
<Container>
<AreaChart data={mixData} margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Area type="monotone" dataKey="uv" stroke="#8884d8" fill="#8884d8" fillOpacity={0.3} />
<Area type={cardinal} dataKey="pv" stroke="#82ca9d" fill="#82ca9d" fillOpacity={0.3} />
</AreaChart>
</Container>
)
const EditorPage = () => (
<div className="content-inner">
<Button type="primary" size="large" style={{
position: 'absolute',
right: 0,
top: -48,
}}>
<a href="http://recharts.org/#/en-US/examples/TinyBarChart" target="blank">Show More</a>
</Button>
<Row gutter={32}>
<Col {...colProps}>
<Card title="SimpleAreaChart">
<SimpleAreaChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="StackedAreaChart">
<StackedAreaChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="PercentAreaChart">
<PercentAreaChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="CardinalAreaChart">
<CardinalAreaChart />
</Card>
</Col>
</Row>
</div>
)
export default EditorPage
+225
View File
@@ -0,0 +1,225 @@
import React, { PropTypes } from 'react'
import { Row, Col, Card, Button } from 'antd'
import Container from '../Container'
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
} from 'recharts'
const data = [
{
name: 'Page A',
uv: 4000,
pv: 2400,
amt: 2400,
}, {
name: 'Page B',
uv: 3000,
pv: 1398,
amt: 2210,
}, {
name: 'Page C',
uv: 2000,
pv: 9800,
amt: 2290,
}, {
name: 'Page D',
uv: 2780,
pv: 3908,
amt: 2000,
}, {
name: 'Page E',
uv: 1890,
pv: 4800,
amt: 2181,
}, {
name: 'Page F',
uv: 2390,
pv: 3800,
amt: 2500,
}, {
name: 'Page G',
uv: 3490,
pv: 4300,
amt: 2100,
},
]
const mixData = [
{
name: 'Page A',
uv: 4000,
female: 2400,
male: 2400,
}, {
name: 'Page B',
uv: 3000,
female: 1398,
male: 2210,
}, {
name: 'Page C',
uv: 2000,
female: 9800,
male: 2290,
}, {
name: 'Page D',
uv: 2780,
female: 3908,
male: 2000,
}, {
name: 'Page E',
uv: 1890,
female: 4800,
male: 2181,
}, {
name: 'Page F',
uv: 2390,
female: 3800,
male: 2500,
}, {
name: 'Page G',
uv: 3490,
female: 4300,
male: 2100,
},
]
const colProps = {
lg: 12,
md: 24,
}
const SimpleBarChart = () => (
<Container>
<BarChart data={data} margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Bar dataKey="pv" fill="#8884d8" />
<Bar dataKey="uv" fill="#82ca9d" />
</BarChart>
</Container>
)
const StackedBarChart = () => (
<Container>
<BarChart data={data} margin={{
top: 20,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Bar dataKey="pv" stackId="a" fill="#8884d8" />
<Bar dataKey="uv" stackId="a" fill="#82ca9d" />
</BarChart>
</Container>
)
const MixBarChart = () => (
<Container>
<BarChart data={mixData} margin={{
top: 20,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Bar dataKey="female" stackId="a" fill="#8884d8" />
<Bar dataKey="male" stackId="a" fill="#82ca9d" />
<Bar dataKey="uv" fill="#ffc658" />
</BarChart>
</Container>
)
// CustomShapeBarChart
const getPath = (x, y, width, height) => {
return `M${x},${y + height}
C${x + width / 3},${y + height} ${x + width / 2},${y + height / 3} ${x + width / 2}, ${y}
C${x + width / 2},${y + height / 3} ${x + 2 * width / 3},${y + height} ${x + width}, ${y + height}
Z`
}
const TriangleBar = (props) => {
const { fill, x, y, width, height } = props
return <path d={getPath(x, y, width, height)} stroke="none" fill={fill} />
}
TriangleBar.propTypes = {
fill: PropTypes.string,
x: PropTypes.number,
y: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
}
const CustomShapeBarChart = () => (
<Container>
<BarChart data={mixData} margin={{
top: 20,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Bar dataKey="female" fill="#8884d8" shape={< TriangleBar />} label />
</BarChart>
</Container>
)
const EditorPage = () => (
<div className="content-inner">
<Button type="primary" size="large" style={{
position: 'absolute',
right: 0,
top: -48,
}}>
<a href="http://recharts.org/#/en-US/examples/TinyBarChart" target="blank">Show More</a>
</Button>
<Row gutter={32}>
<Col {...colProps}>
<Card title="SimpleBarChart">
<SimpleBarChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="StackedBarChart">
<StackedBarChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="MixBarChart">
<MixBarChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="CustomShapeBarChart">
<CustomShapeBarChart />
</Card>
</Col>
</Row>
</div>
)
export default EditorPage
+193
View File
@@ -0,0 +1,193 @@
import React, { PropTypes } from 'react'
import { Row, Col, Card, Button } from 'antd'
import Container from '../Container'
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
} from 'recharts'
const data = [
{
name: 'Page A',
uv: 4000,
pv: 2400,
amt: 2400,
}, {
name: 'Page B',
uv: 3000,
pv: 1398,
amt: 2210,
}, {
name: 'Page C',
uv: 2000,
pv: 9800,
amt: 2290,
}, {
name: 'Page D',
uv: 2780,
pv: 3908,
amt: 2000,
}, {
name: 'Page E',
uv: 1890,
pv: 4800,
amt: 2181,
}, {
name: 'Page F',
uv: 2390,
pv: 3800,
amt: 2500,
}, {
name: 'Page G',
uv: 3490,
pv: 4300,
amt: 2100,
},
]
const colProps = {
lg: 12,
md: 24,
}
const SimpleLineChart = () => (
<Container>
<LineChart data={data} margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="pv" stroke="#8884d8" activeDot={{
r: 8,
}} />
<Line type="monotone" dataKey="uv" stroke="#82ca9d" />
</LineChart>
</Container>
)
const VerticalLineChart = () => (
<Container>
<LineChart layout="vertical" width={600} height={300} data={data} margin={{
top: 20,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis type="number" />
<YAxis dataKey="name" type="category" />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Line dataKey="pv" stroke="#8884d8" />
<Line dataKey="uv" stroke="#82ca9d" />
</LineChart>
</Container>
)
const DashedLineChart = () => (
<Container>
<LineChart width={600} height={300} data={data} margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="pv" stroke="#8884d8" strokeDasharray="5 5" />
<Line type="monotone" dataKey="uv" stroke="#82ca9d" strokeDasharray="3 4 5 2" />
</LineChart>
</Container>
)
// CustomizedDotLineChart
const CustomizedDot = ({ cx, cy, payload }) => {
if (payload.value > 2500) {
return (
<svg x={cx - 10} y={cy - 10} width={20} height={20} fill="red" viewBox="0 0 1024 1024">
<path d="M512 1009.984c-274.912 0-497.76-222.848-497.76-497.76s222.848-497.76 497.76-497.76c274.912 0 497.76 222.848 497.76 497.76s-222.848 497.76-497.76 497.76zM340.768 295.936c-39.488 0-71.52 32.8-71.52 73.248s32.032 73.248 71.52 73.248c39.488 0 71.52-32.8 71.52-73.248s-32.032-73.248-71.52-73.248zM686.176 296.704c-39.488 0-71.52 32.8-71.52 73.248s32.032 73.248 71.52 73.248c39.488 0 71.52-32.8 71.52-73.248s-32.032-73.248-71.52-73.248zM772.928 555.392c-18.752-8.864-40.928-0.576-49.632 18.528-40.224 88.576-120.256 143.552-208.832 143.552-85.952 0-164.864-52.64-205.952-137.376-9.184-18.912-31.648-26.592-50.08-17.28-18.464 9.408-21.216 21.472-15.936 32.64 52.8 111.424 155.232 186.784 269.76 186.784 117.984 0 217.12-70.944 269.76-186.784 8.672-19.136 9.568-31.2-9.12-40.096z" />
</svg>
)
}
return (
<svg x={cx - 10} y={cy - 10} width={20} height={20} fill="green" viewBox="0 0 1024 1024">
<path d="M517.12 53.248q95.232 0 179.2 36.352t145.92 98.304 98.304 145.92 36.352 179.2-36.352 179.2-98.304 145.92-145.92 98.304-179.2 36.352-179.2-36.352-145.92-98.304-98.304-145.92-36.352-179.2 36.352-179.2 98.304-145.92 145.92-98.304 179.2-36.352zM663.552 261.12q-15.36 0-28.16 6.656t-23.04 18.432-15.872 27.648-5.632 33.28q0 35.84 21.504 61.44t51.2 25.6 51.2-25.6 21.504-61.44q0-17.408-5.632-33.28t-15.872-27.648-23.04-18.432-28.16-6.656zM373.76 261.12q-29.696 0-50.688 25.088t-20.992 60.928 20.992 61.44 50.688 25.6 50.176-25.6 20.48-61.44-20.48-60.928-50.176-25.088zM520.192 602.112q-51.2 0-97.28 9.728t-82.944 27.648-62.464 41.472-35.84 51.2q-1.024 1.024-1.024 2.048-1.024 3.072-1.024 8.704t2.56 11.776 7.168 11.264 12.8 6.144q25.6-27.648 62.464-50.176 31.744-19.456 79.36-35.328t114.176-15.872q67.584 0 116.736 15.872t81.92 35.328q37.888 22.528 63.488 50.176 17.408-5.12 19.968-18.944t0.512-18.944-3.072-7.168-1.024-3.072q-26.624-55.296-100.352-88.576t-176.128-33.28z" />
</svg>
)
}
CustomizedDot.propTypes = {
cx: PropTypes.number,
cy: PropTypes.number,
payload: PropTypes.object,
}
const CustomizedDotLineChart = () => (
<Container>
<LineChart width={600} height={300} data={data} margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="pv" stroke="#8884d8" dot={< CustomizedDot />} />
<Line type="monotone" dataKey="uv" stroke="#82ca9d" />
</LineChart>
</Container>
)
const EditorPage = () => (
<div className="content-inner">
<Button type="primary" size="large" style={{
position: 'absolute',
right: 0,
top: -48,
}}>
<a href="http://recharts.org/#/en-US/examples/TinyBarChart" target="blank">Show More</a>
</Button>
<Row gutter={32}>
<Col {...colProps}>
<Card title="SimpleLineChart">
<SimpleLineChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="DashedLineChart">
<DashedLineChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="CustomizedDotLineChart">
<CustomizedDotLineChart />
</Card>
</Col>
<Col {...colProps}>
<Card title="VerticalLineChart">
<VerticalLineChart />
</Card>
</Col>
</Row>
</div>
)
export default EditorPage
@@ -0,0 +1,41 @@
import React, { PropTypes } from 'react'
import { Table, Tag } from 'antd'
import styles from './browser.less'
import { color } from '../../../utils'
const status = {
1: {
color: color.green,
},
2: {
color: color.red,
},
3: {
color: color.blue,
},
4: {
color: color.yellow,
},
}
function Browser ({ data }) {
const columns = [
{
title: 'name',
dataIndex: 'name',
className: styles.name,
}, {
title: 'percent',
dataIndex: 'percent',
className: styles.percent,
render: (text, it) => <Tag color={status[it.status].color}>{text}%</Tag>,
},
]
return <Table pagination={false} showHeader={false} columns={columns} rowKey={(record, key) => key} dataSource={data} />
}
Browser.propTypes = {
data: PropTypes.array,
}
export default Browser
@@ -0,0 +1,7 @@
.percent {
text-align: right!important;
}
.name {
text-align: left!important;
}
@@ -0,0 +1,53 @@
import React, { PropTypes } from 'react'
import { Table, Tag } from 'antd'
import styles from './comments.less'
import { color } from '../../../utils'
const status = {
1: {
color: color.green,
text: 'APPROVED',
},
2: {
color: color.yellow,
text: 'PENDING',
},
3: {
color: color.red,
text: 'REJECTED',
},
}
function Comments ({ data }) {
const columns = [
{
title: 'avatar',
dataIndex: 'avatar',
width: 48,
className: styles.avatarcolumn,
render: text => <span style={{ backgroundImage: `url(${text})` }} className={styles.avatar} />,
}, {
title: 'content',
dataIndex: 'content',
render: (text, it) => <div>
<h5 className={styles.name}>{it.name}</h5>
<p className={styles.content}>{it.content}</p>
<div className={styles.daterow}>
<Tag color={status[it.status].color}>{status[it.status].text}</Tag>
<span className={styles.date}>{it.date}</span>
</div>
</div>,
},
]
return (
<div className={styles.comments}>
<Table pagination={false} showHeader={false} columns={columns} rowKey={(record, key) => key} dataSource={data.filter((item, key) => key < 3)} />
</div>
)
}
Comments.propTypes = {
data: PropTypes.array,
}
export default Comments
@@ -0,0 +1,43 @@
@import "../../../themes/vars.less";
.comments {
:global .ant-table-thead > tr > th {
background: #fff;
border-bottom: solid 1px @border-color-base;
}
.avatar {
width: 48px;
height: 48px;
background-position: center;
background-size: cover;
border-radius: 50%;
background: #f8f8f8;
display: inline-block;
}
.content {
text-align: left;
color: #757575;
}
.date {
color: #a3a3a3;
line-height: 30px;
}
.daterow {
display: flex;
justify-content: space-between;
}
.name {
font-size: 14px;
color: #474747;
text-align: left;
}
.avatarcolumn {
vertical-align: top;
}
}
@@ -0,0 +1,43 @@
import React, { PropTypes } from 'react'
import styles from './completed.less'
import classnames from 'classnames'
import { color } from '../../../utils'
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'
function Completed ({ data }) {
return (
<div className={styles.sales}>
<div className={styles.title}>TEAM TOTAL COMPLETED</div>
<ResponsiveContainer minHeight={360}>
<AreaChart data={data}>
<Legend verticalAlign="top"
content={prop => {
const { payload } = prop
return (<ul className={classnames({ [styles.legend]: true, clearfix: true })}>
{payload.map((item, key) => <li key={key}><span className={styles.radiusdot} style={{ background: item.color }} />{item.value}</li>)}
</ul>)
}}
/>
<XAxis dataKey="name" axisLine={{ stroke: color.borderBase, strokeWidth: 1 }} tickLine={false} />
<YAxis axisLine={false} tickLine={false} />
<CartesianGrid vertical={false} stroke={color.borderBase} strokeDasharray="3 3" />
<Tooltip
wrapperStyle={{ border: 'none', boxShadow: '4px 4px 40px rgba(0, 0, 0, 0.05)' }}
content={content => {
const list = content.payload.map((item, key) => <li key={key} className={styles.tipitem}><span className={styles.radiusdot} style={{ background: item.color }} />{`${item.name}:${item.value}`}</li>)
return <div className={styles.tooltip}><p className={styles.tiptitle}>{content.label}</p><ul>{list}</ul></div>
}}
/>
<Area type="monotone" dataKey="Task complete" stroke={color.grass} fill={color.grass} strokeWidth={2} dot={{ fill: '#fff' }} activeDot={{ r: 5, fill: '#fff', stroke: color.green }} />
<Area type="monotone" dataKey="Cards Complete" stroke={color.sky} fill={color.sky} strokeWidth={2} dot={{ fill: '#fff' }} activeDot={{ r: 5, fill: '#fff', stroke: color.blue }} />
</AreaChart>
</ResponsiveContainer>
</div>
)
}
Completed.propTypes = {
data: PropTypes.array,
}
export default Completed
@@ -0,0 +1,50 @@
@import "../../../themes/vars.less";
.sales {
.title {
margin-left: 32px;
font-size: 16px;
}
}
.radiusdot {
width: 12px;
height: 12px;
margin-right: 8px;
border-radius: 50%;
display: inline-block;
}
.legend {
color: @dark-half;
text-align: right;
color: #999;
font-size: 14px;
li {
height: 48px;
line-height: 48px;
display: inline-block;
&+li {
margin-left: 24px;
}
}
}
.tooltip {
background: #fff;
padding: 20px;
font-size: 14px;
.tiptitle {
font-weight: 700;
font-size: 16px;
margin-bottom: 8px;
}
.tipitem {
height: 32px;
line-height: 32px;
}
}
+61
View File
@@ -0,0 +1,61 @@
import React, { PropTypes } from 'react'
import styles from './cpu.less'
import { color } from '../../../utils'
import CountUp from 'react-countup'
import { LineChart, Line, XAxis, YAxis, CartesianGrid, ResponsiveContainer } from 'recharts'
const countUpProps = {
start: 0,
duration: 2.75,
useEasing: true,
useGrouping: true,
separator: ',',
}
function Cpu ({ usage, space, cpu, data }) {
return (<div className={styles.cpu}>
<div className={styles.number}>
<div className={styles.item}>
<p>usage</p>
<p><CountUp
end={usage}
suffix="GB"
{...countUpProps}
/></p>
</div>
<div className={styles.item}>
<p>space</p>
<p><CountUp
end={space}
suffix="GB"
{...countUpProps}
/></p>
</div>
<div className={styles.item}>
<p>cpu</p>
<p><CountUp
end={cpu}
suffix="%"
{...countUpProps}
/></p>
</div>
</div>
<ResponsiveContainer minHeight={300}>
<LineChart data={data} margin={{ left: -40 }}>
<XAxis dataKey="name" axisLine={{ stroke: color.borderBase, strokeWidth: 1 }} tickLine={false} />
<YAxis axisLine={false} tickLine={false} />
<CartesianGrid vertical={false} stroke={color.borderBase} strokeDasharray="3 3" />
<Line type="monotone" connectNulls dataKey="cpu" stroke={color.blue} fill={color.blue} />
</LineChart>
</ResponsiveContainer>
</div>)
}
Cpu.propTypes = {
data: PropTypes.array,
usage: PropTypes.number,
space: PropTypes.number,
cpu: PropTypes.number,
}
export default Cpu
+40
View File
@@ -0,0 +1,40 @@
.cpu {
.number {
display: flex;
height: 64px;
justify-content: space-between;
margin-bottom: 32px;
.item {
text-align: center;
height: 64px;
width: 100%;
position: relative;
&+.item {
&::before {
content: '';
display: block;
width: 1px;
height: 40px;
position: absolute;
background: #f5f5f5;
top: 12px;
}
}
p {
color: #757575;
&:first-child {
font-size: 16px;
}
&:last-child {
font-size: 20px;
font-weight: 700;
}
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
import NumberCard from './numberCard'
import Quote from './quote'
import Sales from './sales'
import Weather from './weather'
import RecentSales from './recentSales'
import Comments from './comments'
import Completed from './completed'
import Browser from './browser'
import Cpu from './cpu'
import User from './user'
export { NumberCard, Quote, Sales, Weather, RecentSales, Comments, Completed, Browser, Cpu, User }
@@ -0,0 +1,36 @@
import React, { PropTypes } from 'react'
import { Icon, Card } from 'antd'
import CountUp from 'react-countup'
import styles from './numberCard.less'
function NumberCard ({ icon, color, title, number, countUp }) {
return (
<Card className={styles.numberCard} bordered={false} bodyStyle={{ padding: 0 }}>
<Icon className={styles.iconWarp} style={{ color }} type={icon} />
<div className={styles.content}>
<p className={styles.title}>{title || 'No Title'}</p>
<p className={styles.number}>
<CountUp
start={0}
end={number}
duration={2.75}
useEasing
useGrouping
separator=","
{...countUp || {}}
/>
</p>
</div>
</Card>
)
}
NumberCard.propTypes = {
icon: PropTypes.string,
color: PropTypes.string,
title: PropTypes.string,
number: PropTypes.number,
countUp: PropTypes.object,
}
export default NumberCard
@@ -0,0 +1,32 @@
@import "../../../themes/vars.less";
.numberCard {
padding: 32px;
margin-bottom: 24px;
cursor: pointer;
.iconWarp {
font-size: 54px;
float: left;
}
.content {
width: 100%;
padding-left: 78px;
.title {
line-height: 16px;
font-size: 16px;
margin-bottom: 8px;
height: 16px;
.text-overflow();
}
.number {
line-height: 32px;
font-size: 24px;
height: 32px;
.text-overflow();
}
}
}
+28
View File
@@ -0,0 +1,28 @@
import React, { PropTypes } from 'react'
import styles from './quote.less'
function Quote ({ name, content, title, avatar }) {
return (
<div className={styles.quote}>
<div className={styles.inner}>
{content}
</div>
<div className={styles.footer}>
<div className={styles.description}>
<p>-{name}-</p>
<p>{title}</p>
</div>
<div className={styles.avatar} style={{ backgroundImage: `url(${avatar})` }} />
</div>
</div>
)
}
Quote.propTypes = {
name: PropTypes.string,
content: PropTypes.string,
title: PropTypes.string,
avatar: PropTypes.string,
}
export default Quote
@@ -0,0 +1,52 @@
@import "../../../themes/vars.less";
.quote {
color: #fff;
height: 100%;
width: 100%;
padding: 24px;
font-size: 16px;
font-weight: 700;
.inner {
text-overflow: ellipsis;
word-wrap: normal;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
overflow: hidden;
text-indent: 24px;
}
.footer {
position: relative;
margin-top: 14px;
.description {
width: 100%;
p {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 64px;
text-align: right;
&:last-child {
font-weight: 100;
}
}
}
.avatar {
width: 48px;
height: 48px;
background-position: center;
background-size: cover;
border-radius: 50%;
position: absolute;
right: 0;
top: 0;
}
}
}
@@ -0,0 +1,55 @@
import React, { PropTypes } from 'react'
import { Table, Tag } from 'antd'
import styles from './recentSales.less'
import { color } from '../../../utils'
const status = {
1: {
color: color.green,
text: 'SALE',
},
2: {
color: color.yellow,
text: 'REJECT',
},
3: {
color: color.red,
text: 'TAX',
},
4: {
color: color.blue,
text: 'EXTENDED',
},
}
function RecentSales ({ data }) {
const columns = [
{
title: 'NAME',
dataIndex: 'name',
}, {
title: 'STATUS',
dataIndex: 'status',
render: text => <Tag color={status[text].color}>{status[text].text}</Tag>,
}, {
title: 'DATE',
dataIndex: 'date',
render: text => new Date(text).format('yyyy-MM-dd'),
}, {
title: 'PRICE',
dataIndex: 'price',
render: (text, it) => <span style={{ color: status[it.status].color }}>${text}</span>,
},
]
return (
<div className={styles.recentsales}>
<Table pagination={false} columns={columns} rowKey={(record, key) => key} dataSource={data.filter((item, key) => key < 5)} />
</div>
)
}
RecentSales.propTypes = {
data: PropTypes.array,
}
export default RecentSales
@@ -0,0 +1,8 @@
@import "../../../themes/vars.less";
.recentsales {
:global .ant-table-thead > tr > th {
background: #fff;
border-bottom: solid 1px @border-color-base;
}
}
+44
View File
@@ -0,0 +1,44 @@
import React, { PropTypes } from 'react'
import styles from './sales.less'
import classnames from 'classnames'
import { color } from '../../../utils'
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'
function Sales ({ data }) {
return (
<div className={styles.sales}>
<div className={styles.title}>Yearly Sales</div>
<ResponsiveContainer minHeight={360}>
<LineChart data={data}>
<Legend verticalAlign="top"
content={prop => {
const { payload } = prop
return (<ul className={classnames({ [styles.legend]: true, clearfix: true })}>
{payload.map((item, key) => <li key={key}><span className={styles.radiusdot} style={{ background: item.color }} />{item.value}</li>)}
</ul>)
}}
/>
<XAxis dataKey="name" axisLine={{ stroke: color.borderBase, strokeWidth: 1 }} tickLine={false} />
<YAxis axisLine={false} tickLine={false} />
<CartesianGrid vertical={false} stroke={color.borderBase} strokeDasharray="3 3" />
<Tooltip
wrapperStyle={{ border: 'none', boxShadow: '4px 4px 40px rgba(0, 0, 0, 0.05)' }}
content={content => {
const list = content.payload.map((item, key) => <li key={key} className={styles.tipitem}><span className={styles.radiusdot} style={{ background: item.color }} />{`${item.name}:${item.value}`}</li>)
return <div className={styles.tooltip}><p className={styles.tiptitle}>{content.label}</p><ul>{list}</ul></div>
}}
/>
<Line type="monotone" dataKey="Food" stroke={color.purple} strokeWidth={3} dot={{ fill: color.purple }} activeDot={{ r: 5, strokeWidth: 0 }} />
<Line type="monotone" dataKey="Clothes" stroke={color.red} strokeWidth={3} dot={{ fill: color.red }} activeDot={{ r: 5, strokeWidth: 0 }} />
<Line type="monotone" dataKey="Electronics" stroke={color.green} strokeWidth={3} dot={{ fill: color.green }} activeDot={{ r: 5, strokeWidth: 0 }} />
</LineChart>
</ResponsiveContainer>
</div>
)
}
Sales.propTypes = {
data: PropTypes.array,
}
export default Sales

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