Compare commits

..
50 Commits
Author SHA1 Message Date
zuiidea c874e0263e Update index.html 2017-07-17 10:16:51 +08:00
zuiidea edfff63422 Update index.html 2017-05-09 15:30:36 +08:00
zuiidea 78cc06a851 Update index.html 2017-04-25 17:23:47 +08:00
zuiidea f7108940a4 Update CNAME 2017-04-25 17:20:49 +08:00
zuiidea d9c97f4030 Update index.html 2017-04-21 18:49:59 +08:00
zuiidea 0a53a4ba3d Update index.html 2017-04-21 18:49:11 +08:00
zuiidea 5b9543839b Update index.html 2017-04-21 18:48:28 +08:00
zuiidea ddfa5ebfba Create CNAME 2017-04-13 10:21:21 +08:00
zuiidea 22d649291d update 2017-03-27 20:31:56 +08:00
zuiidea 91786f45e9 update 2017-03-23 17:31:08 +08:00
zuiidea 5c45c9a2af update 2017-03-15 16:50:10 +08:00
zuiidea 51fca79ad6 upadte 2017-03-13 18:13:06 +08:00
zuiidea ee222153bd update 2017-02-27 23:15:13 +08:00
zuiidea 610dfdfeb1 update 2017-02-27 23:02:58 +08:00
zuiidea 100bfcedb6 update 2017-02-27 22:44:24 +08:00
zuiidea c6e4883647 update 2017-02-23 22:14:20 +08:00
zuiidea 3d8dee0260 update 2017-02-23 14:03:14 +08:00
zuiidea 7820e6bb39 update 2017-02-15 09:40:58 +08:00
zuiidea 03dff913e9 'update' 2017-01-30 23:43:00 +08:00
zuiidea 599f2796a3 '666' 2017-01-24 19:49:50 +08:00
zuiidea 67c02d09ab update 2017-01-09 23:15:36 +08:00
zuiidea cf096ff90d update 2017-01-08 23:03:09 +08:00
zuiidea 92b4107f7b update 2017-01-04 14:33:00 +08:00
zuiidea 0dbd6e9fbc 😲 2016-12-29 23:42:32 +08:00
zuiidea 1492ec93d4 update 2016-12-28 00:17:34 +08:00
zuiidea 258250b2d4 update 2016-12-28 00:12:25 +08:00
zuiidea 713a1085dd update 2016-12-27 22:14:11 +08:00
zuiidea ae5ae862b4 update 2016-12-26 18:04:40 +08:00
zuiidea bea4eb1c46 update 2016-12-21 22:54:45 +08:00
zuiidea cadb984a13 update 2016-12-15 23:04:54 +08:00
zuiidea 19f4bd0ffd update 2016-12-15 09:36:27 +08:00
zuiidea f898104eb4 update 2016-12-07 09:56:47 +08:00
zuiidea d5eff2cdd5 update 2016-12-05 22:38:31 +08:00
zuiidea f841dfc370 clean gh-pages 2016-12-05 21:23:10 +08:00
zuiidea 06358a1194 update 2016-12-04 00:02:28 +08:00
zuiidea 507faa988e update 2016-12-03 23:19:26 +08:00
zuiidea ab8e470217 update 2016-11-30 22:58:14 +08:00
zuiidea 3be8415946 update 2016-11-29 23:11:47 +08:00
zuiidea 20847e701d 2016-11-29 update 2016-11-29 23:06:52 +08:00
zuiidea 0ae64525fc update 11-28 2016-11-28 23:06:04 +08:00
zuiidea 74d2359a04 update 2016-11-07 23:51:37 +08:00
zuiidea 5abc2ccb2f update 2016-11-07 23:39:36 +08:00
zuiidea 48d4e11576 update 2016-11-01 2016-11-02 22:37:27 +08:00
zuiidea 4fc3a98c5f update 10-25 2016-10-25 19:32:57 +08:00
zuiidea c35bc641fd CNAME 2016-09-20 22:35:21 +08:00
zuiidea 512bee666b 2016/09/19 2016-09-19 23:12:37 +08:00
zuiidea e43ab00e97 update 2016/09/14 2016-09-17 16:20:21 +08:00
zuiidea 75d0287688 2016-09-12 2016-09-12 23:28:26 +08:00
zuiidea 2d1642b47e init 2016-09-11 19:06:49 +08:00
zuiidea d4459e355f Create gh-pages branch via GitHub 2016-09-11 16:31:19 +08:00
141 changed files with 353 additions and 6571 deletions
-16
View File
@@ -1,16 +0,0 @@
# 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
@@ -1 +0,0 @@
src/**/*-test.js
-34
View File
@@ -1,34 +0,0 @@
{
"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
}
}
}
+5 -2
View File
@@ -1,4 +1,7 @@
*.log
.cache
.DS_Store
.idea
build
dist
node_modules
yarn.lock
npm-debug.log
-26
View File
@@ -1,26 +0,0 @@
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
@@ -1,5 +0,0 @@
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
@@ -1,22 +0,0 @@
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
@@ -1,113 +0,0 @@
# 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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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"]}});
+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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 645 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

