├── .editorconfig
├── .env
├── .eslintrc
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
└── launch.json
├── README.md
├── config
├── config.js
├── menu.config.js
├── platform.config.js
├── theme.config.js
└── versions.config.json
├── jsconfig.json
├── mock
├── .gitkeep
├── CryptoJS.js
├── global.js
├── login.js
├── menu.js
├── pathAnalysis.js
├── tree.js
└── users.js
├── package.json
├── public
└── favicon.ico
├── src
├── app.js
├── assets
│ ├── d3tree.png
│ ├── demo.png
│ ├── demo_login.png
│ ├── frame.png
│ ├── logo.png
│ ├── logo_blue_1024.png
│ ├── qrcode_for_wechat.jpg
│ ├── regionalAnalysis.png
│ ├── vew1.png
│ └── view3.png
├── components
│ ├── Announcement
│ │ └── index.js
│ ├── Breadcrumb
│ │ ├── index.js
│ │ └── index.less
│ ├── CanvasChart
│ │ ├── WaterWave
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ └── autoHeight.js
│ ├── Consumer
│ │ └── index.js
│ ├── D3Chart
│ │ ├── Sankey
│ │ │ └── index.js
│ │ └── Tree
│ │ │ ├── _.js
│ │ │ └── index.js
│ ├── ECharts
│ │ ├── A_basic
│ │ │ ├── .test.js
│ │ │ ├── dataZoom.js
│ │ │ ├── dataset.js
│ │ │ ├── index.js
│ │ │ ├── index.propTypes.js
│ │ │ ├── series.js
│ │ │ ├── setY2Series.js
│ │ │ ├── tooltip.js
│ │ │ ├── type.js
│ │ │ ├── xAxis.js
│ │ │ └── yAxis.js
│ │ ├── B_basic
│ │ │ ├── .test.js
│ │ │ ├── dataset.js
│ │ │ ├── index.js
│ │ │ ├── index.propTypes.js
│ │ │ ├── series.js
│ │ │ ├── tooltip.js
│ │ │ └── type.js
│ │ ├── __tests__
│ │ │ └── index.test.js
│ │ ├── charts
│ │ │ ├── Area
│ │ │ │ └── index.js
│ │ │ ├── Bar
│ │ │ │ └── index.js
│ │ │ ├── Funnel
│ │ │ │ └── index.js
│ │ │ ├── Line
│ │ │ │ └── index.js
│ │ │ ├── Pie-doughnut
│ │ │ │ └── index.js
│ │ │ ├── Pie
│ │ │ │ └── index.js
│ │ │ ├── Radar
│ │ │ │ └── index.js
│ │ │ ├── Sankey
│ │ │ │ └── index.js
│ │ │ ├── bar-y
│ │ │ │ └── index.js
│ │ │ ├── candlestick
│ │ │ │ └── index.js
│ │ │ └── chinaMap
│ │ │ │ ├── index.js
│ │ │ │ └── index.test.js
│ │ ├── components
│ │ │ ├── grid.js
│ │ │ ├── grid.propTypes.js
│ │ │ ├── legend.js
│ │ │ ├── legend.propTypes.js
│ │ │ ├── title.js
│ │ │ ├── title.propTypes.js
│ │ │ ├── toolbox.js
│ │ │ └── toolbox.propTypes.js
│ │ ├── config.js
│ │ ├── core
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── mapData
│ │ │ └── china.json
│ │ └── methods
│ │ │ ├── .test.js
│ │ │ ├── _formatNumer.js
│ │ │ ├── _isData.js
│ │ │ ├── _toDataset_A.js
│ │ │ ├── _toDataset_B.js
│ │ │ └── index.js
│ ├── Exception
│ │ ├── index.js
│ │ ├── index.less
│ │ └── typeConfig.js
│ ├── GlobalDrawer
│ │ └── index.js
│ ├── HeaderSearch
│ │ ├── index.js
│ │ └── index.less
│ ├── Icon
│ │ └── index.js
│ ├── Loader
│ │ ├── Loader.js
│ │ ├── Loader.less
│ │ └── package.json
│ ├── PageHeader
│ │ ├── breadcrumb.js
│ │ ├── index.js
│ │ └── index.less
│ ├── PageLoading
│ │ ├── index.js
│ │ └── index.less
│ ├── PageWrapper
│ │ ├── index.js
│ │ └── index.less
│ ├── Register
│ │ ├── index.js
│ │ └── index.less
│ ├── ResetPassword
│ │ ├── index.js
│ │ └── index.less
│ ├── dataTable
│ │ ├── _.js
│ │ ├── header.js
│ │ ├── index.js
│ │ ├── search.js
│ │ ├── select.js
│ │ └── tableFooter.js
│ └── index.js
├── global.css
├── global.less
├── layouts
│ ├── Context.js
│ ├── __tests__
│ │ └── index.test.js
│ ├── basic
│ │ ├── Footer
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── index.less
│ ├── components
│ │ ├── Authorized
│ │ │ └── index.js
│ │ ├── GlobalDownload
│ │ │ └── index.js
│ │ ├── GlobalFooter
│ │ │ ├── demo
│ │ │ │ └── basic.md
│ │ │ ├── index.d.ts
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ └── index.md
│ │ ├── GlobalHeader
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ ├── GlobalRoll
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ ├── GlobalSearch
│ │ │ ├── _.js
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ ├── GlobalUserCenter
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ ├── HeaderDropdown
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ ├── Menus
│ │ │ ├── _.js
│ │ │ └── index.js
│ │ ├── Notice
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ └── SelectLang
│ │ │ ├── index.js
│ │ │ └── index.less
│ ├── constant.js
│ ├── index.js
│ └── platform
│ │ ├── Footer
│ │ ├── index.js
│ │ └── index.less
│ │ ├── header.js
│ │ ├── index.js
│ │ ├── index.less
│ │ ├── logo.js
│ │ └── startedModal.js
├── locales
│ ├── en-US.js
│ ├── en-US
│ │ ├── form.js
│ │ ├── gitDataV.js
│ │ ├── login.js
│ │ └── platform.js
│ ├── zh-CN.js
│ └── zh-CN
│ │ ├── form.js
│ │ ├── gitDataV.js
│ │ ├── login.js
│ │ └── platform.js
├── models
│ ├── .gitkeep
│ ├── global.js
│ └── menu.js
├── pages
│ ├── 403.js
│ ├── 404.js
│ ├── 500.js
│ ├── __tests__
│ │ ├── __mocks__
│ │ │ └── umi-plugin-locale.js
│ │ └── index.test.js
│ ├── document.ejs
│ ├── download
│ │ └── index.js
│ ├── exception
│ │ └── $code.js
│ ├── frame
│ │ ├── $key.js
│ │ ├── index.js
│ │ └── index.less
│ ├── index.js
│ ├── login
│ │ ├── components
│ │ │ └── Login
│ │ │ │ ├── index.js
│ │ │ │ ├── index.less
│ │ │ │ ├── loginQrcode.js
│ │ │ │ └── loginQrcode.less
│ │ ├── index.js
│ │ ├── index.less
│ │ ├── model.js
│ │ └── service.js
│ ├── register
│ │ ├── index.css
│ │ └── index.js
│ ├── resetPassword
│ │ ├── index.css
│ │ └── index.js
│ ├── sys
│ │ ├── echarts
│ │ │ ├── area.js
│ │ │ ├── bar.js
│ │ │ ├── components
│ │ │ │ ├── config.json
│ │ │ │ ├── form.js
│ │ │ │ ├── option.js
│ │ │ │ ├── option.less
│ │ │ │ └── panel.js
│ │ │ ├── funnel.js
│ │ │ ├── index.js
│ │ │ ├── line.js
│ │ │ ├── model.js
│ │ │ ├── pie.js
│ │ │ ├── pieDoughnut.js
│ │ │ ├── sankey.js
│ │ │ ├── services
│ │ │ │ ├── data01.json
│ │ │ │ ├── data02.json
│ │ │ │ └── data03.json
│ │ │ └── yBar.js
│ │ ├── githubpro
│ │ │ ├── $id
│ │ │ │ ├── index.css
│ │ │ │ └── index.js
│ │ │ ├── components
│ │ │ │ ├── account.js
│ │ │ │ ├── content.js
│ │ │ │ ├── dataTable.js
│ │ │ │ ├── footerTable.js
│ │ │ │ ├── getStarHistory.js
│ │ │ │ ├── header.js
│ │ │ │ └── table.less
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ ├── model.js
│ │ │ └── service.js
│ │ ├── index.js
│ │ ├── pathAnalysis
│ │ │ ├── components
│ │ │ │ ├── form.js
│ │ │ │ └── tabs.js
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ ├── model.js
│ │ │ └── service.js
│ │ ├── regionalAnalysis
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ ├── model.js
│ │ │ └── service.js
│ │ ├── sankeyPage
│ │ │ ├── index.js
│ │ │ └── model.js
│ │ ├── treePage
│ │ │ ├── index.js
│ │ │ ├── model.js
│ │ │ └── service.js
│ │ ├── users
│ │ │ ├── $id
│ │ │ │ ├── index.css
│ │ │ │ └── index.js
│ │ │ ├── components
│ │ │ │ └── Modal.js
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── model.js
│ │ │ └── service.js
│ │ └── view
│ │ │ ├── components
│ │ │ ├── DateDickers.js
│ │ │ ├── view.js
│ │ │ └── view1.js
│ │ │ ├── index.js
│ │ │ ├── model.js
│ │ │ ├── p1.js
│ │ │ ├── p2.js
│ │ │ └── service.js
│ └── versions
│ │ ├── components
│ │ ├── version.css
│ │ ├── version.js
│ │ └── version.less
│ │ ├── index.css
│ │ ├── index.js
│ │ └── index.less
├── services
│ ├── global.js
│ ├── index.js
│ └── menu.js
├── themes
│ ├── index.less
│ ├── mixin.less
│ └── vars.less
└── utils
│ ├── CryptoJS.js
│ ├── _.js
│ ├── index.js
│ ├── request.js
│ └── umiRequest.js
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | BROWSER=none
2 | ESLINT=1
3 | PORT=8088
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-umi",
3 | "rules": {},
4 | "parserOptions": {
5 | "ecmaFeatures": {
6 | "experimentalObjectRestSpread": true
7 | }
8 | },
9 | "globals": {
10 | }
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /npm-debug.log*
6 | /yarn-error.log
7 | /yarn.lock
8 | /package-lock.json
9 | # 测试页
10 | /src/pages/test.js
11 | # production
12 | /dist
13 |
14 | # misc
15 | .DS_Store
16 |
17 | # umi
18 | .umi
19 | .umi-production
20 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.html
5 | package.json
6 | .umi
7 | .umi-production
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 100,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}\\dev",
12 | "serverReadyAction": {
13 | "action": "openExternally"
14 | }
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/config/config.js:
--------------------------------------------------------------------------------
1 |
2 | // ref: https://umijs.org/config/
3 | import { resolve } from "path";
4 | import theme from "./theme.config"
5 |
6 |
7 | export default {
8 | base: '/',
9 | treeShaking: true,//用于描述移除 JavaScript 上下文中的未引用代码
10 | history: 'hash',//hash路由
11 | hash: true,//生成hash文件名
12 | // disableRedirectHoist: true,//禁用 redirect 上提。
13 | // devtool: 'source-map',//生成map文件
14 | targets: {//兼容浏览器版本
15 | // ie: 11,
16 | },
17 | // 配置模块不打入代码
18 | externals: {
19 | // echarts: 'window.echarts',
20 | d3: 'window.d3',
21 | },
22 | plugins: [
23 | // ref: https://umijs.org/plugin/umi-plugin-react.html
24 | ['umi-plugin-react', {
25 | antd: true,
26 | dva: true,
27 | dynamicImport: {
28 | webpackChunkName: true,
29 | loadingComponent: './components/PageLoading/index.js'
30 | },
31 | title: 'antd-umi-2.6',
32 | dll: true,
33 | locale: {
34 | enable: true,
35 | default: 'zh-CN',//'en-US',
36 | },
37 | routes: {
38 | exclude: [
39 | /models\//,
40 | /services\//,
41 | /model\.(t|j)sx?$/,
42 | /service\.(t|j)sx?$/,
43 | /components\//,
44 | ],
45 | },
46 | // cdn
47 | scripts: [
48 | // { src: 'https://cdn.bootcss.com/echarts/4.2.1/echarts.min.js' },
49 |
50 | { src: 'https://cdn.bootcss.com/d3/5.9.2/d3.min.js' },
51 | ],
52 | }],
53 | ],
54 | alias: {
55 | "@": resolve(__dirname, "../src"),
56 | '@utils': resolve(__dirname, "../src/utils"),
57 | '@context': resolve(__dirname, "../src/layouts/Context"),
58 | // 组件库
59 | '@components': resolve(__dirname, "../src/components"),
60 | // 系统配置
61 | '@platformConfig': resolve(__dirname, "./platform.config"),
62 | // 全局services
63 | '@services': resolve(__dirname, "../src/services"),
64 | // 全局models
65 | '@models': resolve(__dirname, "../src/models"),
66 | //菜单配置项
67 | "@menuConfig": resolve(__dirname, "./menu.config.js"),
68 | // 版本日志管理
69 | "@versionsConfig": resolve(__dirname, './versions.config.json'),
70 | // request请求
71 | "@http": resolve(__dirname, '../src/utils/request.js')
72 | },
73 | theme,
74 | proxy: {
75 | "/api": {
76 | target: "",
77 | changeOrigin: true,
78 | pathRewrite: { "^/api": "" }
79 | }
80 | },
81 | }
82 |
--------------------------------------------------------------------------------
/config/platform.config.js:
--------------------------------------------------------------------------------
1 |
2 | // 菜单权限:true(开启) false(关闭)
3 | // let GLOBAL_MENU_PERMISSION;
4 | // if (process && process.env.NODE_ENV === 'development') {
5 | // GLOBAL_MENU_PERMISSION = false;
6 | // } else {
7 | // GLOBAL_MENU_PERMISSION = true;
8 | // }
9 |
10 | module.exports = {
11 | // 数据请求api
12 | apiPrefix: document.head.dataset.api || '',
13 | iframePrefix: document.head.dataset.iframe || '',
14 | loginLogo: 'logo_blue_1024.png',
15 | sysLogo: 'logo.png',
16 | // 登录页名称
17 | loginName: '数据平台',
18 | // 系统名称
19 | sysName: 'TEST-Pro',
20 | // 版权
21 | copyright: "2019 mpw0311@163.com.",
22 | // 是否开启菜单权限校验
23 | menuPermission: true,
24 | // table默认一页条数
25 | pageSize: 10,
26 | // iconFont 地址
27 | iconUrl: '//at.alicdn.com/t/font_1030595_depmdbpf3yc.js',
28 | // 系统默认首页
29 | sysDefultPage: {
30 | pathname: '/sys/githubpro',
31 | state: {
32 | key: 'gitDataV',
33 | pathtitles: [{ title: 'gitDataV', icon: 'github' }],
34 | }
35 | },
36 | };
--------------------------------------------------------------------------------
/config/theme.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "@font-family": "Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', SimSun, sans-serif",
3 | // "@border-color-split": 'hsv(0, 0, 89%)',// split border inside a component
4 | "@border-radius-base": "5px",
5 | "@header-height": "64px",
6 | "@font-size-base": "12px",
7 | "@content-color": "rgb(240, 242, 245)",
8 | "@shadow-1": "4px 4px 20px 0 rgba(0, 0, 0, 0.01)",
9 | "@shadow-2": "4px 4px 40px 0 rgba(0, 0, 0, 0.05)",
10 | "@line-height-base": "1.2",
11 | // "@menu-dark-bg": "#333"
12 | };
13 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "emitDecoratorMetadata": true,
4 | "experimentalDecorators": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/mock/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/mock/.gitkeep
--------------------------------------------------------------------------------
/mock/CryptoJS.js:
--------------------------------------------------------------------------------
1 | import { AES, enc, mode, pad } from 'crypto-js';
2 | /**
3 | * 初始化秘钥
4 | * @param {*} key |密钥 16 位
5 | * @param {*} iv |初始向量 initial vector 16 位,key 和 iv 可以一致
6 | */
7 | const _init = (key = 'ANTD-#6$8Y-oi5&K', iv = key) => {
8 | return {
9 | key: enc.Utf8.parse(key),
10 | iv: enc.Utf8.parse(iv)
11 | };
12 | };
13 |
14 | /**
15 | * 加密
16 | * @param {string} str
17 | */
18 | export const encrypt = (str, aes_key) => {
19 | const { key, iv } = _init(aes_key);
20 | const encrypted = AES.encrypt(str, key, {
21 | iv,
22 | mode: mode.CBC,
23 | padding: pad.Pkcs7
24 | });
25 | // 转换为字符串
26 | return encrypted.toString();
27 | };
28 | /**
29 | * 解密
30 | * @param {string} encrypted
31 | */
32 | export const decrypt = (encrypted, aes_key) => {
33 | const { key, iv } = _init(aes_key);
34 | const decrypted = AES.decrypt(encrypted, key, {
35 | iv,
36 | mode: mode.CBC,
37 | padding: pad.Pkcs7
38 | });
39 |
40 | // 转换为 utf8 字符串
41 | return enc.Utf8.stringify(decrypted);
42 | };
--------------------------------------------------------------------------------
/mock/global.js:
--------------------------------------------------------------------------------
1 | const Mock = require('mockjs');
2 |
3 | const { Random } = Mock;
4 | const info = Mock.mock({
5 | data: {
6 | userInfo: {
7 | userName: Random.name(),
8 | },
9 | message: {
10 | news: Random.cparagraph(17, 30),
11 | 'count|18-32': 1
12 | },
13 | },
14 | status: 0
15 | });
16 | module.exports = {
17 | [`POST /getSysInfo`](req, res) {
18 | res.status(200).json(info);
19 | },
20 | [`GET /logout`](req, res) {
21 | res.status(200).json({
22 | data: {
23 | message: "退出登录成功!"
24 | },
25 | status: 0
26 | });
27 | },
28 | [`POST /getMessage`](req, res) {
29 | res.status(200).json({
30 | data: [
31 | {
32 | title: 'Ant Design Title 1',
33 | description: "Ant Design, a design language for background applications, is refined by Ant UED Team",
34 | type: 'read'
35 | },
36 | {
37 | title: 'Ant Design Title 2',
38 | description: "Ant Design, a design language for background applications, is refined by Ant UED Team",
39 | type: 'unread'
40 | },
41 | {
42 | title: 'Ant Design Title 3',
43 | description: "Ant Design, a design language for background applications, is refined by Ant UED Team",
44 | type: 'unread'
45 | },
46 | {
47 | title: 'Ant Design Title 4',
48 | description: "Ant Design, a design language for background applications, is refined by Ant UED Team",
49 | type: 'unread'
50 | },
51 | ],
52 | status: 0
53 | });
54 | }
55 | };
--------------------------------------------------------------------------------
/mock/login.js:
--------------------------------------------------------------------------------
1 | const Mock = require('mockjs');//eslint-disable-line
2 | const { decrypt } = require('./CryptoJS');
3 | console.log(decrypt);
4 | module.exports = {
5 | [`POST /login`](req, res) {
6 | const { body } = req;
7 | const { password, username } = body;
8 | const pwd = decrypt(password)
9 | if (pwd === 'admin123' && username === 'admin') {
10 | res.status(200).json({
11 | data: {
12 | alertDesc: '登录成功!'
13 | },
14 | status: 0
15 | });
16 | } else {
17 | res.status(200).json({
18 | data: {
19 | alertDesc: '账号或密码错误!'
20 | },
21 | status: -1
22 | });
23 | }
24 | },
25 | };
--------------------------------------------------------------------------------
/mock/menu.js:
--------------------------------------------------------------------------------
1 | const Mock = require('mockjs');
2 | const menuData = [
3 | {
4 | title: "gitDataV",
5 | key: "gitDataV",
6 | },
7 | {
8 | title: "地域分析",
9 | key: "regionalAnalysis",
10 | },
11 | {
12 | title: "users",
13 | key: "users",
14 | },
15 | {
16 | title: "404",
17 | key: "404",
18 | },
19 | {
20 | title: "用户行为",
21 | key: "yonghuxingwei",
22 | children: [
23 | {
24 | title: "路径分析",
25 | key: "pathAnalysis",
26 | },
27 | {
28 | title: "view1",
29 | key: "p1",
30 | },
31 | {
32 | title: "view2",
33 | key: "p2",
34 | },
35 | ]
36 | },
37 | {
38 | title: "echarts组件",
39 | key: "echarts",
40 | children: [
41 | {
42 | key: 'Bar',
43 | title: 'Bar'
44 | },
45 | {
46 | key: 'line',
47 | title: 'Line'
48 | },
49 | {
50 | key: 'area',
51 | title: 'Area'
52 | },
53 | {
54 | key: 'yBar',
55 | title: 'YBar'
56 | },
57 | {
58 | key: 'funnel',
59 | title: 'Funnel'
60 | },
61 | {
62 | key: 'pie',
63 | title: 'Pie'
64 | },
65 | {
66 | key: 'pieDoughnut',
67 | title: 'PieDoughnut'
68 | },
69 | {
70 | key: 'sankey',
71 | title: 'Sankey'
72 | },
73 | ]
74 | },
75 | {
76 | title: "d3.js组件",
77 | key: "d3Chart",
78 | children: [
79 | {
80 | title: "树图",
81 | key: "treePage",
82 | },
83 | ]
84 | },
85 | {
86 | title: "iframe",
87 | key: "iframe",
88 | children: [
89 | {
90 | title: "bing",
91 | key: "bing",
92 | }
93 | ]
94 | },
95 | {
96 | title: "水印",
97 | key: "watermark",
98 | },
99 | {
100 | title: "图形组件",
101 | key: "react-charts",
102 | },
103 | {
104 | title: "请给star",
105 | key: "github",
106 | },
107 | ];
108 | const data = Mock.mock({
109 | data: menuData,
110 | status: 0
111 | });
112 | module.exports = {
113 | [`POST /getMenuData`](req, res) {
114 | res.status(200).json(data);
115 | },
116 | };
--------------------------------------------------------------------------------
/mock/tree.js:
--------------------------------------------------------------------------------
1 | import { mock } from 'mockjs';
2 | const getdata = () => {
3 | return mock({
4 | 'time': '220ms',
5 | 'children|4-6': [
6 | {
7 | 'name': '@name',
8 | 'value|500-1000': 600,
9 | 'time|1-200': 10,
10 | },
11 | ],
12 | });
13 | };
14 |
15 | module.exports = {
16 | [`POST /api/treePage`](req, res) {
17 | res.status(200).json({
18 | data: {
19 | ...getdata()
20 | },
21 | status: 0
22 | });
23 | }
24 | };
--------------------------------------------------------------------------------
/mock/users.js:
--------------------------------------------------------------------------------
1 | const Mock = require('mockjs');
2 |
3 | const { Random } = Mock;
4 | let db = Mock.mock({
5 | 'data|3-6': [{
6 | id: '@id',
7 | email: '@email',
8 | name: '@name',
9 | website: Random.cparagraph(1),
10 | operation: '@operation',
11 | 'age|18-32': 1
12 | }],
13 | status: 0
14 | });
15 |
16 | module.exports = {
17 | [`GET /api/users`](req, res) {
18 | res.setHeader('Access-Control-Allow-Origin', '*');
19 | res.status(200).json(db);
20 |
21 | },
22 |
23 | [`POST /api/users`](req, res) {
24 | res.setHeader('Access-Control-Allow-Origin', '*');
25 | let user = req.body;
26 | if (typeof user === 'string') {
27 | user = JSON.parse(user)
28 | }
29 | user.id = Mock.mock('@id');
30 | db.data.push(user);
31 | res.status(200).json(db);
32 | },
33 |
34 | [`PATCH /api/users/:id`](req, res) {
35 | res.setHeader('Access-Control-Allow-Origin', '*');
36 | const { params: { id }, query, body } = req;
37 | console.log(id, query);
38 | // db.data = db.data.map(item => item.id === id ? user : item);
39 | res.status(200).json(db);
40 | },
41 |
42 | [`DELETE /api/users/:id`](req, res) {
43 | res.setHeader('Access-Control-Allow-Origin', '*');
44 | const { params: { id } } = req;
45 | db.data = db.data.filter(item => item.id !== id);
46 | res.status(200).json(db);
47 | }
48 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "start": "umi dev",
5 | "build": "umi build",
6 | "test": "umi test",
7 | "lint": "eslint --ext .js src mock tests",
8 | "precommit": "lint-staged",
9 | "add-page": "umi block add https://github.com/mpw0311/umi-blocks/tree/master/newPage --path=/sys/newPage"
10 | },
11 | "dependencies": {
12 | "antd": "^3.15.0",
13 | "axios": "^0.19.0",
14 | "crypto-js": "^3.1.9-1",
15 | "d3": "^5.9.2",
16 | "d3-sankey": "^0.12.1",
17 | "dva": "^2.5.0-beta.2",
18 | "echarts": "^4.2.1",
19 | "echarts-for-react": "^2.0.15-beta.0",
20 | "memoize-one": "^5.0.2",
21 | "path-to-regexp": "^3.0.0",
22 | "react": "^16.7.0",
23 | "react-container-query": "^0.11.0",
24 | "react-dom": "^16.7.0",
25 | "react-media": "^1.9.2",
26 | "umi-request": "^1.0.7"
27 | },
28 | "devDependencies": {
29 | "babel-eslint": "^9.0.0",
30 | "eslint": "^5.4.0",
31 | "eslint-config-umi": "^1.4.0",
32 | "eslint-plugin-flowtype": "^2.50.0",
33 | "eslint-plugin-import": "^2.14.0",
34 | "eslint-plugin-jsx-a11y": "^5.1.1",
35 | "eslint-plugin-react": "^7.11.1",
36 | "husky": "^0.14.3",
37 | "lint-staged": "^7.2.2",
38 | "mockjs": "^1.0.1-beta3",
39 | "react-test-renderer": "^16.7.0",
40 | "umi": "^2.6.3",
41 | "umi-plugin-react": "^1.6.0"
42 | },
43 | "lint-staged": {
44 | "*.{js,jsx}": [
45 | "eslint --fix",
46 | "git add"
47 | ]
48 | },
49 | "engines": {
50 | "node": ">=8.0.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/public/favicon.ico
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 |
2 | export const dva = {
3 | config: {
4 | onError(err) {
5 | err.preventDefault();
6 | console.error(err.message);
7 | },
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/src/assets/d3tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/d3tree.png
--------------------------------------------------------------------------------
/src/assets/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/demo.png
--------------------------------------------------------------------------------
/src/assets/demo_login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/demo_login.png
--------------------------------------------------------------------------------
/src/assets/frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/frame.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logo_blue_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/logo_blue_1024.png
--------------------------------------------------------------------------------
/src/assets/qrcode_for_wechat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/qrcode_for_wechat.jpg
--------------------------------------------------------------------------------
/src/assets/regionalAnalysis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/regionalAnalysis.png
--------------------------------------------------------------------------------
/src/assets/vew1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/vew1.png
--------------------------------------------------------------------------------
/src/assets/view3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/assets/view3.png
--------------------------------------------------------------------------------
/src/components/Announcement/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Alert } from 'antd';
8 |
9 | function Announcement(props) {
10 | const {
11 | handleClose = () => { },
12 | title = "公告",
13 | description = ""
14 | } = props;
15 | const onClose = (e) => {
16 | handleClose(e);
17 | };
18 | if (description !== "") {
19 | return (
20 |
27 | );
28 | } else {
29 | return ();
30 | }
31 | }
32 | export default Announcement;
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 面包屑
6 | */
7 | import React from 'react';
8 | import { Breadcrumb } from 'antd';
9 | import _ from 'lodash';
10 | import styles from './index.less';
11 |
12 | function MyBreadcrumb(props) {
13 | const { pathtitles, location = {} } = props;
14 | const { state: pathstate } = location;
15 | const { pathtitles: stateTitles } = pathstate || {};
16 | const renderItem = (rows) => {
17 | if (_.isArray(rows)) {
18 | return rows.map((elem, key) => {elem});
19 | }
20 | };
21 | const getTitles = (stateTitles, pathtitles) => {
22 | if (pathtitles && pathtitles.length > 0 && stateTitles && stateTitles.length > 0) {
23 | return _.uniq([...pathtitles, ...stateTitles]);
24 | } else if (pathtitles && pathtitles.length > 0) {
25 | return pathtitles;
26 | } else if (stateTitles && stateTitles.length > 0) {
27 | return stateTitles;
28 | }
29 | };
30 | const titles = getTitles(pathtitles, stateTitles);
31 | if (titles && titles.length > 0) {
32 | return (
33 |
34 | {renderItem(titles)}
35 |
36 | );
37 | } else {
38 | return false;
39 | }
40 | }
41 |
42 | export default MyBreadcrumb;
43 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.less:
--------------------------------------------------------------------------------
1 | .content {
2 | background-color: @content-color;
3 | margin: 0;
4 | font-size: 14px;
5 | height: 28px;
6 | line-height: 25px;
7 | padding: 0 5px;
8 | }
--------------------------------------------------------------------------------
/src/components/CanvasChart/WaterWave/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .waterWave {
4 | position: relative;
5 | display: inline-block;
6 | transform-origin: left;
7 |
8 | .text {
9 | position: absolute;
10 | top: 32px;
11 | left: 0;
12 | width: 100%;
13 | text-align: center;
14 |
15 | span {
16 | color: @text-color-secondary;
17 | font-size: 14px;
18 | line-height: 22px;
19 | }
20 |
21 | h4 {
22 | color: @heading-color;
23 | font-size: 24px;
24 | line-height: 32px;
25 | }
26 | }
27 |
28 | .waterWaveCanvasWrapper {
29 | transform: scale(0.5);
30 | transform-origin: 0 0;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/components/CanvasChart/autoHeight.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 |
3 | function computeHeight(node) {
4 | const totalHeight = parseInt(getComputedStyle(node).height, 10);
5 | const padding =
6 | parseInt(getComputedStyle(node).paddingTop, 10) +
7 | parseInt(getComputedStyle(node).paddingBottom, 10);
8 | return totalHeight - padding;
9 | }
10 |
11 | function getAutoHeight(n) {
12 | if (!n) {
13 | return 0;
14 | }
15 | let node = n;
16 |
17 | let height = computeHeight(node);
18 |
19 | while (!height) {
20 | node = node.parentNode;
21 | if (node) {
22 | height = computeHeight(node);
23 | } else {
24 | break;
25 | }
26 | }
27 | return height;
28 | }
29 |
30 | const autoHeight = () => WrappedComponent =>
31 | class extends Component {
32 | state = {
33 | computedHeight: 0,
34 | }
35 | componentDidMount() {
36 | const { height } = this.props;
37 | if (!height) {
38 | const h = getAutoHeight(this.root);
39 | this.setState({ computedHeight: h });
40 | }
41 | }
42 | handleRoot = node => {
43 | this.root = node;
44 | }
45 | render() {
46 | const { height } = this.props;
47 | const { computedHeight } = this.state;
48 | const h = height || computedHeight;
49 | return (
50 |
{h > 0 && }
51 | );
52 | }
53 | };
54 | export default autoHeight;
--------------------------------------------------------------------------------
/src/components/Consumer/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Context from '@context';
9 | export default (WrappedComponent) => {
10 | return class extends PureComponent {
11 | render() {
12 | return (
13 |
14 | {({ theme, location }) => (
15 | < WrappedComponent theme={theme} location={location} {...this.props} />
16 | )}
17 |
18 | );
19 | }
20 | };
21 | };
--------------------------------------------------------------------------------
/src/components/D3Chart/Tree/_.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/components/D3Chart/Tree/_.js
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/.test.js:
--------------------------------------------------------------------------------
1 | import Basic from './index';
2 | import renderer from 'react-test-renderer';
3 | import { mount } from 'enzyme';
4 | describe('Basic', () => {
5 | const data = {
6 | columns: [
7 | {
8 | field: "product",
9 | name: "分类",
10 | type: "string"
11 | },
12 | {
13 | field: "2015",
14 | name: "2015",
15 | type: "number"
16 | },
17 | {
18 | field: "2016",
19 | name: "2016",
20 | type: "number"
21 | },
22 | {
23 | field: "2017",
24 | name: "2017",
25 | type: "number"
26 | }
27 | ],
28 | rows: [
29 | {
30 | product: 'Matcha Latte',
31 | 2015: 43.3,
32 | 2016: 85.8,
33 | 2017: 93.7
34 | },
35 | {
36 | product: 'Milk Tea',
37 | 2015: 83.1,
38 | 2016: 73.4,
39 | 2017: 55.1
40 | },
41 | {
42 | product: 'Cheese Cocoa',
43 | 2015: 86.4,
44 | 2016: 65.2,
45 | 2017: 82.5
46 | },
47 | {
48 | product: 'Walnut Brownie',
49 | 2015: 72.4,
50 | 2016: 53.9,
51 | 2017: 39.1
52 | },
53 | ]
54 | };
55 | test('Render A_basic', () => {
56 | const component = mount();
57 | expect(component.exists()).toBe(true);
58 | expect(component.find('.echarts-for-react').length).toBe(1);
59 | expect(component.getDOMNode().style.height).toBe('300px');
60 | expect(component.props().loading).toBe(false);
61 | expect(component.props().type.toLowerCase()).toBe('line');
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/dataZoom.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | * @git
7 | */
8 | export default (props) => {
9 | const { dataZoom, showDataZoom } = props;
10 | if (showDataZoom) {
11 | return dataZoom || [
12 | {
13 | show: true,
14 | realtime: true,
15 | start: 0,
16 | end: 100
17 | },
18 | {
19 | type: 'inside',
20 | realtime: true,
21 | start: 0,
22 | end: 100
23 | }
24 | ];
25 | }
26 | };
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/dataset.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const { dataSource } = props;
9 | return {
10 | source: dataSource
11 | };
12 | };
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/series.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.1
5 | * @description
6 | */
7 | import getType from './type';
8 | import setY2Series from './setY2Series';
9 | export default (props) => {
10 | const {
11 | series,
12 | dataSource,
13 | type,
14 | seriesLayoutBy,
15 | seriesSettings,
16 | showY2,
17 | stack,
18 | showLabel,
19 | labelPosition,
20 | maxPoint,
21 | minPoint,
22 | averageLine
23 | } = props;
24 | const setting = { ...seriesSettings };
25 | if (maxPoint === true) {
26 | const { markPoint = {} } = setting;
27 | const { data = [] } = markPoint;
28 | markPoint.data = [...data, { type: 'max', name: '最大值' }];
29 | setting.markPoint = markPoint;
30 | }
31 | if (minPoint === true) {
32 | const { markPoint = {} } = setting;
33 | const { data = [] } = markPoint;
34 | markPoint.data = [...data, { type: 'min', name: '最小值' }];
35 | setting.markPoint = markPoint;
36 | }
37 | if (averageLine === true) {
38 | const { markLine = {} } = setting;
39 | const { data = [] } = markLine;
40 | markLine.data = [...data, { type: 'min', name: '最小值' }];
41 | setting.markLine = markLine;
42 | }
43 | const _getSeries = () => {
44 | const _series = [];
45 | const len = seriesLayoutBy === 'row' ? dataSource.length - 1 : dataSource[0].length - 1;
46 | for (let i = 0; i < len; i++) {
47 | _series.push({
48 | type: getType(type),
49 | stack: stack === true ? '总量' : null,
50 | ...setting,
51 | seriesLayoutBy,
52 | label: {
53 | normal: {
54 | show: showLabel,
55 | position: labelPosition
56 | }
57 | }
58 | });
59 | }
60 | return _series;
61 | };
62 | const y1Series = series || _getSeries();
63 | if (showY2 === true) {
64 | return setY2Series({ ...props, series: y1Series });
65 | }
66 | return y1Series;
67 | };
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/setY2Series.js:
--------------------------------------------------------------------------------
1 | export default (props) => {
2 | const {
3 | series,
4 | dataSource,
5 | seriesLayoutBy,
6 | showY2,
7 | Y2SeriesIndex,
8 | Y2SeriesName,
9 | Y2Series,
10 | Y2Type,
11 | } = props;
12 |
13 | //通过series的name属性查找对应的index
14 | const findIndexByName = (dataSource, name, type) => {
15 | if (seriesLayoutBy === 'row') {
16 | const index = dataSource.findIndex(curr => curr[0] === name) - 1;
17 | if (index < 0) {
18 | console.warn(`echarts-series:未找到要匹配的Y2值(${name})`);
19 | }
20 | return {
21 | name,
22 | type,
23 | index: index
24 | };
25 | } else {
26 | const index = dataSource[0].indexOf(name) - 1;
27 | if (index < 0) {
28 | console.warn(`echarts-series:未找到要匹配的Y2值(${name})`);
29 | }
30 | return {
31 | name,
32 | type,
33 | index: index
34 | };
35 | }
36 | };
37 | /**
38 | * 生成对应的y2轴对应index索引
39 | * index第一个索引从0开始
40 | * Y2Series:[
41 | * {
42 | * type: 'bar',
43 | * index: 2,
44 | * },
45 | * {
46 | * type: 'bar',
47 | * name: 'Cheese Cocoa'
48 | * }
49 | * ]
50 | */
51 | const GetY2Series = (dataSource, Y2Series) => {
52 | return Y2Series.map(item => {
53 | if (!(dataSource && dataSource[0] && item.index === undefined)) return item;
54 | return findIndexByName(dataSource, item.name, item.type);
55 | })
56 | .filter(item => item.index > -1);
57 | };
58 | //设置y2轴series
59 | const setY2Series = (_Y2Series) => {
60 | _Y2Series.forEach(item => {
61 | const { type, index } = item;
62 | series[index] = { ...series[index], type, yAxisIndex: 1 };
63 | });
64 | };
65 | if (showY2 === true) {
66 | if (Y2Series) {
67 | const _Y2Series = GetY2Series(dataSource, Y2Series);
68 | setY2Series(_Y2Series);
69 | } else if (Y2SeriesName && Array.isArray(Y2SeriesName)) {
70 | const _Y2SeriesName = Y2SeriesName.map(item => findIndexByName(dataSource, item, Y2Type));
71 | setY2Series(_Y2SeriesName);
72 |
73 | } else if (Y2SeriesIndex && Array.isArray(Y2SeriesIndex)) {
74 | const _Y2SeriesIndex = Y2SeriesIndex.map(index => ({
75 | index,
76 | type: Y2Type,
77 | }));
78 | setY2Series(_Y2SeriesIndex);
79 | }
80 | }
81 | return series;
82 | };
83 |
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/tooltip.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const { tooltip, showTooltip, axisPointer } = props;
9 | const cross = {
10 | type: 'cross',
11 | label: {
12 | backgroundColor: '#6a7985'
13 | }
14 | };
15 | const shadow = {
16 | type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
17 | };
18 | return {
19 | show:showTooltip,
20 | trigger: 'axis',
21 | axisPointer: axisPointer === 'cross' ? cross : axisPointer === 'shadow' ? shadow : undefined,
22 | ...tooltip
23 | };
24 | };
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/type.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (type) => {
8 | const _type = type.toLowerCase();
9 | switch (_type) {
10 | case 'area':
11 | return 'line';
12 | case 'bar-y':
13 | return 'bar';
14 | default:
15 | return type;
16 | }
17 | };
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/xAxis.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const { xAxis, xAxisRotate, interval } = props;
9 | return {
10 | type: 'category',
11 | axisLabel: {
12 | interval,
13 | rotate: xAxisRotate,
14 | // formatter: function (value, index) {
15 | // return value.length > 10 ? value.slice(0, 10) + '…' : value;
16 | // }
17 | },
18 | ...xAxis
19 | };
20 | };
--------------------------------------------------------------------------------
/src/components/ECharts/A_basic/yAxis.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const { yAxis, YName, YUnit, showY2, Y2Name, Y2Unit, showY2SplitLine } = props;
9 | const YFormatter = YUnit ? { formatter: `{value}${YUnit}` } : {};
10 | const Y2Formatter = Y2Unit ? { formatter: `{value}${Y2Unit}` } : {};
11 | if (showY2) {
12 | return [
13 | {
14 | name: YName,
15 | axisLabel: {
16 | ...YFormatter
17 | },
18 | },
19 | {
20 | name: Y2Name,
21 | axisLabel: {
22 | ...Y2Formatter
23 | },
24 | splitLine: {
25 | show: showY2SplitLine,
26 | lineStyle: {
27 | type: 'dashed',
28 | color: '#ddd'
29 | }
30 | }
31 | }
32 |
33 | ];
34 | } else {
35 | return {
36 | name: YName,
37 | axisLabel: {
38 | ...YFormatter
39 | },
40 | ...yAxis,
41 | };
42 | }
43 | };
--------------------------------------------------------------------------------
/src/components/ECharts/B_basic/.test.js:
--------------------------------------------------------------------------------
1 | import Basic from './index';
2 | import renderer from 'react-test-renderer';
3 | import { mount } from 'enzyme';
4 | describe('Basic', () => {
5 | const data = {
6 | columns: [
7 | {
8 | field: "product",
9 | name: "分类",
10 | type: "string"
11 | },
12 | {
13 | field: "2015",
14 | name: "2015",
15 | type: "number"
16 | },
17 | {
18 | field: "2016",
19 | name: "2016",
20 | type: "number"
21 | },
22 | {
23 | field: "2017",
24 | name: "2017",
25 | type: "number"
26 | }
27 | ],
28 | rows: [
29 | {
30 | product: 'Matcha Latte',
31 | 2015: 43.3,
32 | 2016: 85.8,
33 | 2017: 93.7
34 | },
35 | {
36 | product: 'Milk Tea',
37 | 2015: 83.1,
38 | 2016: 73.4,
39 | 2017: 55.1
40 | },
41 | {
42 | product: 'Cheese Cocoa',
43 | 2015: 86.4,
44 | 2016: 65.2,
45 | 2017: 82.5
46 | },
47 | {
48 | product: 'Walnut Brownie',
49 | 2015: 72.4,
50 | 2016: 53.9,
51 | 2017: 39.1
52 | },
53 | ]
54 | };
55 | test('Render B_basic', () => {
56 | const setttings = {
57 | radius: '55%',
58 | center: ['40%', '50%'],
59 | };
60 | const component = mount();
61 | expect(component.exists()).toBe(true);
62 | expect(component.find('.echarts-for-react').length).toBe(1);
63 | expect(component.getDOMNode().style.height).toBe('300px');
64 | expect(component.props().loading).toBe(false);
65 | expect(component.props().type.toLowerCase()).toBe('pie');
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/src/components/ECharts/B_basic/dataset.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { _toDataset } from '../methods';
8 | export default (props) => {
9 | const { data, dataType } = props;
10 | if (dataType === 'dataset') {
11 | const source = _toDataset(data);
12 | return {
13 | source
14 | };
15 | } else {
16 | return null;
17 | }
18 | };
--------------------------------------------------------------------------------
/src/components/ECharts/B_basic/index.propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import titlePropTypes from '../components/title.propTypes';
3 | import legendPropTypes from '../components/legend.propTypes';
4 | import toolboxPropTypes from '../components/toolbox.propTypes';
5 |
6 | export default {
7 | ...titlePropTypes,
8 | ...legendPropTypes,
9 | ...toolboxPropTypes,
10 | //调色盘颜色列表
11 | color: PropTypes.array,
12 | //支持的图形类型
13 | type: PropTypes.oneOf(['funnel', 'pie', 'sankey']),
14 | //数据格式校验
15 | data: PropTypes.shape({
16 | columns: PropTypes.array,
17 | rows: PropTypes.array,
18 | }),
19 | dataType: PropTypes.oneOf(['dataset', 'tabel', 'special']),
20 | //echart组件div样式
21 | style: PropTypes.object,
22 | //是否显示正在加载中
23 | loading: PropTypes.bool,
24 | //可以传入tooltip配置,校验
25 | tooltip: PropTypes.object,
26 | //是否显示tootip
27 | showTooltip: PropTypes.bool,
28 | //图形系列(series)配置项
29 | series: PropTypes.oneOfType([
30 | PropTypes.array,
31 | PropTypes.object,
32 | ]),
33 | //单个图形系列(series[i])配置项
34 | seriesSettings: PropTypes.object,
35 | //漏斗图数据排序
36 | sort: PropTypes.oneOf(['ascending', 'descending', 'none']),
37 | //图形系列(series)name
38 | seriesName: PropTypes.string,
39 | //when the chart is ready, will callback the function with the echarts object as it's paramter.
40 | onChartReady: PropTypes.func,
41 | //binding the echarts event, will callback with the echarts event object, and the echart object as it's paramters
42 | onEvents: PropTypes.object,
43 | };
--------------------------------------------------------------------------------
/src/components/ECharts/B_basic/series.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import getType from './type';
8 | export default (props) => {
9 | const { series, seriesSettings, data: { rows }, type, sort, seriesName } = props;
10 | const _geySeries = () => {
11 | return rows.map((item, i) => {
12 | if (i === 0) {
13 | return undefined;
14 | } else {
15 | return {
16 | name: seriesName,
17 | type: getType(type),
18 | sort,
19 | ...seriesSettings,
20 | };
21 | }
22 | })
23 | .filter(item => item !== undefined);
24 | };
25 |
26 | return series || _geySeries();
27 | };
--------------------------------------------------------------------------------
/src/components/ECharts/B_basic/tooltip.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const { tooltip, showTooltip } = props;
9 | return {
10 | show: showTooltip,
11 | ...tooltip
12 | };
13 | };
--------------------------------------------------------------------------------
/src/components/ECharts/B_basic/type.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (type) => {
8 | const _type = type.toLowerCase();
9 | switch (_type) {
10 | case 'funnel':
11 | return 'funnel';
12 | default:
13 | return type;
14 | }
15 | };
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Area/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../A_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'line',
14 | loading: false,
15 | stack: true,
16 | seriesSettings: {
17 | areaStyle: {},
18 | },
19 | }
20 | render() {
21 |
22 | return (
23 |
26 | );
27 | }
28 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Bar/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../A_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'bar',
14 | loading: false,
15 | }
16 | render() {
17 |
18 | return (
19 |
22 | );
23 | }
24 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Funnel/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../B_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'funnel',
14 | loading: false,
15 | showLegend: false,
16 | tooltip: {},
17 | sort: 'descending',
18 | seriesSettings: {
19 | left: '10%',
20 | top: 10,
21 | bottom: 10,
22 | width: '80%',
23 | // min: 0,
24 | // max: 100,
25 | // minSize: '0%',
26 | // maxSize: '100%',
27 | gap: 2,
28 | label: {
29 | show: true,
30 | position: 'inside'
31 | },
32 | labelLine: {
33 | length: 10,
34 | lineStyle: {
35 | width: 1,
36 | type: 'solid'
37 | }
38 | },
39 | itemStyle: {
40 | borderColor: '#fff',
41 | borderWidth: 1
42 | },
43 | emphasis: {
44 | label: {
45 | show: true,
46 | fontSize: 20
47 | }
48 | },
49 | }
50 | }
51 | render() {
52 |
53 | return (
54 |
57 | );
58 | }
59 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Line/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../A_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'line',
14 | loading: false,
15 | }
16 | render() {
17 |
18 | return (
19 |
22 | );
23 | }
24 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Pie-doughnut/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../B_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'pie',
14 | loading: false,
15 | showLegend: false,
16 | tooltip: {},
17 | seriesSettings: {
18 | radius: ['50%', '70%'],
19 | center: ['40%', '50%'],
20 | }
21 | }
22 | render() {
23 |
24 | return (
25 |
28 | );
29 | }
30 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Pie/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../B_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'pie',
14 | loading: false,
15 | showLegend: false,
16 | tooltip: {},
17 | seriesSettings: {
18 | radius: '55%',
19 | center: ['40%', '50%'],
20 | }
21 | }
22 | render() {
23 |
24 | return (
25 |
28 | );
29 | }
30 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/Radar/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../B_basic';
9 | class Index extends PureComponent {
10 | static defaultProps = {
11 | data: {},
12 | type: 'radar',
13 | loading: false,
14 | showLegend: false,
15 | tooltip: {},
16 | radar: {
17 | // shape: 'circle',
18 | name: {
19 | textStyle: {
20 | color: '#fff',
21 | backgroundColor: '#999',
22 | borderRadius: 3,
23 | padding: [3, 5]
24 | }
25 | },
26 | },
27 | indicator: []
28 | }
29 | render() {
30 | const { radar, indicator } = this.props;
31 | return (
32 |
36 | );
37 | }
38 | }
39 | export default Index;
--------------------------------------------------------------------------------
/src/components/ECharts/charts/bar-y/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../A_basic';
9 |
10 | export default class extends PureComponent {
11 | static defaultProps = {
12 | data: {},
13 | type: 'bar',
14 | loading: false,
15 | xAxis: {
16 | type: 'value',
17 | },
18 | yAxis: {
19 | type: 'category',
20 | },
21 | }
22 | render() {
23 | return (
24 |
27 | );
28 | }
29 | }
--------------------------------------------------------------------------------
/src/components/ECharts/charts/candlestick/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { PureComponent } from 'react';
8 | import Chart from '../../core';
9 | import getTitle from '../../components/title';
10 | import getToolbox from '../../components/toolbox';
11 | import getGrid from '../../components/grid';
12 |
13 | export default class extends PureComponent {
14 | static defaultProps = {
15 | data: {},
16 | type: 'k',
17 | loading: false,
18 | }
19 | render() {
20 | const { data, height, style, loading, onChartReady, onEvents, YName, yAxis, xAxis } = this.props;
21 | const dataSource = Array.isArray(data) ? data.slice(1) : [];
22 | const xAxisData = Array.isArray(dataSource) ? dataSource.map(item => item[0]) : [];
23 | const seriesData = Array.isArray(data) ? dataSource.map(item => item.slice(1)) : [];
24 | const option = {
25 | title: getTitle(this.props),
26 | toolbox: getToolbox(this.props),
27 | xAxis: {
28 | data: xAxisData,
29 | ...xAxis
30 | },
31 | yAxis: {
32 | name: YName,
33 | ...yAxis,
34 | },
35 | series: [{
36 | type: 'k',
37 | data: seriesData
38 | }],
39 | grid: getGrid(this.props),
40 | };
41 | return (
42 |
50 | );
51 | }
52 | }
--------------------------------------------------------------------------------
/src/components/ECharts/components/grid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.1.0
5 | * @description
6 | * @git
7 | */
8 | export default (props) => {
9 | const { grid, gridTop, gridBottom, gridLeft, gridRight } = props;
10 |
11 | const gridOpt = {};
12 | if (gridTop && gridTop !== '') {
13 | gridOpt.top = gridTop;
14 | }
15 | if (gridBottom && gridBottom !== '') {
16 | gridOpt.bottom = gridBottom;
17 | }
18 | if (gridLeft && gridLeft !== '') {
19 | gridOpt.left = gridLeft;
20 | }
21 | if (gridRight && gridRight !== '') {
22 | gridOpt.right = gridRight;
23 | }
24 | return { ...gridOpt, ...grid };
25 | };
--------------------------------------------------------------------------------
/src/components/ECharts/components/grid.propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | export default {
4 | //直角坐标系内绘图网格配置
5 | grid: PropTypes.object,
6 | gridTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
7 | gridBottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
8 | gridLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
9 | gridRight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
10 | };
11 |
--------------------------------------------------------------------------------
/src/components/ECharts/components/legend.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const { legend, legendOrient, showLegend, legendLeft, legendRight, legendTop, legendBottom } = props;
9 |
10 | return {
11 | show: showLegend,
12 | left: legendLeft,
13 | right: legendRight,
14 | top: legendTop,
15 | bottom: legendBottom,
16 | orient: legendOrient,
17 | ...legend
18 | };
19 | };
--------------------------------------------------------------------------------
/src/components/ECharts/components/legend.propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | export default {
4 | //图形图例配置项
5 | legend: PropTypes.object,
6 | //是否显示图例
7 | showLegend: PropTypes.bool,
8 | //图例列表的布局朝向。
9 | legendOrient: PropTypes.oneOf(['horizontal', 'vertical']),
10 | //图例组件离容器左侧的距离。
11 | legendLeft: PropTypes.oneOfType([
12 | PropTypes.number,
13 | PropTypes.oneOf(['left', 'right', 'center']),
14 | ]),
15 | //图例组件离容器右侧的距离。
16 | legendRight: PropTypes.oneOfType([
17 | PropTypes.number,
18 | PropTypes.oneOf(['left', 'right', 'center']),
19 | ]),
20 | //图例组件离容器上侧的距离。
21 | legendTop: PropTypes.oneOfType([
22 | PropTypes.number,
23 | PropTypes.oneOf(['top', 'bottom', 'middle']),
24 | ]),
25 | //图例组件离容器底侧的距离。
26 | legendBottom: PropTypes.oneOfType([
27 | PropTypes.number,
28 | PropTypes.oneOf(['top', 'bottom', 'middle']),
29 | ]),
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/ECharts/components/title.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const {
9 | title,
10 | titleText,
11 | titleFontSize = 20,
12 | titleColor = "#333",
13 | titleFontWeight = 'normal',
14 | titleTop,
15 | titleBottom,
16 | titleLeft,
17 | titleRight
18 | } = props;
19 |
20 | return {
21 | text: titleText,
22 | textStyle: {
23 | color: titleColor,
24 | fontSize: titleFontSize,
25 | fontWeight: titleFontWeight,
26 | },
27 | top: titleTop,
28 | bottom: titleBottom,
29 | left: titleLeft,
30 | right: titleRight,
31 | ...title
32 | };
33 | };
--------------------------------------------------------------------------------
/src/components/ECharts/components/title.propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | export default {
4 | //组件标题配置项
5 | title: PropTypes.object,
6 | //组件标题
7 | titleText: PropTypes.string,
8 | titleColor: PropTypes.string,
9 | titleFontSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
10 | titleFontWeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
11 | titleTop: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
12 | titleBottom: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
13 | titleLeft: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
14 | titleRight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
15 | };
--------------------------------------------------------------------------------
/src/components/ECharts/components/toolbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export default (props) => {
8 | const {
9 | toolbox,
10 | showToolbox,
11 | showToolboxDataZoom,
12 | showToolboxDataView,
13 | showToolboxMagicType,
14 | toolboxMagicType,
15 | showToolboxRestore,
16 | showToolboxSaveAsImage
17 | } = props;
18 |
19 | return {
20 | show: showToolbox,
21 | feature: {
22 | dataZoom: {
23 | show: showToolboxDataZoom,
24 | yAxisIndex: 'none'
25 | },
26 | dataView: {
27 | show: showToolboxDataView,
28 | readOnly: false
29 | },
30 | magicType: {
31 | show: showToolboxMagicType,
32 | type: toolboxMagicType
33 | },
34 | restore: {
35 | show: showToolboxRestore
36 | },
37 | saveAsImage: {
38 | show: showToolboxSaveAsImage
39 | }
40 | },
41 | ...toolbox
42 | };
43 | };
--------------------------------------------------------------------------------
/src/components/ECharts/components/toolbox.propTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | export default {
4 | //工具栏配置项
5 | toolbox: PropTypes.object,
6 | //是否显示工具栏
7 | showToolbox: PropTypes.bool,
8 | //区域缩放
9 | showToolboxDataZoom: PropTypes.bool,
10 | //数据视图
11 | showToolboxDataView: PropTypes.bool,
12 | //是否图形切换
13 | showToolboxMagicType: PropTypes.bool,
14 | //图形切换类型
15 | toolboxMagicType: PropTypes.array,
16 | //刷新还原
17 | showToolboxRestore: PropTypes.bool,
18 | //保存为图片
19 | showToolboxSaveAsImage: PropTypes.bool,
20 | };
--------------------------------------------------------------------------------
/src/components/ECharts/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description echarts基本配置
6 | */
7 | export default {
8 | notMerge: true,
9 | lazyUpdate: true,
10 | theme: 'light',
11 | loadingOption: {
12 | text: '数据加载中',
13 | }
14 | };
--------------------------------------------------------------------------------
/src/components/ECharts/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import React, { Suspense } from 'react';
8 | import { Skeleton } from 'antd';
9 | const getComponent = Component => props => (
10 | }>
11 |
12 |
13 | );
14 |
15 | const Area = getComponent(React.lazy(() => import('./charts/Area')));
16 | const Bar = getComponent(React.lazy(() => import('./charts/Bar')));
17 | const YBar = getComponent(React.lazy(() => import('./charts/Bar-y')));
18 | const Line = getComponent(React.lazy(() => import('./charts/Line')));
19 | const Funnel = getComponent(React.lazy(() => import('./charts/Funnel')));
20 | const Pie = getComponent(React.lazy(() => import('./charts/Pie')));
21 | const PieDoughnut = getComponent(React.lazy(() => import('./charts/Pie-doughnut')));
22 | const Sankey = getComponent(React.lazy(() => import('./charts/Sankey')));
23 | const Radar = getComponent(React.lazy(() => import('./charts/Radar')));
24 | const ChinaMap = getComponent(React.lazy(() => import('./charts/chinaMap')));
25 | const Candlestick = getComponent(React.lazy(() => import('./charts/candlestick')));
26 |
27 | export {
28 | Area,
29 | Bar,
30 | YBar,
31 | // BarWaterfall,
32 | Line,
33 | Funnel,
34 | Pie,
35 | PieDoughnut,
36 | // Map,
37 | // PieCustom,
38 | // PieNest,
39 | Radar,
40 | Sankey,
41 | ChinaMap,
42 | Candlestick
43 | // Scatter
44 | };
--------------------------------------------------------------------------------
/src/components/ECharts/methods/.test.js:
--------------------------------------------------------------------------------
1 | import { _formatNumer, _isData, _toDataset } from './index';
2 | // import renderer from 'react-test-renderer';
3 |
4 | describe('methods', () => {
5 | const data = {
6 | columns: [
7 | {
8 | field: "product",
9 | name: "分类",
10 | type: "string"
11 | },
12 | {
13 | field: "2015",
14 | name: "2015",
15 | type: "number"
16 | },
17 | {
18 | field: "2016",
19 | name: "2016",
20 | type: "number"
21 | },
22 | {
23 | field: "2017",
24 | name: "2017",
25 | type: "number"
26 | }
27 | ],
28 | rows: [
29 | {
30 | product: 'Matcha Latte',
31 | 2015: 43.3,
32 | 2016: 85.8,
33 | 2017: 93.7
34 | },
35 | {
36 | product: 'Milk Tea',
37 | 2015: 83.1,
38 | 2016: 73.4,
39 | 2017: 55.1
40 | },
41 | {
42 | product: 'Cheese Cocoa',
43 | 2015: 86.4,
44 | 2016: 65.2,
45 | 2017: 82.5
46 | },
47 | {
48 | product: 'Walnut Brownie',
49 | 2015: 72.4,
50 | 2016: 53.9,
51 | 2017: 39.1
52 | },
53 | ]
54 | };
55 | it('_formatNumer', () => {
56 | expect(_formatNumer(10000000000)).toBe('10,000,000,000');
57 | expect(_formatNumer(10000000)).toBe('10,000,000');
58 | expect(_formatNumer(10000)).toBe('10,000');
59 | expect(_formatNumer(1000)).toBe('1,000');
60 | expect(_formatNumer(1000.12)).toBe('1,000.12');
61 | expect(_formatNumer(100)).toBe('100');
62 | expect(_formatNumer([])).toEqual([]);
63 | });
64 | it('_isData', () => {
65 | expect(_isData(data)).toBe(true);
66 | expect(_isData({
67 | columns: [],
68 | rows: []
69 | })).toBe(false);
70 | expect(_isData({})).toBe(false);
71 | });
72 | it('_toDataset', () => {
73 | expect(_toDataset(data)).toEqual([["分类", "2015", "2016", "2017"], ["Matcha Latte", 43.3, 85.8, 93.7], ["Milk Tea", 83.1, 73.4, 55.1], ["Cheese Cocoa", 86.4, 65.2, 82.5], ["Walnut Brownie", 72.4, 53.9, 39.1]]);
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/src/components/ECharts/methods/_formatNumer.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 数字加逗号显示
4 | * @param {number} number
5 | * @param {number} limit
6 | * @returns {string}
7 | */
8 | export default (num, limit = 1000) => {
9 | if (typeof num !== 'number' && typeof num !== 'string') return num;
10 | num += "";
11 | if (num === "NaN") return num;
12 | const n = num.indexOf('.');
13 | let str = "";
14 | if (n > -1) {
15 | str = num.slice(n);
16 | num = num.slice(0, n);
17 | }
18 | const arr = num.split("");
19 | const len = arr.length;
20 | if (limit === 1000) {
21 | if (len >= 4) {
22 | arr.splice(-3, 0, ',');
23 | }
24 | if (len >= 7) {
25 | arr.splice(-7, 0, ',');
26 | }
27 | if (len >= 10) {
28 | arr.splice(-11, 0, ',');
29 | }
30 | if (len >= 13) {
31 | arr.splice(-15, 0, ',');
32 | }
33 | }
34 | num = arr.join("") + str;
35 | return num;
36 | };
--------------------------------------------------------------------------------
/src/components/ECharts/methods/_isData.js:
--------------------------------------------------------------------------------
1 | import { isObject, isArray } from 'lodash';
2 | export default (data, dataType) => {
3 | if (!isObject(data)) {
4 | return false;
5 | }
6 | if (dataType === 'special') {
7 | return true;
8 | }
9 | if (!isArray(data.columns) || !isArray(data.rows)) {
10 | return false;
11 | }
12 | if (data.columns.length === 0 || data.rows.length === 0) {
13 | return false;
14 | }
15 | return true;
16 | };
--------------------------------------------------------------------------------
/src/components/ECharts/methods/_toDataset_A.js:
--------------------------------------------------------------------------------
1 | import memoizeOne from 'memoize-one';
2 | import isEqual from 'lodash/isEqual';
3 | /**
4 | *
5 | * {
6 | columns: [
7 | {
8 | field: "product",
9 | name: "分类",
10 | type: "string"
11 | },
12 | {
13 | field: "2015",
14 | name: "2015",
15 | type: "number"
16 | },
17 | {
18 | field: "2016",
19 | name: "2016",
20 | type: "number"
21 | },
22 | {
23 | field: "2017",
24 | name: "2017",
25 | type: "number"
26 | }
27 | ],
28 | rows: [
29 | {
30 | product: 'Matcha Latte',
31 | 2015: 43.3,
32 | 2016: 85.8,
33 | 2017: 93.7
34 | },
35 | {
36 | product: 'Milk Tea',
37 | 2015: 83.1,
38 | 2016: 73.4,
39 | 2017: 55.1
40 | },
41 | {
42 | product: 'Cheese Cocoa',
43 | 2015: 86.4,
44 | 2016: 65.2,
45 | 2017: 82.5
46 | },
47 | {
48 | product: 'Walnut Brownie',
49 | 2015: 72.4,
50 | 2016: 53.9,
51 | 2017: 39.1
52 | },
53 | ]
54 | }
55 | *
56 | * @param {*} data
57 | * [
58 | ['product', '2015', '2016', '2017'],
59 | ['Matcha Latte', 43.3, 85.8, 93.7],
60 | ['Milk Tea', 83.1, 73.4, 55.1],
61 | ['Cheese Cocoa', 86.4, 65.2, 82.5],
62 | ['Walnut Brownie', 72.4, 53.9, 39.1]
63 | ]
64 | *
65 | */
66 | const toDataset = (data) => {
67 | const { columns, rows } = data;
68 | const row_0 = columns.map(item => item.name);
69 | const rest = rows.map(row => columns.map(item => row[item.field]));
70 | return [row_0, ...rest];
71 | }
72 | export default memoizeOne(toDataset, isEqual);
--------------------------------------------------------------------------------
/src/components/ECharts/methods/_toDataset_B.js:
--------------------------------------------------------------------------------
1 | import memoizeOne from 'memoize-one';
2 | import isEqual from 'lodash/isEqual';
3 | /**
4 | *
5 | * {
6 | columns: [
7 | {
8 | field: "category",
9 | name: "分类",
10 | type: "string"
11 | },
12 | {
13 | field: "2015",
14 | name: "2015",
15 | type: "number"
16 | },
17 | {
18 | field: "2016",
19 | name: "2016",
20 | type: "number"
21 | },
22 | {
23 | field: "2017",
24 | name: "2017",
25 | type: "number"
26 | }
27 | ],
28 | rows: [
29 | {
30 | product: 'Matcha Latte',
31 | 2015: 43.3,
32 | 2016: 85.8,
33 | 2017: 93.7
34 | },
35 | {
36 | product: 'Milk Tea',
37 | 2015: 83.1,
38 | 2016: 73.4,
39 | 2017: 55.1
40 | },
41 | {
42 | product: 'Cheese Cocoa',
43 | 2015: 86.4,
44 | 2016: 65.2,
45 | 2017: 82.5
46 | },
47 | {
48 | product: 'Walnut Brownie',
49 | 2015: 72.4,
50 | 2016: 53.9,
51 | 2017: 39.1
52 | },
53 | ]
54 | }
55 | *
56 | *
57 | * @param {*} data
58 | * {
59 | dimensions: ['product', '2015', '2016', '2017'],
60 | source: [
61 | {product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7},
62 | {product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1},
63 | {product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5},
64 | {product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1}
65 | ]
66 | }
67 | *
68 | */
69 | const toDataset = (data) => {
70 | const { columns, rows } = data;
71 | const dimensions = columns.map(item => item.field);
72 |
73 | return {
74 | dimensions,
75 | source: rows
76 | };
77 | }
78 | export default memoizeOne(toDataset, isEqual);
--------------------------------------------------------------------------------
/src/components/ECharts/methods/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import memoizeOne from 'memoize-one';
8 | import isEqual from 'lodash/isEqual';
9 |
10 | import formatNumer from './_formatNumer';
11 | import toDataset from './_toDataset_A';
12 | import isData from './_isData';
13 |
14 | const _formatNumer = memoizeOne(formatNumer);
15 | const _toDataset = memoizeOne(toDataset, isEqual);
16 | const _isData = memoizeOne(isData, isEqual);
17 |
18 | export {
19 | _formatNumer,
20 | _toDataset,
21 | _isData
22 | };
--------------------------------------------------------------------------------
/src/components/Exception/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 异常处理页面403、404、500
6 | */
7 | import React, { PureComponent } from 'react';
8 | import { Link } from 'umi';
9 | import { Button } from 'antd';
10 | import PropTypes from 'prop-types';
11 | import config from './typeConfig';
12 | import styles from './index.less';
13 |
14 | export default class Exception extends PureComponent {
15 | static defaultProps = {
16 | backText: 'back to home',
17 | redirect: {
18 | pathname: '/sys'
19 | },
20 | };
21 |
22 | constructor(props) {
23 | super(props);
24 | this.state = {};
25 | }
26 | render() {
27 | const {
28 | backText,
29 | type,
30 | title,
31 | desc,
32 | redirect,
33 | img
34 | } = this.props;
35 | const pageType = type in config ? type : '404';
36 | return (
37 |
38 |
44 |
45 |
{title || config[pageType].title}
46 |
{desc || config[pageType].desc}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 | }
57 | }
58 | Exception.propTypes = {
59 | backText: PropTypes.string,
60 | type: PropTypes.oneOfType([
61 | PropTypes.string,
62 | PropTypes.number,
63 | ]),
64 | title: PropTypes.string,
65 | desc: PropTypes.string,
66 | redirect: PropTypes.object,
67 | img: PropTypes.string
68 | };
--------------------------------------------------------------------------------
/src/components/Exception/index.less:
--------------------------------------------------------------------------------
1 | @import "../../../node_modules/antd/lib/style/themes/default.less";
2 | .exception {
3 | display: flex;
4 | flex: auto;
5 | align-items: center;
6 | min-height: 500px;
7 | .imgBlock {
8 | flex: 0 0 62.5%;
9 | width: 62.5%;
10 | padding-right: 152px;
11 | zoom: 1;
12 | &:before,
13 | &:after {
14 | content: ' ';
15 | display: table;
16 | }
17 | &:after {
18 | clear: both;
19 | visibility: hidden;
20 | font-size: 0;
21 | height: 0;
22 | }
23 | }
24 | .imgEle {
25 | height: 360px;
26 | width: 100%;
27 | max-width: 430px;
28 | float: right;
29 | background-repeat: no-repeat;
30 | background-position: 50% 50%;
31 | background-size: contain;
32 | }
33 | .content {
34 | flex: auto;
35 | h1 {
36 | color: #434e59;
37 | font-size: 72px;
38 | font-weight: 600;
39 | line-height: 72px;
40 | margin-bottom: 24px;
41 | }
42 | .desc {
43 | color: @text-color-secondary;
44 | font-size: 20px;
45 | line-height: 28px;
46 | margin-bottom: 16px;
47 | }
48 | .actions {
49 | button:not(:last-child) {
50 | margin-right: 8px;
51 | }
52 | }
53 | }
54 | }
55 |
56 | @media screen and (max-width: @screen-xl) {
57 | .exception {
58 | .imgBlock {
59 | padding-right: 88px;
60 | }
61 | }
62 | }
63 |
64 | @media screen and (max-width: @screen-sm) {
65 | .exception {
66 | display: block;
67 | text-align: center;
68 | .imgBlock {
69 | padding-right: 0;
70 | margin: 0 auto 24px;
71 | }
72 | }
73 | }
74 |
75 | @media screen and (max-width: @screen-xs) {
76 | .exception {
77 | .imgBlock {
78 | margin-bottom: -24px;
79 | overflow: hidden;
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/src/components/Exception/typeConfig.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | 403: {
3 | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
4 | title: '403',
5 | desc: '抱歉,你无权访问该页面',
6 | },
7 | 404: {
8 | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
9 | title: '404',
10 | desc: '抱歉,你访问的页面不存在',
11 | },
12 | 500: {
13 | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
14 | title: '500',
15 | desc: '抱歉,服务器出错了',
16 | },
17 | };
18 |
19 | export default config;
20 |
--------------------------------------------------------------------------------
/src/components/GlobalDrawer/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Component } from 'react';
8 | import { Drawer } from 'antd';
9 | class GlobalDrawer extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { visible = false } = props;
13 | this.state = {
14 | visible: visible
15 | };
16 | }
17 | onClose = () => {
18 | const { visible } = this.state;
19 | this.setState({
20 | visible: !visible
21 | });
22 | }
23 | render() {
24 | return (
25 |
32 | Some contents...
33 | Some contents...
34 | Some contents...
35 |
36 | );
37 | }
38 | }
39 |
40 | export default GlobalDrawer;
41 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/index.less:
--------------------------------------------------------------------------------
1 | .headerSearch {
2 | :global(.anticon-search) {
3 | cursor: pointer;
4 | font-size: 16px;
5 | }
6 | // color: #ffffff;
7 | .input {
8 | transition: width 0.3s, margin-left 0.3s;
9 | width: 0;
10 | background: transparent;
11 | border-radius: 0;
12 | :global(.ant-select-selection) {
13 | background: transparent;
14 | }
15 | input {
16 | border: 0;
17 | padding-left: 0;
18 | padding-right: 0;
19 | box-shadow: none !important;
20 | // color: #ffffff;
21 | }
22 | &,
23 | &:hover,
24 | &:focus {
25 | border-bottom: 1px solid #d9d9d9;
26 | }
27 | &.show {
28 | width: 140px;
29 | margin-left: 8px;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/components/Icon/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description icon组件,兼容iconfont.cn/提供的标签
6 | * @link https://www.iconfont.cn
7 | */
8 | import { Icon } from 'antd';
9 | import { iconUrl } from '@platformConfig';
10 | import PropTypes from 'prop-types';
11 | function Index(props) {
12 | const { type = "bars", style = {}, spin = false } = props;
13 | if (type.indexOf("icon") > -1) {
14 | const MyIcon = Icon.createFromIconfontCN({
15 | scriptUrl: iconUrl, // 在 iconfont.cn 上生成
16 | });
17 | return (
18 |
19 | );
20 | } else {
21 |
22 | return (
23 |
24 | );
25 | }
26 | }
27 | export default Index;
28 | Index.propTypes = {
29 | //icon类型
30 | type: PropTypes.string,
31 | //icon 样式
32 | style: PropTypes.object,
33 | //是否加载中
34 | spin: PropTypes.bool,
35 | };
--------------------------------------------------------------------------------
/src/components/Loader/Loader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import React from 'react';
8 | import PropTypes from 'prop-types';
9 | import classNames from 'classnames';
10 | import styles from './Loader.less';
11 |
12 | const Loader = ({ spinning, fullScreen }) => {
13 | return ();
23 | };
24 |
25 |
26 | Loader.propTypes = {
27 | spinning: PropTypes.bool,
28 | fullScreen: PropTypes.bool,
29 | };
30 |
31 | export default Loader;
32 |
--------------------------------------------------------------------------------
/src/components/Loader/Loader.less:
--------------------------------------------------------------------------------
1 | .loader {
2 | display: block;
3 | background-color: #fff;
4 | width: 100%;
5 | position: absolute;
6 | top: 0;
7 | bottom: 0;
8 | left: 0;
9 | z-index: 100000;
10 | display: flex;
11 | justify-content: center;
12 | align-items: center;
13 | opacity: 1;
14 | text-align: center;
15 | margin-top: 25px;
16 | &.fullScreen {
17 | position: fixed;
18 | }
19 | .warpper {
20 | width: 100px;
21 | height: 100px;
22 | display: inline-flex;
23 | flex-direction: column;
24 | justify-content: space-around;
25 | }
26 | .inner {
27 | width: 40px;
28 | height: 40px;
29 | margin: 0 auto;
30 | text-indent: -12345px;
31 | border-top: 1px solid rgba(0, 0, 0, 0.08);
32 | border-right: 1px solid rgba(0, 0, 0, 0.08);
33 | border-bottom: 1px solid rgba(0, 0, 0, 0.08);
34 | border-left: 1px solid rgba(0, 0, 0, 0.7);
35 | border-radius: 50%;
36 | z-index: 100001;
37 | :local {
38 | animation: spinner 600ms infinite linear;
39 | }
40 | }
41 | .text {
42 | width: 100px;
43 | height: 20px;
44 | text-align: center;
45 | font-size: 12px;
46 | letter-spacing: 4px;
47 | color: #000;
48 | }
49 | &.hidden {
50 | z-index: -1;
51 | opacity: 0;
52 | transition: opacity 1s ease 0.5s, z-index 0.1s ease 1.5s;
53 | }
54 | }
55 |
56 | @keyframes spinner {
57 | 0% {
58 | transform: rotate(0deg);
59 | }
60 | 100% {
61 | transform: rotate(360deg);
62 | }
63 | }
--------------------------------------------------------------------------------
/src/components/Loader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Loader",
3 | "version": "0.0.0",
4 | "private": true,
5 | "main": "./Loader.js"
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/PageHeader/breadcrumb.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { Breadcrumb } from 'antd';
3 | import { Link } from 'umi';
4 | import isEqual from 'lodash/isEqual';
5 | import { Icon } from '@components';
6 | import styles from './index.less';
7 | class BreadcrumbView extends PureComponent {
8 | state = {
9 | breadcrumbItems: null,
10 | };
11 | static defaultProps = {
12 | breadcrumbList: [],
13 | // linkElement: 'a',
14 | breadcrumbSeparator: '/'
15 | };
16 | componentDidMount() {
17 | this.getBreadcrumbDom();
18 | }
19 | componentDidUpdate(preProps) {
20 | const { breadcrumbList } = this.props;
21 | if (!isEqual(breadcrumbList, preProps.breadcrumbList)) {
22 | this.getBreadcrumbDom();
23 | }
24 | }
25 | getBreadcrumbDom = () => {
26 | const breadcrumbItems = this.itemRender();
27 | this.setState({
28 | breadcrumbItems,
29 | });
30 | }
31 | itemRender = () => {
32 | const { breadcrumbList } = this.props;
33 | const len = breadcrumbList.length - 1;
34 | return breadcrumbList.map((item, i) => {
35 | const { title, icon, link, query, state } = typeof item === 'object' ? item : {};
36 | return (
37 |
38 | {icon && }
39 | {link && i !== len ? {title} : {title || item}}
40 |
41 | );
42 | });
43 | }
44 | render() {
45 | const { breadcrumbSeparator } = this.props;
46 | const { breadcrumbItems } = this.state;
47 | return (
48 |
49 | {breadcrumbItems}
50 |
51 | );
52 | }
53 | }
54 | export default BreadcrumbView;
--------------------------------------------------------------------------------
/src/components/PageHeader/index.less:
--------------------------------------------------------------------------------
1 | //height=16-8+8+8+22+10=56px
2 | .wrapper {
3 | padding-top: 8px;
4 | padding-bottom: 8px;
5 | margin-top: -7px;
6 | margin-left: -16px;
7 | margin-right: -16px;
8 | padding-left: 24px;
9 | background-color: #ffffff;
10 | }
11 |
12 | .breadcrumb {
13 | line-height: 22px;
14 | font-size: 14px;
15 | }
16 |
17 | .title {
18 | margin: 10px 0;
19 | font-weight: normal;
20 | }
21 |
22 | .describe {
23 | font-size: 12px;
24 | }
25 |
26 | .hide {
27 | display: none;
28 | }
--------------------------------------------------------------------------------
/src/components/PageLoading/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 全局loading,用于页面加载时
6 | */
7 | import React from 'react';
8 | import { Spin } from 'antd';
9 | import styles from './index.less';
10 |
11 | // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
12 | export default () => (
13 |
14 |
15 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/PageLoading/index.less:
--------------------------------------------------------------------------------
1 | .loader {
2 | width: 100%;
3 | height: 100%;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | }
--------------------------------------------------------------------------------
/src/components/PageWrapper/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 页面wrapper组件
6 | */
7 | import React, { PureComponent } from 'react';
8 | import { connect } from 'dva';
9 | import PropTypes from 'prop-types';
10 | import classNames from 'classnames';
11 | import PageHeader from '../PageHeader';
12 | import Context from '@context';
13 | // import Loader from '../Loader';
14 | import styles from './index.less';
15 |
16 | @connect(({ menu }) => {
17 | const { flattenMenuData, breadcrumbList } = menu;
18 | return {
19 | flattenMenuData,
20 | breadcrumbList
21 | };
22 | })
23 | class PageWrapper extends PureComponent {
24 | static defaultProps = {
25 | loading: false,
26 | showHeader: true,
27 | flex: false,
28 | }
29 | render() {
30 | const {
31 | className,
32 | children,
33 | loading,
34 | pathtitles,
35 | title,
36 | description,
37 | showHeader,
38 | flex,
39 | flattenMenuData,
40 | style
41 | } = this.props;
42 | return (
43 |
44 | {({ location }) => (
45 |
50 |
58 |
64 | {/* {loading && } */}
65 | {children}
66 |
67 |
68 | )}
69 |
70 | );
71 | }
72 | }
73 |
74 | export default PageWrapper;
75 |
76 | PageWrapper.propTypes = {
77 | className: PropTypes.string,
78 | children: PropTypes.node,
79 | loading: PropTypes.bool,
80 | inner: PropTypes.bool,
81 | };
82 |
--------------------------------------------------------------------------------
/src/components/PageWrapper/index.less:
--------------------------------------------------------------------------------
1 | .contentInner {
2 | padding: 0;
3 | box-shadow: @shadow-1;
4 | flex: auto;
5 | display: flex;
6 | flex-direction: column;
7 | }
8 |
9 | .children {
10 | flex: auto;
11 | margin-top: 20px;
12 | background-color: #fff;
13 | padding: 10px;
14 | position: relative;
15 | }
16 |
17 | .loading {
18 | overflow: 'hidden'
19 | }
--------------------------------------------------------------------------------
/src/components/Register/index.less:
--------------------------------------------------------------------------------
1 |
2 | .getCaptcha {
3 | display: block;
4 | width: 100%;
5 | }
6 |
7 | .submit {
8 | width: 50%;
9 | }
10 |
11 | .login {
12 | float: right;
13 | // line-height: @btn-height-lg;
14 | }
15 |
16 | .success,
17 | .warning,
18 | .error {
19 | transition: color 0.3s;
20 | }
21 |
22 | .success {
23 | // color: @success-color;
24 | }
25 |
26 | .warning {
27 | // color: @warning-color;
28 | }
29 |
30 | .error {
31 | // color: @error-color;
32 | }
--------------------------------------------------------------------------------
/src/components/ResetPassword/index.less:
--------------------------------------------------------------------------------
1 | .submit {
2 | margin-left: 50%;
3 | width: 100px;
4 | }
--------------------------------------------------------------------------------
/src/components/dataTable/header.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.1.0
5 | * @description datatable组件
6 | */
7 | import { PureComponent } from 'react';
8 | import PropTypes from 'prop-types';
9 | import { Button, Row, Col } from 'antd';
10 | import Search from './search';
11 | import TableSelect from './select';
12 |
13 |
14 | export default class DataTable extends PureComponent {
15 | static defaultProps = {
16 | selectProps: {}, // select属性
17 | searchProps: {}, // search属性
18 | download: {},
19 | headerStyle: {},
20 | handleSearch: () => { }
21 | }
22 | render() {
23 | const { searchProps, selectProps, download, headerStyle, handleSearch } = this.props;
24 |
25 | const {
26 | show: selectShow,
27 | data: optionData,
28 | defaultValue,
29 | onChange: selectChange,
30 | } = selectProps;
31 |
32 | const {
33 | show: searchShow,
34 | } = searchProps;
35 | const {
36 | show: downloadShow,
37 | handleClick = () => { }
38 | } = download;
39 | const handleSelectChange = (value) => {
40 | if (typeof selectChange === "function") {
41 | selectChange(value);
42 | }
43 | };
44 | const onSearch = key => {
45 | if (typeof handleSearch === "function") {
46 | handleSearch(key);
47 | }
48 | };
49 |
50 | const tableSelect = ;
55 | const search = ;
58 | const down = ;
59 | const layout1 =
60 |
61 | {selectShow ? tableSelect : ''}
62 |
63 |
64 | {searchShow ? search : ''}
65 |
66 |
;
67 | const layout2 =
68 |
69 | {searchShow ? search : ''}
70 |
71 |
72 | {down}
73 |
74 |
;
75 |
76 | return downloadShow ? layout2 : layout1;
77 | }
78 | }
79 | DataTable.propTypes = {
80 | searchProps: PropTypes.object,
81 | selectProps: PropTypes.object,
82 | download: PropTypes.object,
83 | headerStyle: PropTypes.object,
84 | handleSearch: PropTypes.func
85 | };
--------------------------------------------------------------------------------
/src/components/dataTable/search.js:
--------------------------------------------------------------------------------
1 | import { Input } from 'antd';
2 | import { trim } from 'lodash';
3 |
4 | function TableSearch(props) {
5 | const { onSearch = () => { } } = props;
6 | return (
7 | {
10 | const { target } = e;
11 | const { value } = target;
12 | onSearch(trim(value));
13 | }}
14 | style={{ width: 200 }}
15 | />
16 | );
17 | }
18 | export default TableSearch;
--------------------------------------------------------------------------------
/src/components/dataTable/select.js:
--------------------------------------------------------------------------------
1 | import { Select } from 'antd';
2 |
3 | const { Option } = Select;
4 | function TableSelect(props) {
5 | const { data = [], onChange: handleChange, defaultValue } = props;
6 | if (data.length === 0) {
7 | return '';
8 | }
9 | const getOpts = (category) => {
10 | return category.map((item, i) => {
11 | const { name, value } = item;
12 | return ();
13 | });
14 | };
15 |
16 | return ();
27 | }
28 | export default TableSelect;
--------------------------------------------------------------------------------
/src/components/dataTable/tableFooter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @description tableFooter
4 | */
5 | import { PureComponent } from 'react';
6 | import moment from 'moment';
7 | export default class tableFooter extends PureComponent {
8 | render() {
9 | return (
10 | {`表格数据更新时间:${this.props.time || moment().format('HH:mm:ss')}`}
11 | );
12 | }
13 | }
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import Register from './Register';
2 | import Icon from './Icon';
3 | import DataTable from './DataTable';
4 | import Breadcrumb from './Breadcrumb';
5 | import Announcement from './Announcement';
6 | import PageHeader from './PageHeader';
7 | import ResetPassword from './ResetPassword';
8 | import Page from './PageWrapper';
9 | import GlobalDrawer from './GlobalDrawer';
10 | import Exception from './Exception';
11 | import HeaderSearch from './HeaderSearch';
12 | import Consumer from './Consumer';
13 |
14 | export {
15 | Icon,
16 | DataTable,
17 | Breadcrumb,
18 | Announcement,
19 | PageHeader,
20 | ResetPassword,
21 | Register,
22 | Page,
23 | GlobalDrawer,
24 | Exception,
25 | HeaderSearch,
26 | Consumer,
27 | };
28 |
--------------------------------------------------------------------------------
/src/global.less:
--------------------------------------------------------------------------------
1 | @import url("./themes/index.less");
2 |
3 | html,
4 | body,
5 | #root {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0;
11 | // height: 100%;
12 | .scrollbar();
13 | }
14 |
15 | .hideScrollbar {
16 | .scrollbar();
17 | }
18 |
19 | .progressbar {
20 | /*火狐下隐藏滚动条*/
21 | scrollbar-width: none;
22 |
23 | &::-webkit-scrollbar {
24 | /*滚动条整体样式*/
25 | width: 4px;
26 | /*高宽分别对应横竖滚动条的尺寸*/
27 | height: 4px;
28 | }
29 |
30 | &::-webkit-scrollbar-thumb {
31 | /*滚动条里面小方块*/
32 | border-radius: 10px;
33 | -webkit-box-shadow: inset -1px 0 5px rgba(0, 33, 64, 0.7); // background: #999999;
34 | background-color: rgba(240, 242, 245, 0.9)
35 | }
36 |
37 | &::-webkit-scrollbar-track {
38 | /*滚动条里面轨道*/
39 | // -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
40 | // border-radius: 10px;
41 | // background: #EDEDED;
42 | }
43 | }
--------------------------------------------------------------------------------
/src/layouts/Context.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { createContext } from 'react';
8 |
9 | export default createContext();
10 |
--------------------------------------------------------------------------------
/src/layouts/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | import BasicLayout from '..';
2 | import renderer from 'react-test-renderer';
3 |
4 | describe('Layout: BasicLayout', () => {
5 | it('Render correctly', () => {
6 | const wrapper = renderer.create();
7 | expect(wrapper.root.children.length).toBe(1);
8 | const outerLayer = wrapper.root.children[0];
9 | expect(outerLayer.type).toBe('div');
10 | // const title = outerLayer.children[0];
11 | // expect(title.type).toBe('h1');
12 | // expect(title.children[0]).toBe('Yay! Welcome to umi!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/layouts/basic/Footer/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { copyright } from '@platformConfig';
8 | import React, { Fragment } from 'react';
9 | import { Layout, Icon } from 'antd';
10 | import GlobalFooter from '../../components/GlobalFooter';
11 |
12 | const { Footer } = Layout;
13 | const FooterView = () => (
14 |
43 | );
44 | export default FooterView;
45 |
--------------------------------------------------------------------------------
/src/layouts/basic/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Component } from 'react';
8 | import { Layout } from 'antd';
9 | import MyFooter from './Footer';
10 | import styles from './index.less';
11 |
12 | const {
13 | Footer, Content,
14 | } = Layout;
15 | class Index extends Component {
16 | render() {
17 | const { children } = this.props;
18 | return (
19 |
20 |
21 | {children}
22 |
23 |
26 |
27 | );
28 | }
29 | }
30 | export default Index;
--------------------------------------------------------------------------------
/src/layouts/basic/index.less:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | background-image: url(https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg);
6 | background-repeat: no-repeat;
7 | background-position: center 110px;
8 | background-size: 100%;
9 | }
10 |
11 | .content {
12 | display: flex;
13 | justify-content: center;
14 | align-items: center;
15 | }
--------------------------------------------------------------------------------
/src/layouts/components/Authorized/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 路由权限组件
6 | */
7 | import { PureComponent } from 'react';
8 | import { Page } from '@components';
9 | export default class Authorized extends PureComponent {
10 | render() {
11 | const { children, noMatch, diffMenuData, location: { pathname } } = this.props;
12 | const [res] = diffMenuData.filter(item => item.link === pathname);
13 | return res ? ({noMatch}) : children;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/layouts/components/GlobalDownload/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Icon } from 'antd';
8 | import { Link } from 'umi';
9 | import { Consumer } from '@components';
10 |
11 | const themeConfig = {
12 | dark: {
13 | color: '#fff'
14 | },
15 | light: {
16 | color: 'rgba(0, 0, 0, 0.65)'
17 | },
18 | };
19 | function Download(props) {
20 | const { userInfo, theme = 'dark' } = props;
21 | const { userName } = userInfo;
22 | return (
23 |
27 | 下载
28 |
29 | );
30 | }
31 | export default Consumer(Download);
--------------------------------------------------------------------------------
/src/layouts/components/GlobalFooter/demo/basic.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 0
3 | title: 演示
4 | iframe: 400
5 | ---
6 |
7 | 基本页脚。
8 |
9 | ````jsx
10 | import GlobalFooter from 'ant-design-pro/lib/GlobalFooter';
11 | import { Icon } from 'antd';
12 |
13 | const links = [{
14 | key: '帮助',
15 | title: '帮助',
16 | href: '',
17 | }, {
18 | key: 'github',
19 | title: ,
20 | href: 'https://github.com/ant-design/ant-design-pro',
21 | blankTarget: true,
22 | }, {
23 | key: '条款',
24 | title: '条款',
25 | href: '',
26 | blankTarget: true,
27 | }];
28 |
29 | const copyright = Copyright 2017 蚂蚁金服体验技术部出品
;
30 |
31 | ReactDOM.render(
32 |
36 | , mountNode);
37 | ````
38 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalFooter/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | export interface IGlobalFooterProps {
3 | links?: Array<{
4 | key?: string;
5 | title: React.ReactNode;
6 | href: string;
7 | blankTarget?: boolean;
8 | }>;
9 | copyright?: React.ReactNode;
10 | style?: React.CSSProperties;
11 | }
12 |
13 | export default class GlobalFooter extends React.Component {}
14 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalFooter/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import React from 'react';
8 | import classNames from 'classnames';
9 | import styles from './index.less';
10 |
11 | const GlobalFooter = ({ className, links, copyright }) => {
12 | const clsString = classNames(styles.globalFooter, className);
13 | return (
14 |
31 | );
32 | };
33 |
34 | export default GlobalFooter;
35 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalFooter/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .globalFooter {
4 | padding: 0 16px;
5 | margin: 20px 0 10px 0;
6 | text-align: center;
7 |
8 | .links {
9 | margin-bottom: 8px;
10 |
11 | a {
12 | color: @text-color-secondary;
13 | transition: all 0.3s;
14 |
15 | &:not(:last-child) {
16 | margin-right: 40px;
17 | }
18 |
19 | &:hover {
20 | color: @text-color;
21 | }
22 | }
23 | }
24 |
25 | .copyright {
26 | color: @text-color-secondary;
27 | font-size: @font-size-base;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalFooter/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title:
3 | en-US: GlobalFooter
4 | zh-CN: GlobalFooter
5 | subtitle: 全局页脚
6 | cols: 1
7 | order: 7
8 | ---
9 |
10 | 页脚属于全局导航的一部分,作为对顶部导航的补充,通过传递数据控制展示内容。
11 |
12 | ## API
13 |
14 | 参数 | 说明 | 类型 | 默认值
15 | ----|------|-----|------
16 | links | 链接数据 | array<{ title: ReactNode, href: string, blankTarget?: boolean }> | -
17 | copyright | 版权信息 | ReactNode | -
18 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalHeader/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description layout header组件
6 | */
7 | import User from '../GlobalUserCenter';
8 | import Search from '../GlobalSearch';
9 | import Notice from '../Notice';
10 | import SelectLang from '../SelectLang';
11 | import styles from './index.less';
12 |
13 |
14 | function Header(props) {
15 | const {
16 | userInfo = {},
17 | message,
18 | handleLoadMore = () => { },
19 | handleSetting = () => { }
20 | } = props;
21 | return (
22 |
23 |
24 |
30 |
35 |
36 |
37 | );
38 | }
39 | export default Header;
--------------------------------------------------------------------------------
/src/layouts/components/GlobalHeader/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | @pro-header-hover-bg: rgba(0, 0, 0, 0.025);
3 | .rightCenter {
4 | text-align: right;
5 | height: 100%;
6 | }
7 |
8 | .action {
9 | display: inline-block;
10 | height: 100%;
11 | padding: 0 12px;
12 | cursor: pointer;
13 | transition: all 0.3s;
14 | >i {
15 | color: @text-color;
16 | vertical-align: middle;
17 | }
18 | &:hover {
19 | background: @pro-header-hover-bg;
20 | }
21 | &:global(.opened) {
22 | background: @pro-header-hover-bg;
23 | }
24 | }
25 |
26 | .search {
27 | padding: 0 12px;
28 | &:hover {
29 | background: transparent;
30 | }
31 | }
32 |
33 | .account {
34 | .avatar {
35 | margin:~'calc((@{layout-header-height} - 24px) / 2)' 0;
36 | margin-right: 8px;
37 | color: @primary-color;
38 | vertical-align: top;
39 | background: rgba(255, 255, 255, 0.85);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/layouts/components/GlobalRoll/index.css:
--------------------------------------------------------------------------------
1 | .rollbox {
2 | overflow: hidden;
3 | width: 100%;
4 | }
5 | .rollbox .roll {
6 | color: #ffffff;
7 | margin: 0;
8 | padding: 0;
9 | white-space: nowrap;
10 | float: left;
11 | animation-name: roll;
12 | -webkit-animation-name: roll;
13 | animation-iteration-count: infinite;
14 | -webkit-animation-iteration-count: infinite;
15 | animation-timing-function: linear;
16 | -webkit-animation-timing-function: linear;
17 | }
18 | .rollbox .roll:hover {
19 | animation-play-state: paused;
20 | -webkit-animation-play-state: paused;
21 | cursor: progress;
22 | }
23 | @-webkit-keyframes roll {
24 | 0% {
25 | transform: translateX(1200px);
26 | -webkit-transform: translateX(1200px);
27 | }
28 | 100% {
29 | transform: translateX(-100%);
30 | -webkit-transform: translateX(-100%);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalRoll/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import styles from './index.less';
8 |
9 | function Roll(props) {
10 | const {
11 | notification = '',// "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.----------------------------------------------------"
12 | } = props;
13 | const len = notification.length;
14 | const ss = Math.floor(len / 12);
15 | return (
16 |
17 |
18 | {notification}
19 |
20 |
21 | );
22 | }
23 | export default Roll;
--------------------------------------------------------------------------------
/src/layouts/components/GlobalRoll/index.less:
--------------------------------------------------------------------------------
1 | .rollbox {
2 | overflow: hidden;
3 | width: 100%;
4 | .roll {
5 | color: #ffffff;
6 | margin: 0;
7 | padding: 0;
8 | white-space: nowrap;
9 | float: left; // animation: roll 10s infinite linear;
10 | // -webkit-animation: roll 10s infinite linear; // animation-delay:1s;
11 | animation-name: roll;
12 | -webkit-animation-name: roll;
13 | animation-iteration-count: infinite;
14 | -webkit-animation-iteration-count: infinite;
15 | animation-timing-function: linear;
16 | -webkit-animation-timing-function: linear; // animation-delay: 5s;
17 | // -webkit-animation-delay: 5s;
18 | &:hover {
19 | animation-play-state: paused;
20 | -webkit-animation-play-state: paused;
21 | cursor: progress;
22 | }
23 | }
24 | }
25 |
26 | @-webkit-keyframes roll {
27 | 0% {
28 | transform: translateX(1200px);
29 | -webkit-transform: translateX(1200px);
30 | }
31 | 100% {
32 | transform: translateX(-100%);
33 | -webkit-transform: translateX(-100%);
34 | }
35 | }
--------------------------------------------------------------------------------
/src/layouts/components/GlobalSearch/_.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | export const searchFilter = (value, data, names = [], pathtitles = []) => {
3 | if (value === undefined || value === null) return [];
4 | data = _.cloneDeep(data);
5 | data.forEach((item) => {
6 | const { title, children } = item;
7 | if (children && children.length > 0) {
8 | searchFilter(value, children, names, pathtitles.concat(title));
9 | } else if (title.indexOf(value) > -1) {
10 | names.push({ ...item, pathtitles: pathtitles.concat(title) });
11 | }
12 | });
13 | return names;
14 | };
15 | export const searchEqual = (value, data) => {
16 | data = _.cloneDeep(data);
17 | if (value === undefined || value === null || _.trim(value) === "") return {};
18 | value = _.trim(value);
19 | let res = {};
20 | for (const item of data) {
21 | const { key, children } = item;
22 | if (children && children.length > 0) {
23 | return searchFilter(value, children);
24 | } else if (key === value) {
25 | res = item;
26 | break;
27 | }
28 | }
29 | return res;
30 | };
--------------------------------------------------------------------------------
/src/layouts/components/GlobalSearch/index.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */
2 | /* stylelint-disable no-duplicate-selectors */
3 | /* stylelint-disable */
4 | /* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
5 | .text-overflow {
6 | white-space: nowrap;
7 | text-overflow: ellipsis;
8 | overflow: hidden;
9 | }
10 | .headerSearch {
11 | height: 64px;
12 | display: inline-block;
13 | }
14 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalSearch/index.less:
--------------------------------------------------------------------------------
1 | .headerSearch {
2 | height: @header-height;
3 | display: inline-block;
4 | }
--------------------------------------------------------------------------------
/src/layouts/components/GlobalUserCenter/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import React, { PureComponent } from 'react';
8 | import { formatMessage } from 'umi/locale';
9 | import { Menu, Icon, Spin } from 'antd';
10 | import classNames from 'classnames';
11 | import HeaderDropdown from '../HeaderDropdown';
12 | import styles from './index.less';
13 |
14 | export default class SelectLang extends PureComponent {
15 |
16 |
17 | render() {
18 | const { userInfo = {}, onSetting = () => { }, theme, className } = this.props;
19 | const { userName } = userInfo;
20 | const handleMenuClick = (param) => {
21 | onSetting(param);
22 | };
23 | const menu = ();
41 |
42 | return userName ? (
43 |
44 |
46 | {userName}
47 |
48 | )
49 | :
50 | ();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/layouts/components/GlobalUserCenter/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | .menu {
3 | :global(.anticon) {
4 | margin-right: 8px;
5 | } // :global(.ant-dropdown-menu-item) {
6 | // min-width: 160px;
7 | // }
8 | }
9 |
10 | .dropDown {
11 | line-height: @layout-header-height;
12 | vertical-align: top;
13 | cursor: pointer;
14 | >i {
15 | font-size: 16px !important;
16 | transform: none !important;
17 | svg {
18 | position: relative;
19 | top: -1px;
20 | }
21 | }
22 | }
23 |
24 | .dark {
25 | color: #ffffff;
26 | }
--------------------------------------------------------------------------------
/src/layouts/components/HeaderDropdown/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 菜单栏dropdown
6 | */
7 | import React, { PureComponent } from 'react';
8 | import { Dropdown } from 'antd';
9 | import classNames from 'classnames';
10 | import styles from './index.less';
11 |
12 | export default class HeaderDropdown extends PureComponent {
13 | render() {
14 | const { overlayClassName, ...props } = this.props;
15 | return (
16 |
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/layouts/components/HeaderDropdown/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .container > * {
4 | background-color: #fff;
5 | border-radius: 4px;
6 | box-shadow: @shadow-1-down;
7 | }
8 |
9 | @media screen and (max-width: @screen-xs) {
10 | .container {
11 | width: 100% !important;
12 | }
13 | .container > * {
14 | border-radius: 0 !important;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/layouts/components/Menus/_.js:
--------------------------------------------------------------------------------
1 | import { isArray, cloneDeep } from 'lodash';
2 | import memoizeOne from 'memoize-one';
3 | import isEqual from 'lodash/isEqual';
4 | const _queryKeysByPath = (pathname, menusData) => {
5 | if (typeof (pathname) !== "string" || (typeof (pathname) === "string" && pathname.indexOf("frame") > -1)) return {};
6 | const reg = /(^\/*)|(\/*$)/g;// 匹配字符串首尾斜杠
7 | const path = pathname.replace(reg, '');
8 | const searchMenu = (value, data, pathtitles = []) => {
9 | if (value === undefined || value === null) return {};
10 | data = cloneDeep(data);
11 | for (const item of data) {
12 | const { title, link, children } = item;
13 | if (children && children.length > 0) {
14 | return searchMenu(value, children, pathtitles.concat(title));
15 | } else if (value === link.replace(reg, '')) {
16 | return { ...item, pathtitles: pathtitles.concat(title) };
17 | }
18 | }
19 | };
20 | const result = searchMenu(path, menusData);
21 | return { key: undefined, ...result };
22 | };
23 | export const queryKeysByPath = memoizeOne(_queryKeysByPath, isEqual);
24 | const _testMenusData = (item, err) => {
25 | const { title, icon, key, link, url, children } = item;
26 | if (!(title && key)) {
27 | return err && err();
28 | }
29 | if (children) {
30 | if (isArray(children) && icon) {
31 | _testMenusData(children, err);
32 | } else {
33 | err();
34 | };
35 | } else {
36 | if (!(url || link)) {
37 | return err && err();
38 | }
39 | }
40 | }
41 | export const testMenusData = memoizeOne(_testMenusData, isEqual);
--------------------------------------------------------------------------------
/src/layouts/components/Notice/index.css:
--------------------------------------------------------------------------------
1 | .popover {
2 | width: 360px;
3 | }
4 | .popover :global(.ant-popover-inner-content) {
5 | padding: 0;
6 | }
7 | .noticeButton {
8 | cursor: pointer;
9 | display: inline-block;
10 | transition: all 0.3s;
11 | }
12 | .icon {
13 | font-size: 16px;
14 | vertical-align: middle;
15 | padding: 4px;
16 | }
17 | .tabs :global .ant-tabs-nav-scroll {
18 | text-align: center;
19 | }
20 | .tabs :global .ant-tabs-bar {
21 | margin-bottom: 4px;
22 | }
23 | .tabs .ant-tabs-nav .ant-tabs-tab {
24 | margin: 0 !important;
25 | }
26 | .more {
27 | text-align: center;
28 | margin-top: 4px;
29 | height: 28;
30 | line-height: 28px;
31 | cursor: pointer;
32 | }
33 |
--------------------------------------------------------------------------------
/src/layouts/components/Notice/index.less:
--------------------------------------------------------------------------------
1 | .popover {
2 | width: 360px;
3 | :global(.ant-popover-inner-content) {
4 | padding: 0;
5 | }
6 | }
7 |
8 | .noticeButton {
9 | cursor: pointer;
10 | display: inline-block;
11 | transition: all 0.3s;
12 | }
13 |
14 | .icon {
15 | font-size: 16px;
16 | vertical-align: middle;
17 | padding: 4px;
18 | }
19 |
20 | .tabs {
21 | :global {
22 | .ant-tabs-nav-scroll {
23 | text-align: center;
24 | }
25 | .ant-tabs-bar {
26 | margin-bottom: 4px;
27 | }
28 | }
29 | .ant-tabs-nav .ant-tabs-tab {
30 | margin: 0 !important;
31 | }
32 | }
33 |
34 | .more {
35 | text-align: center;
36 | margin-top: 4px;
37 | height: 28;
38 | line-height: 28px;
39 | cursor: pointer;
40 | }
--------------------------------------------------------------------------------
/src/layouts/components/SelectLang/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 语言选择组件
6 | */
7 | import React, { PureComponent } from 'react';
8 | import { formatMessage, setLocale, getLocale } from 'umi/locale';
9 | import { Menu, Icon } from 'antd';
10 | import classNames from 'classnames';
11 | import HeaderDropdown from '../HeaderDropdown';
12 | import styles from './index.less';
13 |
14 | export default class SelectLang extends PureComponent {
15 | changeLang = ({ key }) => {
16 | setLocale(key);
17 | };
18 |
19 | render() {
20 | const { className } = this.props;
21 | const selectedLang = getLocale();
22 | const locales = ['zh-CN', 'en-US'];
23 | const languageLabels = {
24 | 'zh-CN': '简体中文',
25 | 'zh-TW': '繁体中文',
26 | 'en-US': 'English',
27 | 'pt-BR': 'Português',
28 | };
29 | const languageIcons = {
30 | 'zh-CN': '🇨🇳',
31 | 'zh-TW': '🇭🇰',
32 | 'en-US': '🇬🇧',
33 | 'pt-BR': '🇧🇷',
34 | };
35 | const langMenu = (
36 |
46 | );
47 | return (
48 |
49 |
50 |
51 |
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/layouts/components/SelectLang/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 | .menu {
3 | :global(.anticon) {
4 | margin-right: 8px;
5 | }
6 | // :global(.ant-dropdown-menu-item) {
7 | // min-width: 160px;
8 | // }
9 | }
10 |
11 | .dropDown {
12 | line-height: @layout-header-height;
13 | vertical-align: top;
14 | cursor: pointer;
15 | >i {
16 | font-size: 16px !important;
17 | transform: none !important;
18 | svg {
19 | position: relative;
20 | top: -1px;
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/layouts/constant.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | export const query = {
8 | 'screen-xs': {
9 | maxWidth: 575,
10 | },
11 | 'screen-sm': {
12 | minWidth: 576,
13 | maxWidth: 767,
14 | },
15 | 'screen-md': {
16 | minWidth: 768,
17 | maxWidth: 991,
18 | },
19 | 'screen-lg': {
20 | minWidth: 992,
21 | maxWidth: 1199,
22 | },
23 | 'screen-xl': {
24 | minWidth: 1200,
25 | maxWidth: 1599,
26 | },
27 | 'screen-xxl': {
28 | minWidth: 1600,
29 | },
30 | };
--------------------------------------------------------------------------------
/src/layouts/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import BasicLayout from './basic';
8 | import PlatformLayout from './platform';
9 |
10 | function Index(props) {
11 | const { location, children } = props;
12 | const { pathname } = location;
13 | if (
14 | pathname === '/' ||
15 | pathname === '/login' ||
16 | pathname === '/register' ||
17 | /^\/initialize/.test(pathname) ||
18 | /^\/exception/.test(pathname)
19 | ) {
20 | return ({children});
21 | }
22 | return ({children});
23 | }
24 |
25 | export default Index;
26 |
--------------------------------------------------------------------------------
/src/layouts/platform/Footer/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 页脚组件
6 | */
7 | import { copyright } from '@platformConfig';
8 | import React, { Fragment } from 'react';
9 | import { Layout, Icon } from 'antd';
10 | import GlobalFooter from '../../components/GlobalFooter';
11 |
12 | const { Footer } = Layout;
13 | const FooterView = () => (
14 |
43 | );
44 | export default FooterView;
45 |
--------------------------------------------------------------------------------
/src/layouts/platform/Footer/index.less:
--------------------------------------------------------------------------------
1 |
2 | .footer {
3 | text-align: center;
4 | font-size: 14px;
5 | padding: 10px 50px;
6 | }
--------------------------------------------------------------------------------
/src/layouts/platform/header.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description header组件
6 | */
7 | import { routerRedux } from 'dva/router';
8 | import { connect } from 'dva';
9 | import { PureComponent } from 'react';
10 | import GlobalHeader from '../components/GlobalHeader';
11 | class Index extends PureComponent {
12 | handleLoadMore = () => {
13 | const { dispatch } = this.props;
14 | dispatch({
15 | type: 'global/getMessage',
16 | payload: {
17 | size: 10,
18 | },
19 | });
20 | }
21 | handleSetting = (param) => {
22 | const { dispatch } = this.props;
23 | const { key, item } = param;
24 | const { state } = item.props;
25 | if (key === 'logout') {
26 | dispatch({
27 | type: "global/logout",
28 | payload: {
29 | ...state,
30 | },
31 | });
32 | } else {
33 | dispatch(routerRedux.push({
34 | pathname: key,
35 | state,
36 | }));
37 | }
38 | }
39 | render() {
40 | const { userInfo, message, notification } = this.props;
41 | return (
42 |
43 |
50 |
51 | );
52 | }
53 | }
54 | export default connect(({ global: { userInfo, message, notification } }) => {
55 | return {
56 | userInfo,
57 | message,
58 | notification
59 | };
60 | })(Index);
--------------------------------------------------------------------------------
/src/layouts/platform/index.less:
--------------------------------------------------------------------------------
1 | @import '../../themes/index.less';
2 |
3 | .wrap {
4 | min-height: 100vh;
5 |
6 | /***侧边栏***/
7 | .sider {
8 | height: 100vh;
9 | position: fixed;
10 | left: 0;
11 | }
12 |
13 | /***系统主体部分***/
14 | .container {
15 | display: flex;
16 | flex-direction: column;
17 |
18 | /***系统头部***/
19 | .contentHeader {
20 | background: #fff;
21 | padding: 0;
22 | display: flex;
23 | justify-content: space-between
24 | }
25 |
26 | /***系统内容区域***/
27 | .content {
28 | flex: auto;
29 | margin: 10px 16px;
30 | margin-bottom: 0;
31 | display: flex;
32 | }
33 | }
34 | }
35 |
36 |
37 |
38 | .trigger {
39 | font-size: 18px;
40 | line-height: 64px;
41 | padding: 0 24px;
42 | cursor: pointer;
43 | transition: color .3s;
44 | }
45 |
46 | .trigger:hover {
47 | color: #1890ff;
48 | }
49 |
50 |
51 |
52 | .logo {
53 | height: 64px;
54 | background: #002140;
55 | padding: 10px 16px;
56 | }
57 |
58 | @keyframes fadeIn {
59 | 0% {
60 | opacity: 0;
61 | /*初始状态 透明度为0*/
62 | }
63 |
64 | 50% {
65 | opacity: 0.5;
66 | /*中间状态 透明度为0*/
67 | }
68 |
69 | 100% {
70 | opacity: 1;
71 | /*结尾状态 透明度为1*/
72 | }
73 | }
74 |
75 | @-webkit-keyframes fadeIn {
76 | 0% {
77 | opacity: 0;
78 | /*初始状态 透明度为0*/
79 | }
80 |
81 | 50% {
82 | opacity: 0.5;
83 | /*中间状态 透明度为0*/
84 | }
85 |
86 | 100% {
87 | opacity: 1;
88 | /*结尾状态 透明度为1*/
89 | }
90 | }
91 |
92 | .animation {
93 | /*动画名称*/
94 | animation-name: fadeIn;
95 | -webkit-animation-name: fadeIn;
96 | /*动画持续时间*/
97 | animation-duration: 0.6s;
98 | -webkit-animation-duration: 0.6s;
99 | /*动画次数*/
100 | animation-iteration-count: 1;
101 | -webkit-animation-iteration-count: 1;
102 | /*延迟时间*/
103 | animation-delay: 0s;
104 | -webkit-animation-delay: 0s;
105 | }
--------------------------------------------------------------------------------
/src/layouts/platform/logo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import React, { PureComponent } from 'react';
8 | import PropTypes from 'prop-types';
9 | import { Row, Col } from 'antd';
10 | import { sysName } from '@platformConfig';
11 | import styles from './index.less';
12 | import logo from '../../assets/logo.png';
13 | class Index extends PureComponent {
14 | render() {
15 | const { collapsed } = this.props;
16 | const imgLogo =
;
17 | let logoPage;
18 | if (collapsed) {
19 | logoPage = imgLogo;
20 | } else {
21 | logoPage = (
22 |
23 |
24 | {imgLogo}
25 |
26 |
27 |
28 | {sysName}
29 |
30 |
31 |
32 | );
33 | }
34 | return
35 | {logoPage}
36 |
37 | }
38 | }
39 |
40 | Index.propTypes = {
41 | collapsed: PropTypes.bool
42 | };
43 |
44 | export default Index;
45 |
--------------------------------------------------------------------------------
/src/layouts/platform/startedModal.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { Modal } from 'antd';
3 | export default class Started extends PureComponent {
4 | state = {
5 | visible: false
6 | }
7 | componentDidMount() {
8 | this.testStarted();
9 | }
10 | testStarted = () => {
11 | const started = localStorage.getItem('started');
12 | if (started !== 'true') {
13 | clearTimeout(this.timer);
14 | this.timer = setTimeout(() => {
15 | this.setState({
16 | visible: true
17 | });
18 | }, 6000)
19 | }
20 | }
21 | giveStar = () => {
22 | localStorage.setItem('started', true);
23 | this.setState({
24 | visible: false
25 | });
26 | window.location.href = 'https://github.com/mpw0311/antd-umi-sys';
27 | }
28 | handleCancel = () => {
29 | this.setState({
30 | visible: false
31 | });
32 | }
33 | render() {
34 | const { visible } = this.state;
35 | return (
36 |
43 | {/* eslint-disable-next-line */}
44 | 如果你喜欢这个项目请给一个⭐,谢谢!
45 | Please give me a Star if you like this project.Thank you so much.
46 |
47 | );
48 | }
49 | }
--------------------------------------------------------------------------------
/src/locales/en-US.js:
--------------------------------------------------------------------------------
1 | import platform from './en-US/platform';
2 | import login from './en-US/login';
3 | import gitDataV from './en-US/gitDataV';
4 | export default {
5 | 'navBar.lang': 'Languages',
6 | 'layout.user.link.help': 'Help',
7 | 'layout.user.link.privacy': 'Privacy',
8 | 'layout.user.link.terms': 'Terms',
9 | ...platform,
10 | ...login,
11 | ...gitDataV
12 | };
13 |
--------------------------------------------------------------------------------
/src/locales/en-US/form.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'form.get-captcha': 'Get Captcha',
3 | 'form.captcha.second': 'sec',
4 | 'form.optional': ' (optional) ',
5 | 'form.submit': 'Submit',
6 | 'form.save': 'Save',
7 | 'form.email.placeholder': 'Email',
8 | 'form.password.placeholder': 'Password',
9 | 'form.confirm-password.placeholder': 'Confirm password',
10 | 'form.phone-number.placeholder': 'Phone number',
11 | 'form.verification-code.placeholder': 'Verification code',
12 | 'form.title.label': 'Title',
13 | 'form.title.placeholder': 'Give the target a name',
14 | 'form.date.label': 'Start and end date',
15 | 'form.date.placeholder.start': 'Start date',
16 | 'form.date.placeholder.end': 'End date',
17 | 'form.goal.label': 'Goal description',
18 | 'form.goal.placeholder': 'Please enter your work goals',
19 | 'form.standard.label': 'Metrics',
20 | 'form.standard.placeholder': 'Please enter a metric',
21 | 'form.client.label': 'Client',
22 | 'form.client.label.tooltip': 'Target service object',
23 | 'form.client.placeholder':
24 | 'Please describe your customer service, internal customers directly @ Name / job number',
25 | 'form.invites.label': 'Inviting critics',
26 | 'form.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
27 | 'form.weight.label': 'Weight',
28 | 'form.weight.placeholder': 'Please enter weight',
29 | 'form.public.label': 'Target disclosure',
30 | 'form.public.label.help': 'Customers and invitees are shared by default',
31 | 'form.public.radio.public': 'Public',
32 | 'form.public.radio.partially-public': 'Partially public',
33 | 'form.public.radio.private': 'Private',
34 | 'form.publicUsers.placeholder': 'Open to',
35 | 'form.publicUsers.option.A': 'Colleague A',
36 | 'form.publicUsers.option.B': 'Colleague B',
37 | 'form.publicUsers.option.C': 'Colleague C',
38 | };
39 |
--------------------------------------------------------------------------------
/src/locales/en-US/gitDataV.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'gitDataV.desc': 'A GitHub data visualization platform built on react+umi,shows more intuitively views of some data you have on GitHub.',
3 | 'gitDataV.repositories': 'repositories',
4 | 'gitDataV.followers': 'followers',
5 | 'gitDataV.following': 'following',
6 | 'gitDataV.news': 'news',
7 | 'gitDataV.more': 'more',
8 | 'gitDataV.submit': 'submit',
9 | 'gitDataV.repos.overview': 'Top 10',
10 | 'gitDataV.repos.list': 'GitHub repositories list',
11 | 'gitDataV.stargazers.info': 'Top 20',
12 | 'gitDataV.stargazers.list': 'Stargazers list',
13 | }
--------------------------------------------------------------------------------
/src/locales/en-US/login.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'login.userName': 'userName',
3 | 'login.password': 'password',
4 | 'login.remember-me': 'Remember me',
5 | 'login.forgot-password': 'Forgot your password?',
6 | 'login.signup': 'Sign up',
7 | 'login.login': 'Login',
8 | 'register.register': 'Register',
9 | 'register.get-verification-code': 'Get code',
10 | 'register.sign-in': 'Already have an account?',
11 | 'validation.email.required': 'Please enter your email!',
12 | 'validation.email.wrong-format': 'The email address is in the wrong format!',
13 | 'validation.userName.required': 'Please enter your userName!',
14 | 'validation.password.required': 'Please enter your password!',
15 | 'validation.password.twice': 'The passwords entered twice do not match!',
16 | 'validation.password.strength.msg':
17 | "Please enter at least 6 characters and don't use passwords that are easy to guess.",
18 | 'validation.password.strength.strong': 'Strength: strong',
19 | 'validation.password.strength.medium': 'Strength: medium',
20 | 'validation.password.strength.short': 'Strength: too short',
21 | 'validation.confirm-password.required': 'Please confirm your password!',
22 | 'validation.phone-number.required': 'Please enter your phone number!',
23 | 'validation.phone-number.wrong-format': 'Malformed phone number!',
24 | 'validation.verification-code.required': 'Please enter the verification code!',
25 | 'validation.title.required': 'Please enter a title',
26 | 'validation.date.required': 'Please select the start and end date',
27 | 'validation.goal.required': 'Please enter a description of the goal',
28 | 'validation.standard.required': 'Please enter a metric',
29 | }
--------------------------------------------------------------------------------
/src/locales/en-US/platform.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'platform.userCenter': 'Account Center',
3 | 'platform.settings': 'Account Settings',
4 | 'platform.log': 'Change Log',
5 | 'platform.logout': 'Logout',
6 | }
--------------------------------------------------------------------------------
/src/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | import platform from './zh-CN/platform';
2 | import login from './zh-CN/login';
3 | import gitDataV from './zh-CN/gitDataV';
4 | export default {
5 | 'navBar.lang': '语言',
6 | 'layout.user.link.help': '帮助',
7 | 'layout.user.link.privacy': '隐私',
8 | 'layout.user.link.terms': '条款',
9 | ...platform,
10 | ...login,
11 | ...gitDataV
12 | };
13 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/form.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'form.get-captcha': '获取验证码',
3 | 'form.captcha.second': '秒',
4 | 'form.optional': '(选填)',
5 | 'form.submit': '提交',
6 | 'form.save': '保存',
7 | 'form.email.placeholder': '邮箱',
8 | 'form.password.placeholder': '至少6位密码,区分大小写',
9 | 'form.confirm-password.placeholder': '确认密码',
10 | 'form.phone-number.placeholder': '手机号',
11 | 'form.verification-code.placeholder': '验证码',
12 | 'form.title.label': '标题',
13 | 'form.title.placeholder': '给目标起个名字',
14 | 'form.date.label': '起止日期',
15 | 'form.date.placeholder.start': '开始日期',
16 | 'form.date.placeholder.end': '结束日期',
17 | 'form.goal.label': '目标描述',
18 | 'form.goal.placeholder': '请输入你的阶段性工作目标',
19 | 'form.standard.label': '衡量标准',
20 | 'form.standard.placeholder': '请输入衡量标准',
21 | 'form.client.label': '客户',
22 | 'form.client.label.tooltip': '目标的服务对象',
23 | 'form.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
24 | 'form.invites.label': '邀评人',
25 | 'form.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
26 | 'form.weight.label': '权重',
27 | 'form.weight.placeholder': '请输入',
28 | 'form.public.label': '目标公开',
29 | 'form.public.label.help': '客户、邀评人默认被分享',
30 | 'form.public.radio.public': '公开',
31 | 'form.public.radio.partially-public': '部分公开',
32 | 'form.public.radio.private': '不公开',
33 | 'form.publicUsers.placeholder': '公开给',
34 | 'form.publicUsers.option.A': '同事甲',
35 | 'form.publicUsers.option.B': '同事乙',
36 | 'form.publicUsers.option.C': '同事丙',
37 | };
38 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/gitDataV.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'gitDataV.desc': '一个用React+UmiJS构建的GitHub"数据可视化平台"。通过它,您可以更加直观看到您在GitHub里的一些数据情况',
3 | 'gitDataV.repositories': '公开仓库数',
4 | 'gitDataV.followers': '粉丝',
5 | 'gitDataV.following': '跟随',
6 | 'gitDataV.news': '最新消息',
7 | 'gitDataV.more': '更多',
8 | 'gitDataV.submit': '查询',
9 | 'gitDataV.repos.overview': 'GitHub仓库Top 10概览',
10 | 'gitDataV.repos.list': 'GitHub仓库列表',
11 | 'gitDataV.stargazers.info': 'Top 20',
12 | 'gitDataV.stargazers.list': 'Stargazers list',
13 |
14 | }
--------------------------------------------------------------------------------
/src/locales/zh-CN/login.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'login.userName': '用户名',
3 | 'login.password': '扫描二维码关注公众号获取密码',
4 | 'login.remember-me': '自动登录',
5 | 'login.forgot-password': '忘记密码',
6 | 'login.signup': '注册账户',
7 | 'login.login': '登录',
8 | 'register.register': '注册',
9 | 'register.get-verification-code': '获取验证码',
10 | 'register.sign-in': '使用已有账户登录',
11 | 'validation.email.required': '请输入邮箱地址!',
12 | 'validation.email.wrong-format': '邮箱地址格式错误!',
13 | 'validation.userName.required': '请输入用户名!',
14 | 'validation.password.required': '请输入密码!',
15 | 'validation.password.twice': '两次输入的密码不匹配!',
16 | 'validation.password.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
17 | 'validation.password.strength.strong': '强度:强',
18 | 'validation.password.strength.medium': '强度:中',
19 | 'validation.password.strength.short': '强度:太短',
20 | 'validation.confirm-password.required': '请确认密码!',
21 | 'validation.phone-number.required': '请输入手机号!',
22 | 'validation.phone-number.wrong-format': '手机号格式错误!',
23 | 'validation.verification-code.required': '请输入验证码!',
24 | 'validation.title.required': '请输入标题',
25 | 'validation.date.required': '请选择起止日期',
26 | 'validation.goal.required': '请输入目标描述',
27 | 'validation.standard.required': '请输入衡量标准',
28 | }
--------------------------------------------------------------------------------
/src/locales/zh-CN/platform.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'platform.userCenter':'用户中心',
3 | 'platform.settings':'设置',
4 | 'platform.log':'更新日志',
5 | 'platform.logout':'退出登录',
6 | }
--------------------------------------------------------------------------------
/src/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpw0311/antd-umi-sys/55ae3bab87d43dcc64e9471f1a9c8cb757cb303d/src/models/.gitkeep
--------------------------------------------------------------------------------
/src/models/global.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @author M
4 | * @email mpw0311@163.com
5 | * @version 1.0.0
6 | * @description 全局model
7 | */
8 | import { routerRedux } from 'dva/router';
9 | import { message } from 'antd';
10 | import * as api from '@services';
11 | export default {
12 | namespace: 'global',
13 | state: {
14 | userInfo: {},
15 | message: [],
16 | notification: undefined,
17 | },
18 | subscriptions: {
19 | // setupHistory({ dispatch, history }) {
20 | // history.listen((location) => {
21 | // const { pathname, query, state } = location;
22 | // if (/^\/sys/.test(pathname)) {
23 | // } else if (/^\/login/.test(pathname)) {
24 | // }
25 | // });
26 | // },
27 | },
28 |
29 | effects: {
30 | * logout({ payload }, { call, put }) {
31 | const { data, status } = yield call(api.logout, { ...payload });
32 | const { message: msg } = data;
33 | if (status === 0) {
34 | message.success(msg || "退出系统");
35 | sessionStorage.setItem('isLogin', false);
36 | yield put(routerRedux.push('/login'));
37 | }
38 | },
39 | * getSysInfo(_, { call, put }) {
40 | const { data = {}, status } = yield call(api.getSysInfo, {});
41 | if (status === 0) {
42 | const { userInfo = {}, notification } = data;
43 | yield put({
44 | type: 'save',
45 | payload: {
46 | userInfo,
47 | notification
48 | }
49 | });
50 | }
51 |
52 | },
53 | // 请求消息通知栏数据
54 | *getMessage({ payload = {} }, { call, put, select }) {
55 | const { size = 0 } = payload;
56 | let count = yield select(({ global }) => global.message.length);
57 | const { data = [] } = yield call(api.getMessage, { size: count + size });
58 | yield put({
59 | type: 'save',
60 | payload: {
61 | message: data,
62 | }
63 | });
64 | },
65 | },
66 |
67 | reducers: {
68 | save(state, action) {
69 | return { ...state, ...action.payload };
70 | },
71 | clear(state) {
72 | return {
73 | ...state,
74 | userInfo: {},
75 | message: [],
76 | notification: undefined,
77 | };
78 | }
79 | },
80 | };
--------------------------------------------------------------------------------
/src/models/menu.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @author M
4 | * @email mpw0311@163.com
5 | * @version 1.0.0
6 | * @description 菜单栏model
7 | */
8 | import * as api from '../services';
9 | import orginalData from '@menuConfig';
10 | import { munesFilter, flattenMenu } from '@utils/_';
11 | import { menuPermission } from '@platformConfig';
12 | export default {
13 | namespace: 'menu',
14 | state: {
15 | menusData: [],
16 | flattenMenuData: [],
17 | diffMenuData: [],
18 | },
19 | subscriptions: {
20 | setup({ dispatch, history }) { // eslint-disable-line
21 | },
22 | },
23 |
24 | effects: {
25 | *getMenuData(_, { call, put, select }) {
26 | let menusData = yield select(({ menu }) => menu.menuData);
27 | if (!(menusData && menusData.length > 0)) {
28 | const { data = [] } = yield call(api.getMenuData, {});
29 | const { menusData, diffMenuData } = munesFilter(orginalData, data, menuPermission);
30 | const flattenMenuData = flattenMenu(menusData);
31 | yield put({
32 | type: 'save',
33 | payload: {
34 | menusData,
35 | diffMenuData,
36 | flattenMenuData
37 | }
38 | });
39 | }
40 | }
41 | },
42 |
43 | reducers: {
44 | save(state, action) {
45 | return { ...state, ...action.payload };
46 | },
47 | clear(state) {
48 | return {
49 | ...state,
50 | menusData: [],
51 | flattenMenuData: [],
52 | diffMenuData: [],
53 | };
54 | }
55 | },
56 |
57 | };
58 |
--------------------------------------------------------------------------------
/src/pages/403.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Page, Exception } from '@components';
8 |
9 | export default function () {
10 | return (
11 |
15 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Page, Exception } from '@components';
8 |
9 | export default function () {
10 | return (
11 |
15 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/pages/500.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Page, Exception } from '@components';
8 |
9 | export default function () {
10 | return (
11 |
15 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/pages/__tests__/__mocks__/umi-plugin-locale.js:
--------------------------------------------------------------------------------
1 | export const formatMessage = () => 'Mock text';
2 |
--------------------------------------------------------------------------------
/src/pages/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | import Index from '..';
2 | import renderer from 'react-test-renderer';
3 |
4 | jest.mock('umi-plugin-locale');
5 |
6 | describe('Page: index', () => {
7 | it('Render correctly', () => {
8 | const wrapper = renderer.create();
9 | expect(wrapper.root.children.length).toBe(1);
10 | const outerLayer = wrapper.root.children[0];
11 | expect(outerLayer.type).toBe('div');
12 | expect(outerLayer.children.length).toBe(2);
13 | const getStartLink = outerLayer.findAllByProps({
14 | href: 'https://umijs.org/guide/getting-started.html',
15 | });
16 | expect(getStartLink.length).toBe(1);
17 | expect(getStartLink[0].children).toMatchObject(['Mock text']);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/pages/document.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | antd-umi-sys
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/pages/download/index.js:
--------------------------------------------------------------------------------
1 | import { Page } from '@components';
2 | import { Row, Col } from 'antd';
3 |
4 | export default function () {
5 | return (
6 |
10 |
11 |
12 | download……
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/pages/exception/$code.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Exception } from '@components';
8 | import pathToRegexp from 'path-to-regexp';
9 |
10 | export default function (props) {
11 | const { location: { pathname } } = props;
12 | const [, code] = pathToRegexp('/exception/:code').exec(pathname);
13 | return (
14 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/pages/frame/$key.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description iframe兼容第三方页面
6 | */
7 | import React, { Component } from "react";
8 | import { connect } from 'dva';
9 | import Redirect from 'umi/redirect';
10 | import { Page } from '@components';
11 | import { Spin } from 'antd';
12 | // import pathToRegexp from 'path-to-regexp';
13 | import styles from './index.less';
14 | const _filter = (menus, pathname) => {
15 | for (const item of menus) {
16 | if (item.link === pathname) {
17 | return item;
18 | }
19 | };
20 | return {};
21 | };
22 | class Index extends Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | loading: true
27 | };
28 | }
29 | componentDidUpdate(nextProps) {
30 | const { location: nextLocation } = nextProps;
31 | const { pathname: nextPathname } = nextLocation;
32 | const { location } = this.props;
33 | const { pathname } = location;
34 | const backTopDom = document.getElementById('backTop');
35 | backTopDom && (backTopDom.scrollTop = 0);
36 | if (pathname !== nextPathname) {
37 | this.setState({
38 | loading: true
39 | });
40 | }
41 | }
42 | handlerLoad = () => {
43 | const { loading } = this.state;
44 | if (loading) {
45 | this.setState({
46 | loading: false
47 | });
48 | }
49 | }
50 | render() {
51 | const { location, flattenMenuData } = this.props;
52 | const { pathname, state = {}, query = {} } = location;
53 | // const key = pathToRegexp('/frame/:key').exec(pathname)[1];
54 | const { h } = query;
55 | const { pathtitles, title = '', url } = state && state.url ? state : _filter(flattenMenuData, pathname);
56 | const { loading } = this.state;
57 | const frame = (
58 |
59 |
60 |
67 |
68 | );
69 | return url ? frame : ;
70 | }
71 | }
72 | function mapStateToProps({ menu: { flattenMenuData }, loading }) {
73 | return {
74 | flattenMenuData,
75 | loading: loading.global
76 | };
77 | }
78 | export default connect(mapStateToProps)(Index);
79 |
--------------------------------------------------------------------------------
/src/pages/frame/index.js:
--------------------------------------------------------------------------------
1 | import Redirect from 'umi/redirect';
2 | export default () => ;
--------------------------------------------------------------------------------
/src/pages/frame/index.less:
--------------------------------------------------------------------------------
1 | .modal {
2 | // border: 1px solid red;
3 | background-color: rgba(150, 150, 150, 0.25);
4 | position: absolute;
5 | top: 0;
6 | left: 0;
7 | bottom: 0;
8 | right: 0;
9 | z-index: 100;
10 | text-align: center;
11 | padding-top: 400px;
12 | // display: flex;
13 | // justify-content: center;
14 | // align-items: center;
15 | }
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import Redirect from 'umi/redirect';
2 | export default () => ;
--------------------------------------------------------------------------------
/src/pages/login/components/Login/index.less:
--------------------------------------------------------------------------------
1 | .login_form {
2 | width: 100%;
3 | max-width: 400px;
4 | }
5 | .login_form_forgot {
6 | float: right;
7 | }
8 | .login_form_button {
9 | width: 100%;
10 | }
11 | .color {
12 | color: rgba(0, 0, 0, 0.25);
13 | }
14 | .login_form_register {
15 | float: left;
16 | }
--------------------------------------------------------------------------------
/src/pages/login/components/Login/loginQrcode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Icon, Tooltip } from 'antd';
3 | import styles from './loginQrcode.less';
4 |
5 | export default props => {
6 | const { onClick = () => {} } = props;
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/src/pages/login/components/Login/loginQrcode.less:
--------------------------------------------------------------------------------
1 | .qrcode {
2 | display: inline-block;
3 | width: 40px;
4 | height: 40px;
5 | font-size: 28px;
6 | transform: rotate(-45deg);
7 | -webkit-transform: rotate(-45deg);
8 | overflow: hidden;
9 | margin-right: -20px;
10 | > i {
11 | cursor: pointer;
12 | transform: translateX(-25px) rotate(45deg);
13 | }
14 | }
--------------------------------------------------------------------------------
/src/pages/login/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description 登录页面
6 | */
7 | import { PureComponent } from 'react';
8 | import { connect } from 'dva';
9 | import Login from './components/Login';
10 | import { message, Row, Col } from 'antd';
11 | import { loginName } from '@platformConfig';
12 | import logo from '../../assets/logo_blue_1024.png';
13 | import styles from './index.less';
14 |
15 | @connect(
16 | ({ login, loading }) => ({
17 | ...login,
18 | loading: loading.global,
19 | })
20 | )
21 | class Index extends PureComponent {
22 | componentDidMount() {
23 | const { location: { query = {} } } = this.props;
24 | if (query.status === '1') {
25 | message.warning('用户未登录,请登录后访问!');
26 | }
27 | }
28 | handleSubmit = (err, values) => {
29 | for (const name in values) {
30 | if (values[name] === undefined) {
31 | message.error('用户名或密码错误!');
32 | return false;
33 | }
34 | }
35 | this.props.dispatch({
36 | type: 'login/login',
37 | payload: {
38 | ...values,
39 | },
40 | });
41 | };
42 | render() {
43 | const { loading, isError } = this.props;
44 | return (
45 |
46 |
47 |
48 |
49 |
50 |
51 |
{loginName}
52 |
53 |
54 | );
55 | }
56 | }
57 | export default Index;
--------------------------------------------------------------------------------
/src/pages/login/index.less:
--------------------------------------------------------------------------------
1 | .content {
2 | width: 300px;
3 | }
4 |
5 | .logo {
6 | min-height: 60px;
7 | img {
8 | width: 100%;
9 | }
10 | }
11 |
12 | .title {
13 | text-align: center;
14 | }
--------------------------------------------------------------------------------
/src/pages/login/model.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import * as api from './service';
8 | import { notification } from 'antd';
9 | import { routerRedux } from 'dva/router';
10 | import { encrypt } from '@utils/CryptoJS';
11 |
12 | export default {
13 | namespace: 'login',
14 | state: {
15 | isError: false
16 | },
17 | effects: {
18 | *login({ payload }, { call, put }) {
19 | const { password, ...rest } = payload;
20 | const { status } = yield call(api.login, { password: encrypt(password), ...rest });
21 | if (status === 0) {
22 | sessionStorage.setItem("isLogin", true);
23 | yield put(routerRedux.push('/sys'));
24 | } else {
25 | yield put({
26 | type: 'save',
27 | payload: {
28 | isError: true
29 | }
30 | });
31 | notification.error({
32 | message: '用户名或密码错误,请重新登录。',
33 | });
34 | }
35 | },
36 | },
37 |
38 | reducers: {
39 | save(state, action) {
40 | return { ...state, ...action.payload };
41 | },
42 | },
43 |
44 | };
45 |
--------------------------------------------------------------------------------
/src/pages/login/service.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 |
3 | export function login(payload) {
4 | return request('/login', {
5 | method: 'POST',
6 | data: {
7 | ...payload,
8 | }
9 | });
10 | }
--------------------------------------------------------------------------------
/src/pages/register/index.css:
--------------------------------------------------------------------------------
1 |
2 | .normal {
3 | background: #F2B279;
4 | }
5 |
--------------------------------------------------------------------------------
/src/pages/register/index.js:
--------------------------------------------------------------------------------
1 | import { Register } from '@components';
2 | // import styles from './index.css';
3 |
4 | export default function () {
5 | return (
6 |
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/src/pages/resetPassword/index.css:
--------------------------------------------------------------------------------
1 |
2 | .normal {
3 | background: #F27987;
4 | }
5 |
--------------------------------------------------------------------------------
/src/pages/resetPassword/index.js:
--------------------------------------------------------------------------------
1 | import { Page, ResetPassword } from '@components';
2 | import { Row, Col } from 'antd';
3 | // import styles from './index.css';
4 |
5 | export default function () {
6 | return (
7 |
11 |
12 |
13 |
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/pages/sys/echarts/area.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Area } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, area_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/bar.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Bar } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, bar_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/components/option.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { Tabs, Icon, Input, message } from 'antd';
3 | import Form from './form';
4 | import styles from './option.less';
5 |
6 | const { TabPane } = Tabs;
7 | const { TextArea } = Input;
8 | export default class MyTabs extends PureComponent {
9 | static defaultProps = {
10 | rows: 24,
11 | onBlur: () => { },
12 | onChange: () => { },
13 | }
14 | render() {
15 | const { data, onBlur, onChange, rows, type, ...rest } = this.props;
16 | const handleBlur = (e) => {
17 | const { value } = e.target;
18 | const res = value.replace(/{/g, "{")
19 | .replace(/}/g, "}")
20 | .replace(/:/g, ":")
21 | .replace(/,/g, ",")
22 | .replace(/;/g, ";")
23 | .replace(/“/g, "\"")
24 | .replace(/”/g, "\"")
25 | .replace(/‘/g, "\"")
26 | .replace(/’/g, "\"");
27 | let data = {};
28 | try {
29 | /*eslint-disable-next-line*/
30 | data = eval(`(${res})`);
31 | } catch (err) {
32 | console.log("err", err);
33 | message.error('数据格式有误!');
34 | }
35 | onBlur(data);
36 | };
37 |
38 | const tab = (e) => {
39 | const { keyCode, target } = e;
40 | const { selectionStart, selectionEnd, value } = target;
41 | if (keyCode === 9) {
42 | const postion = selectionStart + 4;
43 | target.value = `${value.substr(0, selectionStart)} ${value.substr(selectionEnd)}`;
44 | target.selectionStart = postion;
45 | target.selectionEnd = postion;
46 | e.preventDefault();
47 | }
48 | };
49 | return (
50 |
51 | OPTION} key="1">
52 |
53 |
54 | DATA} key="2">
55 |
56 |
57 |
58 | );
59 | }
60 | }
--------------------------------------------------------------------------------
/src/pages/sys/echarts/components/option.less:
--------------------------------------------------------------------------------
1 | .TextArea {
2 | background-color: #1E1E1E;
3 | color: #1A8CC6;
4 | }
--------------------------------------------------------------------------------
/src/pages/sys/echarts/components/panel.js:
--------------------------------------------------------------------------------
1 | import { PureComponent, cloneElement } from 'react';
2 | import Option from './option';
3 | import { Row, Col } from 'antd';
4 |
5 | export default class extends PureComponent {
6 | constructor(props) {
7 | super(props);
8 | this.state = {};
9 | }
10 | static getDerivedStateFromProps(nextProps, prevState) {
11 | const { children: { props: { data } } } = nextProps;
12 | if (prevState.data === undefined) {
13 | return {
14 | data
15 | };
16 | } else {
17 | return prevState;
18 | }
19 | }
20 | render() {
21 | const { children, type } = this.props;
22 | const { data } = this.state;
23 | const handleBlur = (data) => {
24 | this.setState({
25 | data
26 | });
27 | };
28 | const handleChange = (values) => {
29 | this.setState({
30 | ...values
31 | });
32 | };
33 | return (
34 |
35 |
36 |
37 |
38 |
39 | {cloneElement(children, { ...this.state })}
40 |
41 |
42 | );
43 | }
44 | }
--------------------------------------------------------------------------------
/src/pages/sys/echarts/funnel.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Funnel } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, funnel_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/index.js:
--------------------------------------------------------------------------------
1 | import Redirect from 'umi/redirect';
2 | export default () => ;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/line.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Line } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, line_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/model.js:
--------------------------------------------------------------------------------
1 |
2 | import data1 from './services/data01.json';
3 | import data2 from './services/data02.json';
4 | import data3 from './services/data03.json';
5 | export default {
6 | namespace: 'echarts',
7 | state: {
8 | bar_data: data1,
9 | line_data: data1,
10 | area_data: data1,
11 | yBar_data: data1,
12 | funnel_data: data2,
13 | pie_data: data2,
14 | pieDoughnut_data: data2,
15 | sankey_data: data3,
16 | description: '本Demo仅作为参考,只展示少数Echarts图形及属性,仍有许多需要完善和修改的地方,后期有时间会慢慢完善更新!'
17 | },
18 | subscriptions: {
19 |
20 | },
21 |
22 | effects: {
23 |
24 | },
25 |
26 | reducers: {
27 | save(state, action) {
28 | return { ...state, ...action.payload };
29 | },
30 | },
31 |
32 | };
33 |
--------------------------------------------------------------------------------
/src/pages/sys/echarts/pie.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Pie } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, pie_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/pieDoughnut.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { PieDoughnut } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, pieDoughnut_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/sankey.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Sankey } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, sankey_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/echarts/services/data01.json:
--------------------------------------------------------------------------------
1 | {
2 | "columns": [
3 | {
4 | "field": "product",
5 | "name": "分类",
6 | "type": "string"
7 | },
8 | {
9 | "field": "2015",
10 | "name": "2015",
11 | "type": "number"
12 | },
13 | {
14 | "field": "2016",
15 | "name": "2016",
16 | "type": "number"
17 | },
18 | {
19 | "field": "2017",
20 | "name": "2017",
21 | "type": "number"
22 | }
23 | ],
24 | "rows": [
25 | {
26 | "product": "Matcha Latte",
27 | "2015": 43.3,
28 | "2016": 185.8,
29 | "2017": 293.7
30 | },
31 | {
32 | "product": "Milk Tea",
33 | "2015": 83.1,
34 | "2016": 173.4,
35 | "2017": 255.1
36 | },
37 | {
38 | "product": "Cheese Cocoa",
39 | "2015": 86.4,
40 | "2016": 165.2,
41 | "2017": 282.5
42 | },
43 | {
44 | "product": "Walnut Brownie",
45 | "2015": 72.4,
46 | "2016": 153.9,
47 | "2017": 239.1
48 | }
49 | ]
50 | }
--------------------------------------------------------------------------------
/src/pages/sys/echarts/services/data02.json:
--------------------------------------------------------------------------------
1 | {
2 | "columns": [
3 | {
4 | "field": "name",
5 | "name": "分类",
6 | "type": "string"
7 | },
8 | {
9 | "field": "value",
10 | "name": "2015",
11 | "type": "number"
12 | }
13 | ],
14 | "rows": [
15 | {
16 | "name": "Matcha Latte",
17 | "value": 43.3
18 | },
19 | {
20 | "name": "Milk Tea",
21 | "value": 83.1
22 | },
23 | {
24 | "name": "Cheese Cocoa",
25 | "value": 86.4
26 | },
27 | {
28 | "name": "Walnut Brownie",
29 | "value": 72.4
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/src/pages/sys/echarts/services/data03.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "name": "a"
5 | },
6 | {
7 | "name": "b"
8 | },
9 | {
10 | "name": "a1"
11 | },
12 | {
13 | "name": "a2"
14 | },
15 | {
16 | "name": "b1"
17 | },
18 | {
19 | "name": "c"
20 | }
21 | ],
22 | "links": [
23 | {
24 | "source": "a",
25 | "target": "a1",
26 | "value": 5
27 | },
28 | {
29 | "source": "a",
30 | "target": "a2",
31 | "value": 3
32 | },
33 | {
34 | "source": "b",
35 | "target": "b1",
36 | "value": 8
37 | },
38 | {
39 | "source": "a",
40 | "target": "b1",
41 | "value": 3
42 | },
43 | {
44 | "source": "b1",
45 | "target": "a1",
46 | "value": 1
47 | },
48 | {
49 | "source": "b1",
50 | "target": "c",
51 | "value": 2
52 | }
53 | ]
54 | }
--------------------------------------------------------------------------------
/src/pages/sys/echarts/yBar.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { YBar } from '@components/Echarts';
4 | import PageHeader from '@components/PageWrapper';
5 | import Panel from './components/panel';
6 | @connect(({ echarts }) => echarts)
7 | class Chart extends PureComponent {
8 | render() {
9 | const { description, yBar_data } = this.props;
10 | return (
11 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 | export default Chart;
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/$id/index.css:
--------------------------------------------------------------------------------
1 | .normal {
2 | /* background: #79F293; */
3 | /* height: 1000px; */
4 | /* border: 1px solid #79F293; */
5 | }
6 |
7 | .chart {
8 | width: 100%;
9 | height: 400px;
10 | }
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/$id/index.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Card } from 'antd';
4 | import pathToRegexp from 'path-to-regexp';
5 | import { Page } from '@components';
6 | import { Line } from '@components/Echarts';
7 | @connect(({ githubPro, loading }) => {
8 | const { accountInfo, starHistory, description } = githubPro;
9 | return {
10 | accountInfo,
11 | starHistory,
12 | description,
13 | loading: loading.models.githubPro,
14 | };
15 | })
16 | class Repo extends PureComponent {
17 | componentDidMount() {
18 | const { account, reposName } = this.getInfo();
19 | //stargazers Analysis
20 | this.getRepoStars(account, reposName);
21 | }
22 | componentDidUpdate(nextProps, nextState) {
23 | const { account: preAccount, reposName: preReposName } = this.getInfo();
24 | const { account, reposName } = this.getInfo(nextProps);
25 | if (account && reposName && (account !== preAccount || reposName !== preReposName)) {
26 | //stargazers Analysis
27 | this.getRepoStars(account, reposName);
28 | }
29 | }
30 | // 获取账户名、项目名
31 | getInfo = (props) => {
32 | const { location: { pathname, query: { _n: reposName } } } = props || this.props;
33 | const [, account] = pathToRegexp('/sys/githubpro/:id').exec(pathname);
34 | return {
35 | reposName,
36 | account
37 | };
38 | }
39 | /**
40 | * 数据请求
41 | */
42 | getRepoStars = (account, repoName) => {
43 | this.props.dispatch({
44 | type: 'githubPro/getReposStars',
45 | payload: {
46 | account,
47 | repoName,
48 | },
49 | });
50 | };
51 | render() {
52 | const { starHistory, loading, description } = this.props;
53 | const { account, reposName } = this.getInfo();
54 | return (
55 |
69 |
70 |
71 |
72 |
73 | );
74 | }
75 | }
76 | export default Repo;
77 |
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/components/account.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { Form, Icon, Input, Button } from 'antd';
3 | import { formatMessage } from 'umi/locale';
4 |
5 | class Index extends PureComponent {
6 | handleSubmit = (e) => {
7 | e.preventDefault();
8 | const { form, onSubmit } = this.props;
9 | form.validateFields((err, values) => {
10 | if (!err) {
11 | console.log('Received values of form: ', values);
12 | onSubmit && onSubmit(values);
13 | }
14 | });
15 | }
16 | render() {
17 | const { form, value: defaultValue } = this.props;
18 | const {
19 | getFieldDecorator,
20 | } = form;
21 | return (
22 |
25 | {getFieldDecorator('account', {
26 | initialValue: defaultValue,
27 | rules: [{ required: true, message: 'Please input your account!' }],
28 | })(
29 | } placeholder="Account" />
30 | )}
31 |
32 |
33 |
39 |
40 |
41 | );
42 | }
43 | }
44 | export default Form.create({ name: 'account' })(Index);
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/components/dataTable.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { Table, Pagination } from 'antd';
3 | import { methods } from '@utils';
4 | import styles from './table.less'
5 | const { toTableData } = methods;
6 | export default class DataTable extends PureComponent {
7 | static defaultProps = {
8 | pagination: {
9 | current: 1,
10 | pageSize: 10,
11 | total: 1
12 | }
13 | }
14 | /**
15 | * 切换分页事件
16 | * @param {object} pagination 分页消息
17 | * @param {object} filters
18 | * @param {} sorter
19 | */
20 | onChange = (current) => {
21 | const { onChange } = this.props;
22 | typeof onChange === 'function' && onChange({ current })
23 | }
24 | onShowSizeChange = (current, pageSize) => {
25 | const { onChange } = this.props;
26 | typeof onChange === 'function' && onChange({ current, pageSize })
27 | }
28 | render() {
29 | const { pagination: { current, pageSize, total }, loading, data } = this.props;
30 | // 转换table数据
31 | const { columns, dataSource } = toTableData(data);
32 | return (
33 |
56 | );
57 | }
58 | }
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/components/table.less:
--------------------------------------------------------------------------------
1 | .wrap {
2 | .pagination {
3 | text-align: right;
4 | padding: 16px 20px;
5 | }
6 | }
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/index.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { formatMessage } from 'umi/locale';
4 | import { Page } from '@components';
5 | import Header from './components/header';
6 | import Content from './components/content';
7 | import FooterTable from './components/footerTable';
8 |
9 | class Index extends PureComponent {
10 |
11 | render() {
12 | return (
13 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | export default connect(({ githubPro, loading }) => {
31 | return {
32 | loading: loading.models.github
33 | };
34 | })(Index);
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/index.less:
--------------------------------------------------------------------------------
1 | .a {
2 | color: rgba(0, 0, 0, 0.65);
3 | transition: all 0.3s;
4 | }
--------------------------------------------------------------------------------
/src/pages/sys/githubpro/service.js:
--------------------------------------------------------------------------------
1 | import { request } from "@http";
2 | import getStarHistory from './components/getStarHistory';
3 | //获取账户信息api
4 | export const getAccountInfo = async function ({ account }) {
5 | return await request(`https://api.github.com/users/${account}`);
6 | };
7 |
8 | export const getData = async function ({ url }) {
9 | return await request(url);
10 | }
11 | // 获取历史stars趋势图数据
12 | export const getReposStargazers = async function (payload) {
13 | const { gitname } = payload;
14 | if (!gitname) {
15 | throw new Error("getReposStargazers:gitname参数为空");
16 | }
17 | return await getStarHistory(gitname)
18 | .catch(err => {
19 | console.log(err);
20 | });;
21 | };
--------------------------------------------------------------------------------
/src/pages/sys/index.js:
--------------------------------------------------------------------------------
1 | import Redirect from 'umi/redirect';
2 | import {sysDefultPage} from '@platformConfig'
3 | // export default () => ;
4 | export default () => ;
--------------------------------------------------------------------------------
/src/pages/sys/pathAnalysis/index.less:
--------------------------------------------------------------------------------
1 | .content {
2 | padding: 0 10px;
3 | padding-top: 10px;
4 | }
5 |
6 | .tabContent {
7 | position: relative;
8 | .btn {
9 | z-index: 200;
10 | position: absolute;
11 | right: 15px;
12 | top: 45px;
13 | text-align: right;
14 | }
15 | }
16 |
17 | .pop {
18 | position: absolute;
19 | background-color: rgba(150, 150, 150, 0.6);
20 | width: 100px;
21 | ul,
22 | li {
23 | list-style: none;
24 | padding: 5px;
25 | margin: 0;
26 | }
27 | ul{
28 | margin-top: 4px;
29 | }
30 | li {
31 | height: 25px;
32 | line-height: 25px;
33 | color: #fefefe;
34 | overflow: hidden;
35 | text-overflow: ellipsis;
36 | white-space: nowrap;
37 | }
38 | border-radius: 6px;
39 | }
40 |
41 | .close {
42 | cursor: pointer;
43 | margin: 2px;
44 | z-index: 10;
45 | display: inline-block;
46 | width: 20px;
47 | height: 20px;
48 | line-height: 18px;
49 | text-align: center;
50 | font-size: 18px;
51 | color: red;
52 | position: absolute;
53 | right: 0px;
54 | border: 1px solid #eeeeee;
55 | background-color: #efefef;
56 | border-radius: 50%;
57 | }
--------------------------------------------------------------------------------
/src/pages/sys/pathAnalysis/service.js:
--------------------------------------------------------------------------------
1 | import {request} from '@utils';
2 |
3 | export function fetch(payload) {
4 | return request(`/getPath`, {
5 | method: 'POST',
6 | body: JSON.stringify({
7 | ...payload
8 | }),
9 | });
10 | }
11 | export function getInfoTypeDict(payload) {
12 | return request(`/getPathDict`, {
13 | method: 'POST',
14 | body: JSON.stringify({
15 | ...payload
16 | }),
17 | });
18 | }
--------------------------------------------------------------------------------
/src/pages/sys/regionalAnalysis/index.css:
--------------------------------------------------------------------------------
1 | .list {
2 | position: absolute;
3 | top: 120px;
4 | left: 20px;
5 | min-width: 220px;
6 | background-color: rgba(250, 250, 250, 0.8);
7 | }
8 |
--------------------------------------------------------------------------------
/src/pages/sys/regionalAnalysis/index.less:
--------------------------------------------------------------------------------
1 | .list {
2 | position: absolute;
3 | top: 120px;
4 | left: 20px;
5 | min-width: 220px;
6 | background-color: rgba(250, 250, 250, 0.8)
7 | }
--------------------------------------------------------------------------------
/src/pages/sys/regionalAnalysis/service.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 |
3 | export function fetch(payload) {
4 | return request(`/getData`, {
5 | method: 'POST',
6 | body: JSON.stringify({
7 | ...payload
8 | }),
9 | });
10 | }
--------------------------------------------------------------------------------
/src/pages/sys/sankeyPage/index.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Page } from '@components';
4 |
5 | import Sankey from '@/components/D3Chart/Sankey';
6 |
7 | class SankeyChart extends PureComponent {
8 |
9 | render() {
10 | const { dataset, dispatch, loading } = this.props;
11 | //eslint-disable-next-line
12 | const handleClick = (name) => {
13 | dispatch({
14 | type: 'dimensional/getData',
15 | payload: {
16 | name
17 | }
18 | });
19 | };
20 | const getData = (dd) => {
21 | const { nodes, links } = dd;
22 | const Nodes = nodes.map(item => item.name);
23 | const Links = links.map(item => {
24 | const { source, target, ...rest } = item;
25 |
26 | return {
27 | source: Nodes.indexOf(source),
28 | target: Nodes.indexOf(target),
29 | ...rest
30 | };
31 | });
32 | return {
33 | nodes,
34 | links: Links
35 | };
36 | };
37 | return (
38 |
39 |
44 |
45 | );
46 | }
47 | }
48 |
49 | export default connect(({ sankeyModel }) => {
50 | return {
51 | ...sankeyModel
52 | };
53 | })(SankeyChart);
--------------------------------------------------------------------------------
/src/pages/sys/treePage/index.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Page } from '@components';
4 |
5 | import Tree from '@/components/D3Chart/Tree';
6 |
7 | class Sankey extends PureComponent {
8 |
9 | render() {
10 | const { dataset, dispatch, loading } = this.props;
11 | const handleClick = (name) => {
12 | dispatch({
13 | type: 'dimensional/getData',
14 | payload: {
15 | name
16 | }
17 | });
18 | };
19 | return (
20 |
21 | {
24 | const { data: { name } } = d;
25 | handleClick(name);
26 | }}
27 | maxDepth={5}
28 | siderbarClick={(d) => {
29 | console.log(d);
30 | }}
31 | routerClick={(d) => {
32 | const { value } = d;
33 | handleClick(value);
34 | }}
35 | />
36 |
37 | );
38 | }
39 | }
40 |
41 | export default connect(({ dimensional }) => {
42 | return {
43 | ...dimensional
44 | };
45 | })(Sankey);
--------------------------------------------------------------------------------
/src/pages/sys/treePage/service.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 | // import { mock } from 'mockjs';
3 | // const getdata = () => {
4 | // return mock({
5 | // 'time': '220ms',
6 | // 'children|4-6': [
7 | // {
8 | // 'name': '@name',
9 | // 'value|500-1000': 600,
10 | // 'time|1-200': 10,
11 | // },
12 | // ],
13 | // });
14 | // };
15 | export function fetch(payload) {
16 | // const { name } = payload;
17 | return request(`/api/treePage`, {
18 | method: 'POST',
19 | body: JSON.stringify({
20 | ...payload
21 | }),
22 | });
23 | // return {
24 | // data: {
25 | // name,
26 | // ...getdata()
27 | // }
28 | // };
29 | };
--------------------------------------------------------------------------------
/src/pages/sys/users/$id/index.css:
--------------------------------------------------------------------------------
1 |
2 | .normal {
3 | /* background: #79F293; */
4 | /* height: 1000px; */
5 | border:1px solid #79F293;
6 | }
7 |
--------------------------------------------------------------------------------
/src/pages/sys/users/$id/index.js:
--------------------------------------------------------------------------------
1 | import { Page } from '@components';
2 | import pathToRegexp from 'path-to-regexp';
3 | import styles from './index.css';
4 |
5 | export default function (props) {
6 | const { location } = props;
7 | const { pathname } = location;
8 | const match = pathToRegexp('/sys/users/:id').exec(pathname);
9 | const userName = match[1];
10 | return (
11 |
16 |
17 |
{userName}
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/pages/sys/users/components/Modal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Modal, Form, Input } from 'antd';
3 |
4 | const FormItem = Form.Item;
5 | class UserEditModal extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | visible: false,
10 | };
11 | }
12 | showModalHandler = (e) => {
13 | if (e) e.preventDefault();
14 | this.setState({
15 | visible: true,
16 | });
17 | }
18 | hideModalHandler = () => {
19 | this.setState({
20 | visible: false,
21 | });
22 | }
23 | okHandler = () => {
24 | const { onOk } = this.props;
25 | this.props.form.validateFields((err, values) => {
26 | if (!err) {
27 | onOk(values);
28 | this.hideModalHandler();
29 | }
30 | });
31 | }
32 | render() {
33 | const { children, form, record } = this.props;
34 | const { getFieldDecorator } = form;
35 | const { name, email, website } = record;
36 | const formItemLayout = {
37 | labelCol: { span: 6 },
38 | wrapperCol: { span: 14 },
39 | };
40 |
41 | return (
42 |
43 |
44 | {children}
45 |
46 |
52 |
84 |
85 |
86 | );
87 | }
88 | }
89 | export default Form.create()(UserEditModal);
90 |
--------------------------------------------------------------------------------
/src/pages/sys/users/index.css:
--------------------------------------------------------------------------------
1 | .operation a {
2 | margin: 0 .5em;
3 | }
4 | .create{
5 | margin-bottom: 1.5em;
6 | }
--------------------------------------------------------------------------------
/src/pages/sys/users/model.js:
--------------------------------------------------------------------------------
1 |
2 | import * as api from './service';
3 | export default {
4 | namespace: 'users',
5 | state: {
6 | path: undefined,
7 | list: [],
8 | total: null,
9 | page: null,
10 | },
11 | subscriptions: {
12 | setupHistory({ dispatch, history }) {
13 | history.listen(({ pathname, query, state }) => { // eslint-disable-line
14 | if (pathname === '/sys/users') {
15 | dispatch({ type: 'fetch', payload: query });
16 | }
17 | });
18 | },
19 | },
20 |
21 | effects: {
22 | *fetch({ payload: { page = 1 } }, { call, put }) {
23 | const result = yield call(api.fetch, { page });
24 | const { data: list, } = result;
25 | yield put({
26 | type: 'save',
27 | payload: {
28 | list,
29 | total: 10,
30 | page: parseInt(page, 10),
31 | },
32 | });
33 | },
34 | *removeuser({ payload: id }, { call, put }) {
35 | yield call(api.remove, id);
36 | yield put({ type: 'reload' });
37 | },
38 | *patch({ payload: { id, values } }, { call, put }) {
39 | yield call(api.patch, id, values);
40 | yield put({ type: 'reload' });
41 | },
42 | *create({ payload: values }, { call, put }) {
43 | yield call(api.create, values);
44 | yield put({ type: 'reload' });
45 | },
46 | *reload(action, { put, select }) {
47 | const page = yield select(state => state.users.page);
48 | yield put({ type: 'fetch', payload: { page } });
49 | },
50 | },
51 |
52 | reducers: {
53 | save(state, action) {
54 | return { ...state, ...action.payload };
55 | },
56 | },
57 |
58 | };
59 |
--------------------------------------------------------------------------------
/src/pages/sys/users/service.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 |
3 | export function fetch({ page }) {
4 | return request(`/api/users?_page=${page}&_limit=${10}`, {
5 | method: 'GET',
6 | });
7 | }
8 | export function remove(id) {
9 | return request(`/api/users/${id}`, {
10 | method: 'DELETE',
11 | });
12 | }
13 | export function patch(id, values) {
14 | return request(`/api/users/${id}`, {
15 | method: 'PATCH',
16 | body: JSON.stringify(values),
17 | });
18 | }
19 | export function create(values) {
20 | return request('/api/users', {
21 | method: 'POST',
22 | body: JSON.stringify(values),
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/src/pages/sys/view/components/view.js:
--------------------------------------------------------------------------------
1 | import { Fragment, PureComponent } from 'react';
2 | import { DataTable } from '@components';
3 | import { Bar, Line } from '@components/Echarts';
4 | import { Icon, Tabs } from 'antd';
5 | import DatePickers from './DateDickers';
6 |
7 | const TabPane = Tabs.TabPane;
8 | class Index extends PureComponent {
9 | render() {
10 | const { data, loading,
11 | handleClick = () => {
12 | console.log("download");
13 | },
14 | handleSubmit = () => { }
15 | } = this.props;
16 | return (
17 |
18 | { handleSubmit(times); }} />
19 |
23 | } key="1" style={{ textAlign: 'left' }}>
24 |
25 |
26 | } key="2" style={{ textAlign: 'left' }}>
27 |
28 |
29 |
30 |
38 |
39 | );
40 | }
41 | }
42 | export default Index;
--------------------------------------------------------------------------------
/src/pages/sys/view/components/view1.js:
--------------------------------------------------------------------------------
1 | import { Fragment, PureComponent } from 'react';
2 | import { DataTable } from '@components';
3 | import { Line } from '@components/Echarts';
4 | import { Row, Col, DatePicker, Button, Card } from 'antd';
5 | import moment from 'moment';
6 |
7 | const { RangePicker } = DatePicker;
8 | const dateFormat = 'YYYY/MM/DD';
9 | const initTime = [moment().subtract(7, 'days'), moment().subtract(1, 'days')];
10 | class Index extends PureComponent {
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | date: initTime
15 | };
16 | }
17 | onChange = (v) => {
18 | this.setState({
19 | date: v
20 | });
21 | }
22 | submit = () => {
23 | const { handleSubmit = () => { } } = this.props;
24 | const { date } = this.state;
25 | const times = date.map(time => moment(time).format(dateFormat));
26 | handleSubmit(times);
27 | }
28 | render() {
29 | const { data, showY2, Y2Name, YName, loading,
30 | handleClick = () => {
31 | console.log("download");
32 | }
33 | } = this.props;
34 | return (
35 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
54 |
61 |
62 | );
63 | }
64 | }
65 | export default Index;
--------------------------------------------------------------------------------
/src/pages/sys/view/index.js:
--------------------------------------------------------------------------------
1 | import Redirect from 'umi/redirect';
2 | export default () => ;
--------------------------------------------------------------------------------
/src/pages/sys/view/p1.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { connect } from 'dva';
3 | import { Page } from '@components';
4 | import View from './components/view1.js';
5 |
6 | class Index extends PureComponent {
7 | render() {
8 | const { loading, p1, dispatch } = this.props;
9 | const onSubmit = (times) => {
10 | dispatch({
11 | type: 'viewModel/getData',
12 | payload: {
13 | time: times,
14 | key: "p1"
15 | }
16 | });
17 | };
18 | return (
19 |
20 |
21 |
22 |
23 | );
24 | }
25 | }
26 | function mapStateToProps({ viewModel, loading }) {
27 | return {
28 | ...viewModel,
29 | loading: loading.models.snbThree,
30 | };
31 | }
32 |
33 | export default connect(mapStateToProps)(Index);
--------------------------------------------------------------------------------
/src/pages/sys/view/p2.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react";
2 | import { connect } from 'dva';
3 | import { Page } from '@components';
4 | import View from './components/view.js';
5 |
6 | class Index extends PureComponent {
7 | render() {
8 | const { p2, dispatch, loading } = this.props;
9 | const onSubmit = (times) => {
10 | dispatch({
11 | type: 'viewModel/getData',
12 | payload: {
13 | time: times,
14 | key: "p2"
15 | }
16 | });
17 | };
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 | function mapStateToProps({ viewModel, loading }) {
26 | return {
27 | ...viewModel,
28 | loading: loading.snbThree
29 | };
30 | }
31 | export default connect(mapStateToProps)(Index);
32 |
--------------------------------------------------------------------------------
/src/pages/sys/view/service.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 |
3 | export function fetch(payload) {
4 | const { time, key } = payload;
5 | return request(`/view/${key}`, {
6 | method: 'POST',
7 | body: JSON.stringify({
8 | time
9 | }),
10 | });
11 | }
--------------------------------------------------------------------------------
/src/pages/versions/components/version.css:
--------------------------------------------------------------------------------
1 | .content {
2 | padding-bottom: 0;
3 | }
4 | .content:before,
5 | .content:after {
6 | content: ' ';
7 | display: table;
8 | }
9 | .content:after {
10 | clear: both;
11 | visibility: hidden;
12 | font-size: 0;
13 | height: 0;
14 | }
15 | .content > ul > li > ol > li {
16 | line-height: 25px;
17 | }
18 | .content code {
19 | background: #f7f7f7;
20 | font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
21 | margin: 0 3px;
22 | padding: 1px 6px;
23 | border-radius: 3px;
24 | color: #777;
25 | font-size: 12.8px;
26 | border: 1px solid #e9e9e9;
27 | }
28 |
--------------------------------------------------------------------------------
/src/pages/versions/components/version.less:
--------------------------------------------------------------------------------
1 | .content {
2 | // padding: 20px;
3 | padding-bottom: 0;
4 | &:before,
5 | &:after {
6 | content: ' ';
7 | display: table;
8 | }
9 | &:after {
10 | clear: both;
11 | visibility: hidden;
12 | font-size: 0;
13 | height: 0;
14 | }
15 | >ul>li>ol>li {
16 | line-height: 25px;
17 | }
18 | code {
19 | background: #f7f7f7;
20 | font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
21 | margin: 0 3px;
22 | padding: 1px 6px;
23 | border-radius: 3px;
24 | color: #777;
25 | font-size: 12.8px;
26 | border: 1px solid #e9e9e9;
27 | }
28 | }
--------------------------------------------------------------------------------
/src/pages/versions/index.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | padding: 30px;
3 | padding-left: 60px;
4 | }
5 |
--------------------------------------------------------------------------------
/src/pages/versions/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description
6 | */
7 | import { Timeline } from 'antd';
8 | import { Page } from '@components';
9 | import Version from './components/version';
10 | import styles from './index.less';
11 | import data from '@versionsConfig';
12 | function Index() {
13 | return (
14 |
15 |
16 |
17 | {
18 | data.map((item, i) =>
19 |
20 |
21 | )
22 | }
23 |
24 |
25 |
26 | );
27 | }
28 | export default Index;
--------------------------------------------------------------------------------
/src/pages/versions/index.less:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | padding: 30px;
3 | padding-left: 60px;
4 | }
--------------------------------------------------------------------------------
/src/services/global.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 | export function logout() {
3 | return request('/logout', {
4 | method: 'GET',
5 | });
6 | }
7 | export function getSysInfo(payload) {
8 | return request('/getSysInfo', {
9 | method: 'POST',
10 | body: JSON.stringify({
11 | ...payload,
12 | }),
13 | });
14 | }
15 | export function getMessage(payload) {
16 | return request('/getMessage', {
17 | method: 'POST',
18 | body: JSON.stringify({
19 | ...payload,
20 | }),
21 | });
22 | }
--------------------------------------------------------------------------------
/src/services/index.js:
--------------------------------------------------------------------------------
1 | import { logout, getSysInfo, getMessage } from './global';
2 | import { getMenuData } from './menu';
3 |
4 | export {
5 | logout,
6 | getSysInfo,
7 | getMessage,
8 | getMenuData
9 | };
--------------------------------------------------------------------------------
/src/services/menu.js:
--------------------------------------------------------------------------------
1 | import { request } from '@utils';
2 | // export function getMenuData() {
3 | // return request('/getMenuData', {
4 | // method: 'GET',
5 | // });
6 | // }
7 | export function getMenuData(payload) {
8 | return request('/getMenuData', {
9 | method: 'POST',
10 | body: JSON.stringify({
11 | ...payload,
12 | }),
13 | });
14 | }
--------------------------------------------------------------------------------
/src/themes/index.less:
--------------------------------------------------------------------------------
1 | @import "./vars.less";
2 |
3 | /***滚动条美化 Start***/
4 | .hideScrollbar() {
5 | /*火狐下隐藏滚动条*/
6 | scrollbar-width: none;
7 |
8 | &::-webkit-scrollbar {
9 | display: none;
10 | /*滚动条整体样式*/
11 | width: 0px;
12 | /*高宽分别对应横竖滚动条的尺寸*/
13 | height: 0px;
14 | }
15 |
16 | /*隐藏滚动条,当IE下溢出,仍然可以滚动*/
17 | -ms-overflow-style:none;
18 | /*火狐下隐藏滚动条*/
19 | // overflow:-moz-scrollbars-none;
20 | }
21 |
22 | .scrollbar(@w: 8px, @h: 8px) {
23 | &::-webkit-scrollbar {
24 | /*滚动条整体样式*/
25 | width: @w;
26 | /*高宽分别对应横竖滚动条的尺寸*/
27 | height: @h;
28 | }
29 |
30 | &::-webkit-scrollbar-thumb {
31 | /*滚动条里面小方块*/
32 | border-radius: 10px;
33 | -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); // background: #999999;
34 | background-color: rgba(152, 152, 152, 0.4)
35 | }
36 |
37 | &::-webkit-scrollbar-track {
38 | /*滚动条里面轨道*/
39 | -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
40 | border-radius: 10px;
41 | background: #EDEDED;
42 | }
43 | }
44 |
45 | /***滚动条美化 End***/
46 | /***文字超出隐藏 Start***/
47 | .text-overflow {
48 | white-space: nowrap;
49 | text-overflow: ellipsis;
50 | overflow: hidden;
51 | }
52 |
53 | /***文字超出隐藏 End***/
54 | /***注册滚动条全局样式***/
55 | :global(.scrollbar) {
56 | .scrollbar();
57 | }
58 |
59 | :global(.ant-select-dropdown>div>ul) {
60 | .scrollbar();
61 | }
--------------------------------------------------------------------------------
/src/themes/mixin.less:
--------------------------------------------------------------------------------
1 | @purple: #d897eb;
2 |
--------------------------------------------------------------------------------
/src/themes/vars.less:
--------------------------------------------------------------------------------
1 | @import "../../node_modules/antd/lib/style/themes/default.less";
2 | @import "./mixin.less";
3 |
--------------------------------------------------------------------------------
/src/utils/CryptoJS.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author M
3 | * @email mpw0311@163.com
4 | * @version 1.0.0
5 | * @description AES加密解密
6 | */
7 | import { AES, enc, mode, pad } from 'crypto-js';
8 | /**
9 | * 初始化秘钥
10 | * @param {*} key |密钥 16 位
11 | * @param {*} iv |初始向量 initial vector 16 位,key 和 iv 可以一致
12 | */
13 | const _init = (key = 'ANTD-#6$8Y-oi5&K', iv = key) => {
14 | return {
15 | key: enc.Utf8.parse(key),
16 | iv: enc.Utf8.parse(iv)
17 | };
18 | };
19 |
20 | /**
21 | * 加密
22 | * @param {string} str
23 | */
24 | export const encrypt = (str, aes_key) => {
25 | const { key, iv } = _init(aes_key);
26 | const encrypted = AES.encrypt(str, key, {
27 | iv,
28 | mode: mode.CBC,
29 | padding: pad.Pkcs7
30 | });
31 | // 转换为字符串
32 | return encrypted.toString();
33 | };
34 | /**
35 | * 解密
36 | * @param {string} encrypted
37 | */
38 | export const decrypt = (encrypted, aes_key) => {
39 | const { key, iv } = _init(aes_key);
40 | const decrypted = AES.decrypt(encrypted, key, {
41 | iv,
42 | mode: mode.CBC,
43 | padding: pad.Pkcs7
44 | });
45 |
46 | // 转换为 utf8 字符串
47 | return enc.Utf8.stringify(decrypted);
48 | };
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 |
2 | // import request from './request';
3 | import request from './umiRequest';
4 | import menusData from '@menuConfig';
5 | import * as methods from './_';
6 | export {
7 | request,
8 | menusData,
9 | methods,
10 | };
--------------------------------------------------------------------------------
/src/utils/umiRequest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * request 网络请求工具
3 | * 更详细的api文档: https://bigfish.alipay.com/doc/api#request
4 | */
5 | import { extend } from 'umi-request';
6 | import { notification } from 'antd';
7 | import router from 'umi/router';
8 | import { apiPrefix } from '@platformConfig';
9 |
10 | const codeMessage = {
11 | 200: '服务器成功返回请求的数据。',
12 | 201: '新建或修改数据成功。',
13 | 202: '一个请求已经进入后台排队(异步任务)。',
14 | 204: '删除数据成功。',
15 | 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
16 | 401: '用户没有权限(令牌、用户名、密码错误)。',
17 | 403: '用户得到授权,但是访问是被禁止的。',
18 | 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
19 | 406: '请求的格式不可得。',
20 | 410: '请求的资源被永久删除,且不会再得到的。',
21 | 422: '当创建一个对象时,发生一个验证错误。',
22 | 500: '服务器发生错误,请检查服务器。',
23 | 502: '网关错误。',
24 | 503: '服务不可用,服务器暂时过载或维护。',
25 | 504: '网关超时。',
26 | };
27 | /**
28 | * 异常处理程序
29 | */
30 | const errorHandler = error => {
31 | const { response = {} } = error;
32 | const errortext = codeMessage[response.status] || response.statusText;
33 | const { status, url } = response;
34 | if (status === 401) {
35 | notification.error({
36 | message: '未登录或登录已过期,请重新登录。',
37 | });
38 | // @HACK
39 | /* eslint-disable no-underscore-dangle */
40 | window.g_app._store.dispatch({
41 | type: 'login/logout',
42 | });
43 | return;
44 | }
45 | notification.error({
46 | message: `请求错误 ${status}: ${url}`,
47 | description: errortext,
48 | });
49 | // environment should not be used
50 | if (status === 403) {
51 | router.push('/exception/403');
52 | return;
53 | }
54 | if (status <= 504 && status >= 500) {
55 | router.push('/exception/500');
56 | return;
57 | }
58 | if (status >= 404 && status < 422) {
59 | router.push('/exception/404');
60 | }
61 | };
62 |
63 | /**
64 | * 配置request请求时的默认参数
65 | */
66 | const request = extend({
67 | errorHandler, // 默认错误处理
68 | credentials: 'include', // 默认请求是否带上cookie
69 | prefix: apiPrefix,
70 | });
71 | export default request;
72 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 不是真实的 webpack 配置,仅为兼容 webstorm 和 intellij idea 代码跳转
3 | * ref: https://github.com/umijs/umi/issues/1109#issuecomment-423380125
4 | */
5 |
6 | module.exports = {
7 | resolve: {
8 | alias: {
9 | '@': require('path').resolve(__dirname, 'src'),
10 | },
11 | },
12 | };
13 |
--------------------------------------------------------------------------------