Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c874e0263e | ||
|
|
edfff63422 | ||
|
|
78cc06a851 | ||
|
|
f7108940a4 | ||
|
|
d9c97f4030 | ||
|
|
0a53a4ba3d | ||
|
|
5b9543839b | ||
|
|
ddfa5ebfba | ||
|
|
22d649291d | ||
|
|
91786f45e9 | ||
|
|
5c45c9a2af | ||
|
|
51fca79ad6 | ||
|
|
ee222153bd | ||
|
|
610dfdfeb1 | ||
|
|
100bfcedb6 | ||
|
|
c6e4883647 | ||
|
|
3d8dee0260 | ||
|
|
7820e6bb39 | ||
|
|
03dff913e9 | ||
|
|
599f2796a3 | ||
|
|
67c02d09ab | ||
|
|
cf096ff90d | ||
|
|
92b4107f7b | ||
|
|
0dbd6e9fbc | ||
|
|
1492ec93d4 | ||
|
|
258250b2d4 | ||
|
|
713a1085dd | ||
|
|
ae5ae862b4 | ||
|
|
bea4eb1c46 | ||
|
|
cadb984a13 | ||
|
|
19f4bd0ffd | ||
|
|
f898104eb4 | ||
|
|
d5eff2cdd5 | ||
|
|
f841dfc370 | ||
|
|
06358a1194 | ||
|
|
507faa988e | ||
|
|
ab8e470217 | ||
|
|
3be8415946 | ||
|
|
20847e701d | ||
|
|
0ae64525fc | ||
|
|
74d2359a04 | ||
|
|
5abc2ccb2f | ||
|
|
48d4e11576 | ||
|
|
4fc3a98c5f | ||
|
|
c35bc641fd | ||
|
|
512bee666b | ||
|
|
e43ab00e97 | ||
|
|
75d0287688 | ||
|
|
2d1642b47e | ||
|
|
d4459e355f |
@@ -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 +0,0 @@
|
||||
src/**/*-test.js
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
*.log
|
||||
.cache
|
||||
.DS_Store
|
||||
.idea
|
||||
build
|
||||
dist
|
||||
node_modules
|
||||
yarn.lock
|
||||
npm-debug.log
|
||||
|
||||
@@ -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}]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -1,113 +0,0 @@
|
||||
# Antd Admin
|
||||
|
||||
[](https://github.com/facebook/react)
|
||||
[](https://github.com/ant-design/ant-design)
|
||||
[](https://github.com/dvajs/dva)
|
||||
|
||||
[](https://github.com/zuiidea/antd-admin)
|
||||
[](https://github.com/zuiidea/antd-admin/pulls)
|
||||
[](http://opensource.org/licenses/MIT)
|
||||
[](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
|
||||
|
||||

|
||||
|
||||
移动
|
||||
|
||||

|
||||
@@ -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"]}});
|
||||
@@ -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"]}});
|
||||
@@ -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"]}});
|
||||
@@ -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>
|
||||
|
Before Width: | Height: | Size: 262 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 645 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 2.6 MiB |
@@ -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 |
@@ -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"]}});
|
||||
@@ -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>
|
||||
@@ -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,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
|
||||
@@ -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
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "DataTable",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./DataTable.js"
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "DropOption",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./DropOption.js"
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Editor",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./Editor.js"
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
:global .iconfont {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Iconfont",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./Iconfont.js"
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
@import "../../themes/vars.less";
|
||||
|
||||
.bread {
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 +0,0 @@
|
||||
@import "../../themes/vars.less";
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Search",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./Search.js"
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "layer",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./layer.js"
|
||||
}
|
||||
@@ -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')
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
@@ -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 })
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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 }
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||