-35
View File
@@ -1,35 +0,0 @@
### 开发环境
- 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)

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

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
@@ -0,0 +1 @@
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
@@ -0,0 +1,12 @@
<!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
@@ -1,56 +0,0 @@
{
"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
@@ -1,66 +0,0 @@
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
@@ -1,111 +0,0 @@
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
@@ -1,6 +0,0 @@
{
"name": "DataTable",
"version": "0.0.0",
"private": true,
"main": "./DataTable.js"
}
-24
View File
@@ -1,24 +0,0 @@
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
@@ -1,6 +0,0 @@
{
"name": "DropOption",
"version": "0.0.0",
"private": true,
"main": "./DropOption.js"
}
-11
View File
@@ -1,11 +0,0 @@
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
@@ -1,111 +0,0 @@
.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
@@ -1,6 +0,0 @@
{
"name": "Editor",
"version": "0.0.0",
"private": true,
"main": "./Editor.js"
}
-14
View File
@@ -1,14 +0,0 @@
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
@@ -1,8 +0,0 @@
:global .iconfont {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
font-size: 16px;
}
-6
View File
@@ -1,6 +0,0 @@
{
"name": "Iconfont",
"version": "0.0.0",
"private": true,
"main": "./Iconfont.js"
}
-71
View File
@@ -1,71 +0,0 @@
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
@@ -1,8 +0,0 @@
@import "../../themes/vars.less";
.bread {
height: 64px;
line-height: 64px;
padding: 0 24px;
margin-bottom: -24px;
}
-9
View File
@@ -1,9 +0,0 @@
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
@@ -1,12 +0,0 @@
@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
@@ -1,63 +0,0 @@
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
@@ -1,57 +0,0 @@
@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
@@ -1,293 +0,0 @@
@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
@@ -1,79 +0,0 @@
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
@@ -1,39 +0,0 @@
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
@@ -1 +0,0 @@
@import "../../themes/vars.less";
-8
View File
@@ -1,8 +0,0 @@
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
@@ -1,66 +0,0 @@
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
@@ -1,55 +0,0 @@
.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
@@ -1,6 +0,0 @@
{
"name": "Search",
"version": "0.0.0",
"private": true,
"main": "./Search.js"
}
-18
View File
@@ -1,18 +0,0 @@
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
@@ -1,83 +0,0 @@
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
@@ -1,14 +0,0 @@
.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
@@ -1,6 +0,0 @@
{
"name": "layer",
"version": "0.0.0",
"private": true,
"main": "./layer.js"
}
-23
View File
@@ -1,23 +0,0 @@
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
@@ -1,130 +0,0 @@
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
@@ -1,186 +0,0 @@
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
@@ -1,143 +0,0 @@
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
@@ -1,229 +0,0 @@
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
@@ -1,45 +0,0 @@
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
@@ -1,129 +0,0 @@
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
@@ -1,139 +0,0 @@
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
@@ -1,137 +0,0 @@
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
@@ -1,95 +0,0 @@
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
@@ -1,80 +0,0 @@
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
@@ -1,50 +0,0 @@
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
@@ -1,33 +0,0 @@
.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
@@ -1,145 +0,0 @@
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
@@ -1,98 +0,0 @@
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
@@ -1,86 +0,0 @@
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
@@ -1,21 +0,0 @@
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
@@ -1,20 +0,0 @@
.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
@@ -1,279 +0,0 @@
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
@@ -1,225 +0,0 @@
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
@@ -1,193 +0,0 @@
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
@@ -1,41 +0,0 @@
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
@@ -1,7 +0,0 @@
.percent {
text-align: right!important;
}
.name {
text-align: left!important;
}
@@ -1,53 +0,0 @@
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
@@ -1,43 +0,0 @@
@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;
}
}
@@ -1,43 +0,0 @@
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
@@ -1,50 +0,0 @@
@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
@@ -1,61 +0,0 @@
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
@@ -1,40 +0,0 @@
.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
@@ -1,12 +0,0 @@
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 }
@@ -1,36 +0,0 @@
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
@@ -1,32 +0,0 @@
@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
@@ -1,28 +0,0 @@
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
@@ -1,52 +0,0 @@
@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;
}
}
}
@@ -1,55 +0,0 @@
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

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