mirror of
https://github.com/chillzhuang/Sword
synced 2024-11-23 10:49:27 +08:00
1.0.0.RELEASE
This commit is contained in:
commit
633669b2dc
35
.dockerignore
Normal file
35
.dockerignore
Normal file
@ -0,0 +1,35 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
**/node_modules
|
||||
/src/utils/request-temp.js
|
||||
|
||||
# production
|
||||
/.vscode
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-error.log
|
||||
|
||||
/coverage
|
||||
.idea
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
*bak
|
||||
.vscode
|
||||
|
||||
# visual studio code
|
||||
.history
|
||||
*.log
|
||||
|
||||
functions/mock
|
||||
.temp/**
|
||||
|
||||
# umi
|
||||
.umi
|
||||
.umi-production
|
||||
|
||||
# screenshot
|
||||
screenshot
|
||||
.firebase
|
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
/functions/mock/**
|
||||
/scripts
|
||||
/config
|
40
.eslintrc.js
Normal file
40
.eslintrc.js
Normal file
@ -0,0 +1,40 @@
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
extends: ['airbnb', 'prettier', 'plugin:compat/recommended'],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
mocha: true,
|
||||
jest: true,
|
||||
jasmine: true,
|
||||
},
|
||||
globals: {
|
||||
APP_TYPE: true,
|
||||
page: true,
|
||||
},
|
||||
rules: {
|
||||
'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
|
||||
'react/jsx-wrap-multilines': 0,
|
||||
'react/prop-types': 0,
|
||||
'react/forbid-prop-types': 0,
|
||||
'react/jsx-one-expression-per-line': 0,
|
||||
'import/no-unresolved': [2, { ignore: ['^@/', '^umi/'] }],
|
||||
'import/no-extraneous-dependencies': [
|
||||
2,
|
||||
{
|
||||
optionalDependencies: true,
|
||||
devDependencies: ['**/tests/**.js', '/mock/**.js', '**/**.test.js'],
|
||||
},
|
||||
],
|
||||
'jsx-a11y/no-noninteractive-element-interactions': 0,
|
||||
'jsx-a11y/click-events-have-key-events': 0,
|
||||
'jsx-a11y/no-static-element-interactions': 0,
|
||||
'jsx-a11y/anchor-is-valid': 0,
|
||||
'no-nested-ternary': 0,
|
||||
'linebreak-style': 0,
|
||||
},
|
||||
settings: {
|
||||
polyfills: ['fetch', 'promises', 'url'],
|
||||
},
|
||||
};
|
5
.firebaserc
Normal file
5
.firebaserc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "antd-pro"
|
||||
}
|
||||
}
|
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
**/node_modules
|
||||
# roadhog-api-doc ignore
|
||||
/src/utils/request-temp.js
|
||||
_roadhog-api-doc
|
||||
|
||||
# production
|
||||
/dist
|
||||
/.vscode
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-error.log
|
||||
|
||||
/coverage
|
||||
.idea
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
*bak
|
||||
.vscode
|
||||
|
||||
# visual studio code
|
||||
.history
|
||||
*.log
|
||||
|
||||
functions/mock
|
||||
.temp/**
|
||||
|
||||
# umi
|
||||
.umi
|
||||
.umi-production
|
||||
|
||||
# screenshot
|
||||
screenshot
|
||||
.firebase
|
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@ -0,0 +1,6 @@
|
||||
ports:
|
||||
- port: 8000
|
||||
onOpen: open-preview
|
||||
tasks:
|
||||
- init: npm install
|
||||
command: npm start
|
5
.prettierignore
Normal file
5
.prettierignore
Normal file
@ -0,0 +1,5 @@
|
||||
**/*.md
|
||||
**/*.svg
|
||||
package.json
|
||||
.umi
|
||||
.umi-production
|
11
.prettierrc
Normal file
11
.prettierrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ".prettierrc",
|
||||
"options": { "parser": "json" }
|
||||
}
|
||||
]
|
||||
}
|
13
.stylelintrc.json
Normal file
13
.stylelintrc.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-css-modules",
|
||||
"stylelint-config-rational-order",
|
||||
"stylelint-config-prettier"
|
||||
],
|
||||
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
|
||||
"rules": {
|
||||
"no-descending-specificity": null,
|
||||
"plugin/declaration-block-no-ignored-properties": true
|
||||
}
|
||||
}
|
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM circleci/node:latest-browsers
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
USER root
|
||||
COPY package.json ./
|
||||
RUN yarn
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN npm run test:all
|
||||
|
||||
CMD ["npm", "run", "build"]
|
11
Dockerfile.dev
Normal file
11
Dockerfile.dev
Normal file
@ -0,0 +1,11 @@
|
||||
FROM node:latest
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm install --silent --no-cache
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
|
||||
CMD ["npm", "run", "start"]
|
11
Dockerfile.hub
Normal file
11
Dockerfile.hub
Normal file
@ -0,0 +1,11 @@
|
||||
FROM nginx
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY ./docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY ./dist /usr/share/nginx/html/
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 bladex.vip
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
92
README.md
Normal file
92
README.md
Normal file
@ -0,0 +1,92 @@
|
||||
## 简介
|
||||
Sword 是 [SpringBlade](https://gitee.com/smallc/SpringBlade)前端UI项目,基于react 、ant design、dva,用于快速构建系统中后台业务。
|
||||
|
||||
## 在线演示
|
||||
演示地址:[http://sword.bladex.vip](http://sword.bladex.vip)
|
||||
|
||||
## 特性
|
||||
|
||||
- :gem: **优雅美观**:基于 Ant Design 体系精心设计
|
||||
- :triangular_ruler: **常见设计模式**:提炼自中后台应用的典型页面和场景
|
||||
- :rocket: **最新技术栈**:使用 React/umi/dva/antd 等前端前沿技术开发
|
||||
- :iphone: **响应式**:针对不同屏幕大小设计
|
||||
- :art: **主题**:可配置的主题满足多样化的品牌诉求
|
||||
- :globe_with_meridians: **国际化**:内建业界通用的国际化方案
|
||||
- :zap: **最佳实践**:良好的工程实践助您持续产出高质量代码
|
||||
- :1234: **Mock 数据**:实用的本地数据调试方案
|
||||
- :white_check_mark: **UI 测试**:自动化测试保障前端产品质量
|
||||
|
||||
## 支持环境
|
||||
|
||||
现代浏览器及 IE11。
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera |
|
||||
| --------- | --------- | --------- | --------- | --------- |
|
||||
| IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions
|
||||
|
||||
|
||||
## 用户权益
|
||||
* 允许免费用于学习、毕设、公司项目、私活等。
|
||||
* 代码文件需保留相关license信息。
|
||||
|
||||
## 禁止事项
|
||||
* 直接将本项目挂淘宝等商业平台出售。
|
||||
* 业务代码50%以上相似度的二次开源,二次开源需先联系作者。
|
||||
|
||||
注意:若禁止条款被发现有权追讨19999的授权费。
|
||||
|
||||
## 如何启动
|
||||
```
|
||||
$ git clone https://gitee.com/smallc/Sword.git
|
||||
$ cd Sword
|
||||
$ npm install
|
||||
# mock模式
|
||||
$ npm start
|
||||
# 服务模式
|
||||
$ npm run start:no-mock
|
||||
# 访问 http://localhost:88
|
||||
```
|
||||
|
||||
# 界面一览
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-k8s.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-harbor.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-traefik.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-traefik-health.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-consul.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-consul-nodes1.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-admin1.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-admin2.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-swagger1.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-swagger2.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-main.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-menu.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-menu-edit.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-menu-icon.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-role.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-user.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-dict.png "/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-log.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-locale-cn.png"/></td>
|
||||
<td><img src="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-locale-us.png"/></td>
|
||||
</tr>
|
||||
</table>
|
74
azure-pipelines.yml
Normal file
74
azure-pipelines.yml
Normal file
@ -0,0 +1,74 @@
|
||||
# Node.js
|
||||
# Build a general Node.js project with npm.
|
||||
# Add steps that analyze code, save build artifacts, deploy, and more:
|
||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
||||
name: ant design pro
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
- job: lintAndBuild
|
||||
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: false
|
||||
- script: yarn install
|
||||
displayName: install
|
||||
- script: npm run lint
|
||||
displayName: lint
|
||||
- script: npm run build
|
||||
env:
|
||||
PROGRESS: none
|
||||
displayName: build
|
||||
|
||||
- job: test
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
|
||||
container:
|
||||
image: circleci/node:latest-browsers
|
||||
options: '-u root'
|
||||
|
||||
steps:
|
||||
- script: yarn install
|
||||
displayName: install
|
||||
- script: npm run test:all
|
||||
env:
|
||||
PROGRESS: none
|
||||
displayName: test
|
||||
|
||||
- job: Windows
|
||||
pool:
|
||||
vmImage: 'vs2017-win2016'
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '11.x'
|
||||
- script: yarn install
|
||||
displayName: install
|
||||
- script: npm run lint
|
||||
displayName: lint
|
||||
- script: npm run build
|
||||
env:
|
||||
PROGRESS: none
|
||||
displayName: build
|
||||
|
||||
- job: MacOS
|
||||
pool:
|
||||
vmImage: 'macOS-10.13'
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '11.x'
|
||||
- script: yarn install
|
||||
displayName: install
|
||||
- script: npm run lint
|
||||
displayName: lint
|
||||
- script: npm run
|
||||
env:
|
||||
PROGRESS: none
|
||||
displayName: build
|
119
config/config.js
Normal file
119
config/config.js
Normal file
@ -0,0 +1,119 @@
|
||||
// https://umijs.org/config/
|
||||
import os from 'os';
|
||||
import pageRoutes from './router.config';
|
||||
import webpackPlugin from './plugin.config';
|
||||
import defaultSettings from '../src/defaultSettings';
|
||||
import slash from 'slash2';
|
||||
|
||||
const { pwa, primaryColor } = defaultSettings;
|
||||
|
||||
const plugins = [
|
||||
[
|
||||
'umi-plugin-react',
|
||||
{
|
||||
antd: true,
|
||||
dva: {
|
||||
hmr: true,
|
||||
},
|
||||
locale: {
|
||||
enable: true, // default false
|
||||
default: 'zh-CN', // default zh-CN
|
||||
baseNavigator: true, // default true, when it is true, will use `navigator.language` overwrite default
|
||||
},
|
||||
dynamicImport: {
|
||||
loadingComponent: './components/PageLoading/index',
|
||||
webpackChunkName: true,
|
||||
},
|
||||
pwa: pwa
|
||||
? {
|
||||
workboxPluginMode: 'InjectManifest',
|
||||
workboxOptions: {
|
||||
importWorkboxFrom: 'local',
|
||||
},
|
||||
}
|
||||
: {},
|
||||
...(!process.env.TEST && os.platform() === 'darwin'
|
||||
? {
|
||||
dll: {
|
||||
include: ['dva', 'dva/router', 'dva/saga', 'dva/fetch'],
|
||||
exclude: ['@babel/runtime'],
|
||||
},
|
||||
hardSource: false,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
// 针对 preview.pro.ant.design 的 GA 统计代码
|
||||
// 业务上不需要这个
|
||||
if (process.env.APP_TYPE === 'site') {
|
||||
plugins.push([
|
||||
'umi-plugin-ga',
|
||||
{
|
||||
code: 'UA-72788897-6',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
export default {
|
||||
// add for transfer to umi
|
||||
plugins,
|
||||
history: 'hash',
|
||||
define: {
|
||||
APP_TYPE: process.env.APP_TYPE || '',
|
||||
},
|
||||
treeShaking: true,
|
||||
targets: {
|
||||
ie: 11,
|
||||
},
|
||||
// 路由配置
|
||||
routes: pageRoutes,
|
||||
// Theme for antd
|
||||
// https://ant.design/docs/react/customize-theme-cn
|
||||
theme: {
|
||||
'primary-color': primaryColor,
|
||||
},
|
||||
externals: {
|
||||
'@antv/data-set': 'DataSet',
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost',
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/api': '' },
|
||||
},
|
||||
},
|
||||
ignoreMomentLocale: true,
|
||||
lessLoaderOptions: {
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
disableRedirectHoist: true,
|
||||
cssLoaderOptions: {
|
||||
modules: true,
|
||||
getLocalIdent: (context, localIdentName, localName) => {
|
||||
if (
|
||||
context.resourcePath.includes('node_modules') ||
|
||||
context.resourcePath.includes('ant.design.pro.less') ||
|
||||
context.resourcePath.includes('global.less')
|
||||
) {
|
||||
return localName;
|
||||
}
|
||||
const match = context.resourcePath.match(/src(.*)/);
|
||||
if (match && match[1]) {
|
||||
const antdProPath = match[1].replace('.less', '');
|
||||
const arr = slash(antdProPath)
|
||||
.split('/')
|
||||
.map(a => a.replace(/([A-Z])/g, '-$1'))
|
||||
.map(a => a.toLowerCase());
|
||||
return `antd-pro${arr.join('-')}-${localName}`.replace(/--/g, '-');
|
||||
}
|
||||
return localName;
|
||||
},
|
||||
},
|
||||
manifest: {
|
||||
basePath: '/',
|
||||
},
|
||||
|
||||
chainWebpack: webpackPlugin,
|
||||
};
|
33
config/plugin.config.js
Normal file
33
config/plugin.config.js
Normal file
@ -0,0 +1,33 @@
|
||||
// Change theme plugin
|
||||
|
||||
import MergeLessPlugin from 'antd-pro-merge-less';
|
||||
import AntDesignThemePlugin from 'antd-theme-webpack-plugin';
|
||||
import path from 'path';
|
||||
|
||||
export default config => {
|
||||
// pro 和 开发环境再添加这个插件
|
||||
if (process.env.APP_TYPE === 'site' || process.env.NODE_ENV !== 'production') {
|
||||
// 将所有 less 合并为一个供 themePlugin使用
|
||||
const outFile = path.join(__dirname, '../.temp/ant-design-pro.less');
|
||||
const stylesDir = path.join(__dirname, '../src/');
|
||||
|
||||
config.plugin('merge-less').use(MergeLessPlugin, [
|
||||
{
|
||||
stylesDir,
|
||||
outFile,
|
||||
},
|
||||
]);
|
||||
|
||||
config.plugin('ant-design-theme').use(AntDesignThemePlugin, [
|
||||
{
|
||||
antDir: path.join(__dirname, '../node_modules/antd'),
|
||||
stylesDir,
|
||||
varFile: path.join(__dirname, '../node_modules/antd/lib/style/themes/default.less'),
|
||||
mainLessFile: outFile, // themeVariables: ['@primary-color'],
|
||||
indexFileName: 'index.html',
|
||||
generateOne: true,
|
||||
lessUrl: 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js',
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
216
config/router.config.js
Normal file
216
config/router.config.js
Normal file
@ -0,0 +1,216 @@
|
||||
export default [
|
||||
// user
|
||||
{
|
||||
path: '/user',
|
||||
component: '../layouts/UserLayout',
|
||||
routes: [
|
||||
{ path: '/user', redirect: '/user/login' },
|
||||
{ path: '/user/login', component: './Login/Login' },
|
||||
{ path: '/user/register', component: './Login/Register' },
|
||||
{ path: '/user/register-result', component: './Login/RegisterResult' },
|
||||
],
|
||||
},
|
||||
// app
|
||||
{
|
||||
path: '/',
|
||||
component: '../layouts/BasicLayout',
|
||||
Routes: ['src/pages/Authorized'],
|
||||
authority: ['admin', 'user'],
|
||||
routes: [
|
||||
// dashboard
|
||||
{ path: '/', redirect: '/dashboard/workplace' },
|
||||
{
|
||||
path: '/result',
|
||||
routes: [
|
||||
// result
|
||||
{ path: '/result/success', component: './Result/Success' },
|
||||
{ path: '/result/fail', component: './Result/Error' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/exception',
|
||||
routes: [
|
||||
// exception
|
||||
{ path: '/exception/403', component: './Exception/403' },
|
||||
{ path: '/exception/404', component: './Exception/404' },
|
||||
{ path: '/exception/500', component: './Exception/500' },
|
||||
{ path: '/exception/trigger', component: './Exception/TriggerException' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/account',
|
||||
routes: [
|
||||
{
|
||||
path: '/account/center',
|
||||
component: './Account/Center/Center',
|
||||
routes: [
|
||||
{ path: '/account/center', redirect: '/account/center/articles' },
|
||||
{ path: '/account/center/articles', component: './Account/Center/Articles' },
|
||||
{ path: '/account/center/applications', component: './Account/Center/Applications' },
|
||||
{ path: '/account/center/projects', component: './Account/Center/Projects' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/account/settings',
|
||||
component: './Account/Settings/Info',
|
||||
routes: [
|
||||
{ path: '/account/settings', redirect: '/account/settings/base' },
|
||||
{ path: '/account/settings/base', component: './Account/Settings/BaseView' },
|
||||
{ path: '/account/settings/security', component: './Account/Settings/SecurityView' },
|
||||
{ path: '/account/settings/binding', component: './Account/Settings/BindingView' },
|
||||
{
|
||||
path: '/account/settings/notification',
|
||||
component: './Account/Settings/NotificationView',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
routes: [
|
||||
{ path: '/dashboard/analysis', component: './Dashboard/Analysis' },
|
||||
{ path: '/dashboard/monitor', component: './Dashboard/Monitor' },
|
||||
{ path: '/dashboard/workplace', component: './Dashboard/Workplace' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/desk',
|
||||
routes: [
|
||||
{
|
||||
path: '/desk/notice',
|
||||
routes: [
|
||||
{ path: '/desk/notice', redirect: '/desk/notice/list' },
|
||||
{ path: '/desk/notice/list', component: './Desk/Notice/Notice' },
|
||||
{ path: '/desk/notice/add', component: './Desk/Notice/NoticeAdd' },
|
||||
{ path: '/desk/notice/edit/:id', component: './Desk/Notice/NoticeEdit' },
|
||||
{ path: '/desk/notice/view/:id', component: './Desk/Notice/NoticeView' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system',
|
||||
routes: [
|
||||
{
|
||||
path: '/system/user',
|
||||
routes: [
|
||||
{ path: '/system/user', redirect: '/system/user/list' },
|
||||
{ path: '/system/user/list', component: './System/User/User' },
|
||||
{ path: '/system/user/add', component: './System/User/UserAdd' },
|
||||
{ path: '/system/user/edit/:id', component: './System/User/UserEdit' },
|
||||
{ path: '/system/user/view/:id', component: './System/User/UserView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system/dict',
|
||||
routes: [
|
||||
{ path: '/system/dict', redirect: '/system/dict/list' },
|
||||
{ path: '/system/dict/list', component: './System/Dict/Dict' },
|
||||
{ path: '/system/dict/add', component: './System/Dict/DictAdd' },
|
||||
{ path: '/system/dict/add/:id', component: './System/Dict/DictAdd' },
|
||||
{ path: '/system/dict/edit/:id', component: './System/Dict/DictEdit' },
|
||||
{ path: '/system/dict/view/:id', component: './System/Dict/DictView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system/dept',
|
||||
routes: [
|
||||
{ path: '/system/dept', redirect: '/system/dept/list' },
|
||||
{ path: '/system/dept/list', component: './System/Dept/Dept' },
|
||||
{ path: '/system/dept/add', component: './System/Dept/DeptAdd' },
|
||||
{ path: '/system/dept/add/:id', component: './System/Dept/DeptAdd' },
|
||||
{ path: '/system/dept/edit/:id', component: './System/Dept/DeptEdit' },
|
||||
{ path: '/system/dept/view/:id', component: './System/Dept/DeptView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system/role',
|
||||
routes: [
|
||||
{ path: '/system/role', redirect: '/system/role/list' },
|
||||
{ path: '/system/role/list', component: './System/Role/Role' },
|
||||
{ path: '/system/role/add', component: './System/Role/RoleAdd' },
|
||||
{ path: '/system/role/add/:id', component: './System/Role/RoleAdd' },
|
||||
{ path: '/system/role/edit/:id', component: './System/Role/RoleEdit' },
|
||||
{ path: '/system/role/view/:id', component: './System/Role/RoleView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system/menu',
|
||||
routes: [
|
||||
{ path: '/system/menu', redirect: '/system/menu/list' },
|
||||
{ path: '/system/menu/list', component: './System/Menu/Menu' },
|
||||
{ path: '/system/menu/add', component: './System/Menu/MenuAdd' },
|
||||
{ path: '/system/menu/add/:id', component: './System/Menu/MenuAdd' },
|
||||
{ path: '/system/menu/edit/:id', component: './System/Menu/MenuEdit' },
|
||||
{ path: '/system/menu/view/:id', component: './System/Menu/MenuView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system/param',
|
||||
routes: [
|
||||
{ path: '/system/param', redirect: '/system/param/list' },
|
||||
{ path: '/system/param/list', component: './System/Param/Param' },
|
||||
{ path: '/system/param/add', component: './System/Param/ParamAdd' },
|
||||
{ path: '/system/param/edit/:id', component: './System/Param/ParamEdit' },
|
||||
{ path: '/system/param/view/:id', component: './System/Param/ParamView' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/monitor',
|
||||
routes: [
|
||||
{
|
||||
path: '/monitor/log',
|
||||
routes: [
|
||||
{
|
||||
path: '/monitor/log/usual',
|
||||
routes: [
|
||||
{ path: '/monitor/log/usual', redirect: '/monitor/log/usual/list' },
|
||||
{ path: '/monitor/log/usual/list', component: './Monitor/Log/LogUsual' },
|
||||
{ path: '/monitor/log/usual/view/:id', component: './Monitor/Log/LogUsualView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/monitor/log/api',
|
||||
routes: [
|
||||
{ path: '/monitor/log/api', redirect: '/monitor/log/api/list' },
|
||||
{ path: '/monitor/log/api/list', component: './Monitor/Log/LogApi' },
|
||||
{ path: '/monitor/log/api/view/:id', component: './Monitor/Log/LogApiView' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/monitor/log/error',
|
||||
routes: [
|
||||
{ path: '/monitor/log/error', redirect: '/monitor/log/error/list' },
|
||||
{ path: '/monitor/log/error/list', component: './Monitor/Log/LogError' },
|
||||
{ path: '/monitor/log/error/view/:id', component: './Monitor/Log/LogErrorView' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/tool',
|
||||
routes: [
|
||||
{
|
||||
path: '/tool/code',
|
||||
routes: [
|
||||
{ path: '/tool/code', redirect: '/tool/code/list' },
|
||||
{ path: '/tool/code/list', component: './System/Code/Code' },
|
||||
{ path: '/tool/code/add', component: './System/Code/CodeAdd' },
|
||||
{ path: '/tool/code/add/:id', component: './System/Code/CodeAdd' },
|
||||
{ path: '/tool/code/edit/:id', component: './System/Code/CodeEdit' },
|
||||
{ path: '/tool/code/view/:id', component: './System/Code/CodeView' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
component: '404',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
14
docker/docker-compose.dev.yml
Normal file
14
docker/docker-compose.dev.yml
Normal file
@ -0,0 +1,14 @@
|
||||
version: "3.5"
|
||||
|
||||
services:
|
||||
ant-design-pro_dev:
|
||||
ports:
|
||||
- 8000:8000
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: "ant-design-pro_dev"
|
||||
volumes:
|
||||
- ../src:/usr/src/app/src
|
||||
- ../config:/usr/src/app/config
|
||||
- ../mock:/usr/src/app/mock
|
21
docker/docker-compose.yml
Normal file
21
docker/docker-compose.yml
Normal file
@ -0,0 +1,21 @@
|
||||
version: "3.5"
|
||||
|
||||
services:
|
||||
ant-design-pro_build:
|
||||
build: ../
|
||||
container_name: "ant-design-pro_build"
|
||||
volumes:
|
||||
- dist:/usr/src/app/dist
|
||||
|
||||
ant-design-pro_web:
|
||||
image: nginx
|
||||
ports:
|
||||
- 80:80
|
||||
container_name: "ant-design-pro_web"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- dist:/usr/share/nginx/html:ro
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
|
||||
volumes:
|
||||
dist:
|
22
docker/nginx.conf
Normal file
22
docker/nginx.conf
Normal file
@ -0,0 +1,22 @@
|
||||
server {
|
||||
listen 80;
|
||||
# gzip config
|
||||
gzip on;
|
||||
gzip_min_length 1k;
|
||||
gzip_comp_level 9;
|
||||
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
|
||||
gzip_vary on;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
location /api {
|
||||
proxy_pass https://preview.pro.ant.design;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
13
firebase.json
Normal file
13
firebase.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"hosting": {
|
||||
"public": "dist",
|
||||
"rewrites": [
|
||||
{ "source": "/api/**", "function": "api" },
|
||||
{
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
|
||||
}
|
||||
}
|
10
functions/index.js
Normal file
10
functions/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
// [START functionsimport]
|
||||
const functions = require('firebase-functions');
|
||||
const express = require('express');
|
||||
|
||||
const matchMock = require('./matchMock');
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(matchMock);
|
||||
exports.api = functions.https.onRequest(app);
|
115
functions/matchMock.js
Normal file
115
functions/matchMock.js
Normal file
@ -0,0 +1,115 @@
|
||||
const pathToRegexp = require('path-to-regexp');
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
const mockFile = require('./mock/index');
|
||||
|
||||
const BODY_PARSED_METHODS = ['post', 'put', 'patch'];
|
||||
|
||||
const debug = console.log;
|
||||
function parseKey(key) {
|
||||
let method = 'get';
|
||||
let path = key;
|
||||
if (key.indexOf(' ') > -1) {
|
||||
const splited = key.split(' ');
|
||||
method = splited[0].toLowerCase();
|
||||
path = splited[1]; // eslint-disable-line
|
||||
}
|
||||
return {
|
||||
method,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
function createHandler(method, path, handler) {
|
||||
return (req, res, next) => {
|
||||
function sendData() {
|
||||
if (typeof handler === 'function') {
|
||||
handler(req, res, next);
|
||||
} else {
|
||||
res.json(handler);
|
||||
}
|
||||
}
|
||||
if (BODY_PARSED_METHODS.includes(method)) {
|
||||
bodyParser.json({ limit: '5mb', strict: false })(req, res, () => {
|
||||
bodyParser.urlencoded({ limit: '5mb', extended: true })(req, res, () => {
|
||||
sendData();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
sendData();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeConfig(config) {
|
||||
return Object.keys(config).reduce((memo, key) => {
|
||||
const handler = config[key];
|
||||
const { method, path } = parseKey(key);
|
||||
const keys = [];
|
||||
const re = pathToRegexp(path, keys);
|
||||
memo.push({
|
||||
method,
|
||||
path,
|
||||
re,
|
||||
keys,
|
||||
handler: createHandler(method, path, handler),
|
||||
});
|
||||
return memo;
|
||||
}, []);
|
||||
}
|
||||
|
||||
const mockData = normalizeConfig(mockFile);
|
||||
|
||||
function matchMock(req) {
|
||||
const { path: exceptPath } = req;
|
||||
const exceptMethod = req.method.toLowerCase();
|
||||
function decodeParam(val) {
|
||||
if (typeof val !== 'string' || val.length === 0) {
|
||||
return val;
|
||||
}
|
||||
|
||||
try {
|
||||
return decodeURIComponent(val);
|
||||
} catch (err) {
|
||||
if (err instanceof URIError) {
|
||||
err.message = `Failed to decode param ' ${val} '`;
|
||||
err.statusCode = 400;
|
||||
err.status = 400;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const mock of mockData) {
|
||||
const { method, re, keys } = mock;
|
||||
if (method === exceptMethod) {
|
||||
const match = re.exec(req.path);
|
||||
if (match) {
|
||||
const params = {};
|
||||
|
||||
for (let i = 1; i < match.length; i += 1) {
|
||||
const key = keys[i - 1];
|
||||
const prop = key.name;
|
||||
const val = decodeParam(match[i]);
|
||||
|
||||
if (val !== undefined || !hasOwnProperty.call(params, prop)) {
|
||||
params[prop] = val;
|
||||
}
|
||||
}
|
||||
req.params = params;
|
||||
return mock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mockData.filter(({ method, re }) => method === exceptMethod && re.test(exceptPath))[0];
|
||||
}
|
||||
module.exports = (req, res, next) => {
|
||||
const match = matchMock(req);
|
||||
if (match) {
|
||||
debug(`mock matched: [${match.method}] ${match.path}`);
|
||||
return match.handler(req, res, next);
|
||||
}
|
||||
return next();
|
||||
};
|
23
functions/package.json
Normal file
23
functions/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase serve --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "npm run mock && firebase deploy --only functions",
|
||||
"logs": "firebase functions:log",
|
||||
"mock": "node ../scripts/generateMock.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"body-parser": "^1.18.3",
|
||||
"express": "^4.16.4",
|
||||
"firebase-admin": "^6.4.0",
|
||||
"firebase-functions": "^2.1.0",
|
||||
"mockjs": "^1.0.1-beta3",
|
||||
"moment": "^2.22.2",
|
||||
"path-to-regexp": "^3.0.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
11
jest-puppeteer.config.js
Normal file
11
jest-puppeteer.config.js
Normal file
@ -0,0 +1,11 @@
|
||||
// ps https://github.com/GoogleChrome/puppeteer/issues/3120
|
||||
module.exports = {
|
||||
launch: {
|
||||
args: [
|
||||
'--disable-gpu',
|
||||
'--disable-dev-shm-usage',
|
||||
'--no-first-run',
|
||||
'--no-zygote',
|
||||
],
|
||||
},
|
||||
};
|
4
jest.config.js
Normal file
4
jest.config.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
testURL: 'http://localhost:8000',
|
||||
preset: 'jest-puppeteer',
|
||||
};
|
10
jsconfig.json
Normal file
10
jsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
341
mock/api.js
Normal file
341
mock/api.js
Normal file
@ -0,0 +1,341 @@
|
||||
import mockjs from 'mockjs';
|
||||
|
||||
const titles = [
|
||||
'Alipay',
|
||||
'Angular',
|
||||
'Ant Design',
|
||||
'Ant Design Pro',
|
||||
'Bootstrap',
|
||||
'React',
|
||||
'Vue',
|
||||
'Webpack',
|
||||
];
|
||||
const avatars = [
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', // Alipay
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png', // Angular
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png', // Ant Design
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png', // Ant Design Pro
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png', // Bootstrap
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png', // React
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png', // Vue
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png', // Webpack
|
||||
];
|
||||
|
||||
const avatars2 = [
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/psOgztMplJMGpVEqfcgF.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/ZpBqSxLxVEXfcUNoPKrz.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/laiEnJdGHVOhJrUShBaJ.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/UrQsqscbKEpNuJcvBZBu.png',
|
||||
];
|
||||
|
||||
const covers = [
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png',
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png',
|
||||
];
|
||||
const desc = [
|
||||
'那是一种内在的东西, 他们到达不了,也无法触及的',
|
||||
'希望是一个好东西,也许是最好的,好东西是不会消亡的',
|
||||
'生命就像一盒巧克力,结果往往出人意料',
|
||||
'城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
|
||||
'那时候我只会想自己想要什么,从不想自己拥有什么',
|
||||
];
|
||||
|
||||
const user = [
|
||||
'付小小',
|
||||
'曲丽丽',
|
||||
'林东东',
|
||||
'周星星',
|
||||
'吴加好',
|
||||
'朱偏右',
|
||||
'鱼酱',
|
||||
'乐哥',
|
||||
'谭小仪',
|
||||
'仲尼',
|
||||
];
|
||||
|
||||
function fakeList(count) {
|
||||
const list = [];
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
list.push({
|
||||
id: `fake-list-${i}`,
|
||||
owner: user[i % 10],
|
||||
title: titles[i % 8],
|
||||
avatar: avatars[i % 8],
|
||||
cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],
|
||||
status: ['active', 'exception', 'normal'][i % 3],
|
||||
percent: Math.ceil(Math.random() * 50) + 50,
|
||||
logo: avatars[i % 8],
|
||||
href: 'https://ant.design',
|
||||
updatedAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
|
||||
createdAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
|
||||
subDescription: desc[i % 5],
|
||||
description:
|
||||
'在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',
|
||||
activeUser: Math.ceil(Math.random() * 100000) + 100000,
|
||||
newUser: Math.ceil(Math.random() * 1000) + 1000,
|
||||
star: Math.ceil(Math.random() * 100) + 100,
|
||||
like: Math.ceil(Math.random() * 100) + 100,
|
||||
message: Math.ceil(Math.random() * 10) + 10,
|
||||
content:
|
||||
'段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。',
|
||||
members: [
|
||||
{
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png',
|
||||
name: '曲丽丽',
|
||||
id: 'member1',
|
||||
},
|
||||
{
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png',
|
||||
name: '王昭君',
|
||||
id: 'member2',
|
||||
},
|
||||
{
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png',
|
||||
name: '董娜娜',
|
||||
id: 'member3',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
let sourceData;
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const params = req.query;
|
||||
|
||||
const count = params.count * 1 || 20;
|
||||
|
||||
const result = fakeList(count);
|
||||
sourceData = result;
|
||||
return res.json(result);
|
||||
}
|
||||
|
||||
function postFakeList(req, res) {
|
||||
const { /* url = '', */ body } = req;
|
||||
// const params = getUrlParams(url);
|
||||
const { method, id } = body;
|
||||
// const count = (params.count * 1) || 20;
|
||||
let result = sourceData;
|
||||
|
||||
switch (method) {
|
||||
case 'delete':
|
||||
result = result.filter(item => item.id !== id);
|
||||
break;
|
||||
case 'update':
|
||||
result.forEach((item, i) => {
|
||||
if (item.id === id) {
|
||||
result[i] = Object.assign(item, body);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'post':
|
||||
result.unshift({
|
||||
body,
|
||||
id: `fake-list-${result.length}`,
|
||||
createdAt: new Date().getTime(),
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res.json(result);
|
||||
}
|
||||
|
||||
const getNotice = {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
id: 'xxx1',
|
||||
title: titles[0],
|
||||
logo: avatars[0],
|
||||
description: '那是一种内在的东西,他们到达不了,也无法触及的',
|
||||
updatedAt: new Date(),
|
||||
member: '科学搬砖组',
|
||||
href: '',
|
||||
memberLink: '',
|
||||
},
|
||||
{
|
||||
id: 'xxx2',
|
||||
title: titles[1],
|
||||
logo: avatars[1],
|
||||
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
|
||||
updatedAt: new Date('2017-07-24'),
|
||||
member: '全组都是吴彦祖',
|
||||
href: '',
|
||||
memberLink: '',
|
||||
},
|
||||
{
|
||||
id: 'xxx3',
|
||||
title: titles[2],
|
||||
logo: avatars[2],
|
||||
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
|
||||
updatedAt: new Date(),
|
||||
member: '中二少女团',
|
||||
href: '',
|
||||
memberLink: '',
|
||||
},
|
||||
{
|
||||
id: 'xxx4',
|
||||
title: titles[3],
|
||||
logo: avatars[3],
|
||||
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
|
||||
updatedAt: new Date('2017-07-23'),
|
||||
member: '程序员日常',
|
||||
href: '',
|
||||
memberLink: '',
|
||||
},
|
||||
{
|
||||
id: 'xxx5',
|
||||
title: titles[4],
|
||||
logo: avatars[4],
|
||||
description: '凛冬将至',
|
||||
updatedAt: new Date('2017-07-23'),
|
||||
member: '高逼格设计天团',
|
||||
href: '',
|
||||
memberLink: '',
|
||||
},
|
||||
{
|
||||
id: 'xxx6',
|
||||
title: titles[5],
|
||||
logo: avatars[5],
|
||||
description: '生命就像一盒巧克力,结果往往出人意料',
|
||||
updatedAt: new Date('2017-07-23'),
|
||||
member: '骗你来学计算机',
|
||||
href: '',
|
||||
memberLink: '',
|
||||
},
|
||||
],
|
||||
msg: '操作成功',
|
||||
};
|
||||
|
||||
const getActivities = [
|
||||
{
|
||||
id: 'trend-1',
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
name: '曲丽丽',
|
||||
avatar: avatars2[0],
|
||||
},
|
||||
group: {
|
||||
name: '高逼格设计天团',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
project: {
|
||||
name: '六月迭代',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
template: '在 @{group} 新建项目 @{project}',
|
||||
},
|
||||
{
|
||||
id: 'trend-2',
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
name: '付小小',
|
||||
avatar: avatars2[1],
|
||||
},
|
||||
group: {
|
||||
name: '高逼格设计天团',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
project: {
|
||||
name: '六月迭代',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
template: '在 @{group} 新建项目 @{project}',
|
||||
},
|
||||
{
|
||||
id: 'trend-3',
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
name: '林东东',
|
||||
avatar: avatars2[2],
|
||||
},
|
||||
group: {
|
||||
name: '中二少女团',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
project: {
|
||||
name: '六月迭代',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
template: '在 @{group} 新建项目 @{project}',
|
||||
},
|
||||
{
|
||||
id: 'trend-4',
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
name: '周星星',
|
||||
avatar: avatars2[4],
|
||||
},
|
||||
project: {
|
||||
name: '5 月日常迭代',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
template: '将 @{project} 更新至已发布状态',
|
||||
},
|
||||
{
|
||||
id: 'trend-5',
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
name: '朱偏右',
|
||||
avatar: avatars2[3],
|
||||
},
|
||||
project: {
|
||||
name: '工程效能',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
comment: {
|
||||
name: '留言',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
template: '在 @{project} 发布了 @{comment}',
|
||||
},
|
||||
{
|
||||
id: 'trend-6',
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
name: '乐哥',
|
||||
avatar: avatars2[5],
|
||||
},
|
||||
group: {
|
||||
name: '程序员日常',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
project: {
|
||||
name: '品牌迭代',
|
||||
link: 'http://github.com/',
|
||||
},
|
||||
template: '在 @{group} 新建项目 @{project}',
|
||||
},
|
||||
];
|
||||
|
||||
function getFakeCaptcha(req, res) {
|
||||
return res.json('captcha-xxx');
|
||||
}
|
||||
|
||||
export default {
|
||||
'GET /api/blade-desk/notice/notices': getNotice,
|
||||
'GET /api/blade-desk/dashboard/activities': getActivities,
|
||||
'POST /api/forms': (req, res) => {
|
||||
res.send({ message: 'Ok' });
|
||||
},
|
||||
'GET /api/tags': mockjs.mock({
|
||||
'list|100': [{ name: '@city', 'value|1-100': 150, 'type|0-2': 1 }],
|
||||
}),
|
||||
'GET /api/fake_list': getFakeList,
|
||||
'POST /api/fake_list': postFakeList,
|
||||
'GET /api/captcha': getFakeCaptcha,
|
||||
};
|
196
mock/chart.js
Normal file
196
mock/chart.js
Normal file
@ -0,0 +1,196 @@
|
||||
import moment from 'moment';
|
||||
|
||||
// mock data
|
||||
const visitData = [];
|
||||
const beginDay = new Date().getTime();
|
||||
|
||||
const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5];
|
||||
for (let i = 0; i < fakeY.length; i += 1) {
|
||||
visitData.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: fakeY[i],
|
||||
});
|
||||
}
|
||||
|
||||
const visitData2 = [];
|
||||
const fakeY2 = [1, 6, 4, 8, 3, 7, 2];
|
||||
for (let i = 0; i < fakeY2.length; i += 1) {
|
||||
visitData2.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: fakeY2[i],
|
||||
});
|
||||
}
|
||||
|
||||
const salesData = [];
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
salesData.push({
|
||||
x: `${i + 1}月`,
|
||||
y: Math.floor(Math.random() * 1000) + 200,
|
||||
});
|
||||
}
|
||||
const searchData = [];
|
||||
for (let i = 0; i < 50; i += 1) {
|
||||
searchData.push({
|
||||
index: i + 1,
|
||||
keyword: `搜索关键词-${i}`,
|
||||
count: Math.floor(Math.random() * 1000),
|
||||
range: Math.floor(Math.random() * 100),
|
||||
status: Math.floor((Math.random() * 10) % 2),
|
||||
});
|
||||
}
|
||||
const salesTypeData = [
|
||||
{
|
||||
x: '家用电器',
|
||||
y: 4544,
|
||||
},
|
||||
{
|
||||
x: '食用酒水',
|
||||
y: 3321,
|
||||
},
|
||||
{
|
||||
x: '个护健康',
|
||||
y: 3113,
|
||||
},
|
||||
{
|
||||
x: '服饰箱包',
|
||||
y: 2341,
|
||||
},
|
||||
{
|
||||
x: '母婴产品',
|
||||
y: 1231,
|
||||
},
|
||||
{
|
||||
x: '其他',
|
||||
y: 1231,
|
||||
},
|
||||
];
|
||||
|
||||
const salesTypeDataOnline = [
|
||||
{
|
||||
x: '家用电器',
|
||||
y: 244,
|
||||
},
|
||||
{
|
||||
x: '食用酒水',
|
||||
y: 321,
|
||||
},
|
||||
{
|
||||
x: '个护健康',
|
||||
y: 311,
|
||||
},
|
||||
{
|
||||
x: '服饰箱包',
|
||||
y: 41,
|
||||
},
|
||||
{
|
||||
x: '母婴产品',
|
||||
y: 121,
|
||||
},
|
||||
{
|
||||
x: '其他',
|
||||
y: 111,
|
||||
},
|
||||
];
|
||||
|
||||
const salesTypeDataOffline = [
|
||||
{
|
||||
x: '家用电器',
|
||||
y: 99,
|
||||
},
|
||||
{
|
||||
x: '食用酒水',
|
||||
y: 188,
|
||||
},
|
||||
{
|
||||
x: '个护健康',
|
||||
y: 344,
|
||||
},
|
||||
{
|
||||
x: '服饰箱包',
|
||||
y: 255,
|
||||
},
|
||||
{
|
||||
x: '其他',
|
||||
y: 65,
|
||||
},
|
||||
];
|
||||
|
||||
const offlineData = [];
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
offlineData.push({
|
||||
name: `Stores ${i}`,
|
||||
cvr: Math.ceil(Math.random() * 9) / 10,
|
||||
});
|
||||
}
|
||||
const offlineChartData = [];
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
offlineChartData.push({
|
||||
x: new Date().getTime() + 1000 * 60 * 30 * i,
|
||||
y1: Math.floor(Math.random() * 100) + 10,
|
||||
y2: Math.floor(Math.random() * 100) + 10,
|
||||
});
|
||||
}
|
||||
|
||||
const radarOriginData = [
|
||||
{
|
||||
name: '个人',
|
||||
ref: 10,
|
||||
koubei: 8,
|
||||
output: 4,
|
||||
contribute: 5,
|
||||
hot: 7,
|
||||
},
|
||||
{
|
||||
name: '团队',
|
||||
ref: 3,
|
||||
koubei: 9,
|
||||
output: 6,
|
||||
contribute: 3,
|
||||
hot: 1,
|
||||
},
|
||||
{
|
||||
name: '部门',
|
||||
ref: 4,
|
||||
koubei: 1,
|
||||
output: 6,
|
||||
contribute: 5,
|
||||
hot: 7,
|
||||
},
|
||||
];
|
||||
|
||||
const radarData = [];
|
||||
const radarTitleMap = {
|
||||
ref: '引用',
|
||||
koubei: '口碑',
|
||||
output: '产量',
|
||||
contribute: '贡献',
|
||||
hot: '热度',
|
||||
};
|
||||
radarOriginData.forEach(item => {
|
||||
Object.keys(item).forEach(key => {
|
||||
if (key !== 'name') {
|
||||
radarData.push({
|
||||
name: item.name,
|
||||
label: radarTitleMap[key],
|
||||
value: item[key],
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const getFakeChartData = {
|
||||
visitData,
|
||||
visitData2,
|
||||
salesData,
|
||||
searchData,
|
||||
offlineData,
|
||||
offlineChartData,
|
||||
salesTypeData,
|
||||
salesTypeDataOnline,
|
||||
salesTypeDataOffline,
|
||||
radarData,
|
||||
};
|
||||
|
||||
export default {
|
||||
'GET /api/fake_chart_data': getFakeChartData,
|
||||
};
|
55
mock/code.js
Normal file
55
mock/code.js
Normal file
@ -0,0 +1,55 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push({
|
||||
id: '1',
|
||||
serviceName: 'blade-demo',
|
||||
codeName: '通知公告',
|
||||
tableName: 'blade_notice',
|
||||
tablePrefix: 'blade_',
|
||||
pkName: 'id',
|
||||
packageName: 'org.springblade.desk',
|
||||
});
|
||||
json.data = {
|
||||
total: 10,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 1,
|
||||
records: list,
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '1',
|
||||
serviceName: 'blade-demo',
|
||||
codeName: '通知公告',
|
||||
tableName: 'blade_notice',
|
||||
pkName: 'id',
|
||||
tablePrefix: 'blade_',
|
||||
packageName: 'org.springblade.desk',
|
||||
apiPath: 'D:\\Develop\\WorkSpace\\Git\\SpringBlade\\blade-ops\\blade-develop',
|
||||
webPath: 'D:\\Develop\\WorkSpace\\Git\\Sword',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-develop/code/list': getFakeList,
|
||||
'GET /api/blade-develop/code/detail': getFakeDetail,
|
||||
'POST /api/blade-develop/code/submit': fakeSuccess,
|
||||
'POST /api/blade-develop/code/remove': fakeSuccess,
|
||||
'POST /api/blade-develop/code/gen-code': fakeSuccess,
|
||||
};
|
||||
export default delay(proxy, 500);
|
83
mock/dept.js
Normal file
83
mock/dept.js
Normal file
@ -0,0 +1,83 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const data = [];
|
||||
data.push({
|
||||
id: '1',
|
||||
deptName: '刀锋科技',
|
||||
fullName: '江苏刀锋科技有限公司',
|
||||
sort: '1',
|
||||
children: [
|
||||
{
|
||||
id: '2',
|
||||
deptName: '常州刀锋',
|
||||
fullName: '常州刀锋科技有限公司',
|
||||
sort: '1',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
deptName: '南京刀锋',
|
||||
fullName: '南京刀锋科技有限公司',
|
||||
sort: '2',
|
||||
},
|
||||
],
|
||||
});
|
||||
json.data = data;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: 2,
|
||||
parentId: 1,
|
||||
parentName: '江苏刀锋',
|
||||
deptName: '常州刀锋',
|
||||
fullName: '常州刀锋科技有限公司',
|
||||
sort: 1,
|
||||
nextSort: 4,
|
||||
remark: '测试备注',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeTree(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push({
|
||||
title: '江苏刀锋',
|
||||
value: '1',
|
||||
key: '1',
|
||||
children: [
|
||||
{
|
||||
title: '常州刀锋',
|
||||
value: '2',
|
||||
key: '2',
|
||||
},
|
||||
{
|
||||
title: '南京刀锋',
|
||||
value: '3',
|
||||
key: '3',
|
||||
},
|
||||
],
|
||||
});
|
||||
json.data = list;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-system/dept/list': getFakeList,
|
||||
'GET /api/blade-system/dept/detail': getFakeDetail,
|
||||
'GET /api/blade-system/dept/tree': getFakeTree,
|
||||
'POST /api/blade-system/dept/submit': fakeSuccess,
|
||||
'POST /api/blade-system/dept/remove': fakeSuccess,
|
||||
};
|
||||
|
||||
export default delay(proxy, 500);
|
218
mock/dict.js
Normal file
218
mock/dict.js
Normal file
@ -0,0 +1,218 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeDictionary(req, res) {
|
||||
const params = req.query;
|
||||
const { code } = params;
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const dict = [];
|
||||
if (code === 'notice') {
|
||||
dict.push(
|
||||
{
|
||||
dictKey: '1',
|
||||
dictValue: '发布通知',
|
||||
},
|
||||
{
|
||||
dictKey: '2',
|
||||
dictValue: '批转通知',
|
||||
},
|
||||
{
|
||||
dictKey: '3',
|
||||
dictValue: '转发通知',
|
||||
},
|
||||
{
|
||||
dictKey: '4',
|
||||
dictValue: '指示通知',
|
||||
},
|
||||
{
|
||||
dictKey: '5',
|
||||
dictValue: '任免通知',
|
||||
},
|
||||
{
|
||||
dictKey: '6',
|
||||
dictValue: '事务通知',
|
||||
}
|
||||
);
|
||||
json.data = dict;
|
||||
}
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const data = [];
|
||||
data.push(
|
||||
{
|
||||
id: '1',
|
||||
code: 'sex',
|
||||
dictKey: '-1',
|
||||
dictValue: '性别',
|
||||
sort: '1',
|
||||
children: [
|
||||
{
|
||||
id: '2',
|
||||
code: 'sex',
|
||||
dictKey: '1',
|
||||
dictValue: '男',
|
||||
sort: '1',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
code: 'sex',
|
||||
dictKey: '2',
|
||||
dictValue: '女',
|
||||
sort: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
code: 'notice',
|
||||
dictKey: '-1',
|
||||
dictValue: '通知类型',
|
||||
sort: '1',
|
||||
children: [
|
||||
{
|
||||
id: '5',
|
||||
code: 'notice',
|
||||
dictKey: '1',
|
||||
dictValue: '发布通知',
|
||||
sort: '1',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
code: 'notice',
|
||||
dictKey: '2',
|
||||
dictValue: '批转通知',
|
||||
sort: '2',
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
code: 'notice',
|
||||
dictKey: '3',
|
||||
dictValue: '转发通知',
|
||||
sort: '3',
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
code: 'notice',
|
||||
dictKey: '4',
|
||||
dictValue: '指示通知',
|
||||
sort: '4',
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
code: 'notice',
|
||||
dictKey: '5',
|
||||
dictValue: '任免通知',
|
||||
sort: '5',
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
code: 'notice',
|
||||
dictKey: '6',
|
||||
dictValue: '事务通知',
|
||||
sort: '6',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
json.data = data;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: 2,
|
||||
parentId: 1,
|
||||
parentName: '性别',
|
||||
code: 'sex',
|
||||
dictKey: 1,
|
||||
dictValue: '男',
|
||||
sort: 1,
|
||||
remark: '测试备注',
|
||||
nextKey: 3,
|
||||
nextSort: 3,
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeTree(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
title: '性别',
|
||||
value: '1',
|
||||
key: '1',
|
||||
children: [
|
||||
{
|
||||
title: '男',
|
||||
value: '2',
|
||||
key: '2',
|
||||
},
|
||||
{
|
||||
title: '女',
|
||||
value: '3',
|
||||
key: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '通知类型',
|
||||
value: '4',
|
||||
key: '4',
|
||||
children: [
|
||||
{
|
||||
title: '发布通知',
|
||||
value: '5',
|
||||
key: '5',
|
||||
},
|
||||
{
|
||||
title: '批转通知',
|
||||
value: '6',
|
||||
key: '6',
|
||||
},
|
||||
{
|
||||
title: '转发通知',
|
||||
value: '7',
|
||||
key: '7',
|
||||
},
|
||||
{
|
||||
title: '指示通知',
|
||||
value: '8',
|
||||
key: '8',
|
||||
},
|
||||
{
|
||||
title: '任免通知',
|
||||
value: '9',
|
||||
key: '9',
|
||||
},
|
||||
{
|
||||
title: '事务通知',
|
||||
value: '10',
|
||||
key: '10',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
json.data = list;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-system/dict/dictionary': getFakeDictionary,
|
||||
'GET /api/blade-system/dict/list': getFakeList,
|
||||
'GET /api/blade-system/dict/detail': getFakeDetail,
|
||||
'GET /api/blade-system/dict/tree': getFakeTree,
|
||||
'POST /api/blade-system/dict/submit': fakeSuccess,
|
||||
'POST /api/blade-system/dict/remove': fakeSuccess,
|
||||
};
|
||||
|
||||
export default delay(proxy, 500);
|
15
mock/geographic.js
Normal file
15
mock/geographic.js
Normal file
@ -0,0 +1,15 @@
|
||||
import city from './geographic/city.json';
|
||||
import province from './geographic/province.json';
|
||||
|
||||
function getProvince(req, res) {
|
||||
return res.json(province);
|
||||
}
|
||||
|
||||
function getCity(req, res) {
|
||||
return res.json(city[req.params.province]);
|
||||
}
|
||||
|
||||
export default {
|
||||
'GET /api/geographic/province': getProvince,
|
||||
'GET /api/geographic/city/:province': getCity,
|
||||
};
|
1784
mock/geographic/city.json
Normal file
1784
mock/geographic/city.json
Normal file
File diff suppressed because it is too large
Load Diff
138
mock/geographic/province.json
Normal file
138
mock/geographic/province.json
Normal file
@ -0,0 +1,138 @@
|
||||
[
|
||||
{
|
||||
"name": "北京市",
|
||||
"id": "110000"
|
||||
},
|
||||
{
|
||||
"name": "天津市",
|
||||
"id": "120000"
|
||||
},
|
||||
{
|
||||
"name": "河北省",
|
||||
"id": "130000"
|
||||
},
|
||||
{
|
||||
"name": "山西省",
|
||||
"id": "140000"
|
||||
},
|
||||
{
|
||||
"name": "内蒙古自治区",
|
||||
"id": "150000"
|
||||
},
|
||||
{
|
||||
"name": "辽宁省",
|
||||
"id": "210000"
|
||||
},
|
||||
{
|
||||
"name": "吉林省",
|
||||
"id": "220000"
|
||||
},
|
||||
{
|
||||
"name": "黑龙江省",
|
||||
"id": "230000"
|
||||
},
|
||||
{
|
||||
"name": "上海市",
|
||||
"id": "310000"
|
||||
},
|
||||
{
|
||||
"name": "江苏省",
|
||||
"id": "320000"
|
||||
},
|
||||
{
|
||||
"name": "浙江省",
|
||||
"id": "330000"
|
||||
},
|
||||
{
|
||||
"name": "安徽省",
|
||||
"id": "340000"
|
||||
},
|
||||
{
|
||||
"name": "福建省",
|
||||
"id": "350000"
|
||||
},
|
||||
{
|
||||
"name": "江西省",
|
||||
"id": "360000"
|
||||
},
|
||||
{
|
||||
"name": "山东省",
|
||||
"id": "370000"
|
||||
},
|
||||
{
|
||||
"name": "河南省",
|
||||
"id": "410000"
|
||||
},
|
||||
{
|
||||
"name": "湖北省",
|
||||
"id": "420000"
|
||||
},
|
||||
{
|
||||
"name": "湖南省",
|
||||
"id": "430000"
|
||||
},
|
||||
{
|
||||
"name": "广东省",
|
||||
"id": "440000"
|
||||
},
|
||||
{
|
||||
"name": "广西壮族自治区",
|
||||
"id": "450000"
|
||||
},
|
||||
{
|
||||
"name": "海南省",
|
||||
"id": "460000"
|
||||
},
|
||||
{
|
||||
"name": "重庆市",
|
||||
"id": "500000"
|
||||
},
|
||||
{
|
||||
"name": "四川省",
|
||||
"id": "510000"
|
||||
},
|
||||
{
|
||||
"name": "贵州省",
|
||||
"id": "520000"
|
||||
},
|
||||
{
|
||||
"name": "云南省",
|
||||
"id": "530000"
|
||||
},
|
||||
{
|
||||
"name": "西藏自治区",
|
||||
"id": "540000"
|
||||
},
|
||||
{
|
||||
"name": "陕西省",
|
||||
"id": "610000"
|
||||
},
|
||||
{
|
||||
"name": "甘肃省",
|
||||
"id": "620000"
|
||||
},
|
||||
{
|
||||
"name": "青海省",
|
||||
"id": "630000"
|
||||
},
|
||||
{
|
||||
"name": "宁夏回族自治区",
|
||||
"id": "640000"
|
||||
},
|
||||
{
|
||||
"name": "新疆维吾尔自治区",
|
||||
"id": "650000"
|
||||
},
|
||||
{
|
||||
"name": "台湾省",
|
||||
"id": "710000"
|
||||
},
|
||||
{
|
||||
"name": "香港特别行政区",
|
||||
"id": "810000"
|
||||
},
|
||||
{
|
||||
"name": "澳门特别行政区",
|
||||
"id": "820000"
|
||||
}
|
||||
]
|
324
mock/log.js
Normal file
324
mock/log.js
Normal file
@ -0,0 +1,324 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeUsualList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
id: '1',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
logLevel: 'info',
|
||||
logId: 'test',
|
||||
logData: '测试日志1',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
logLevel: 'info',
|
||||
logId: 'test',
|
||||
logData: '测试日志2',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
logLevel: 'info',
|
||||
logId: 'test',
|
||||
logData: '测试日志3',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
}
|
||||
);
|
||||
json.data = {
|
||||
total: 10,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 1,
|
||||
records: list,
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeUsualDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '3',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
logLevel: 'info',
|
||||
logId: 'test',
|
||||
logData: '测试日志3',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
|
||||
params: "{'name':'test'}",
|
||||
createBy: 'admin',
|
||||
createTime: '2018-12-21 12:00:00',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeApiList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
id: '1',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
title: '测试日志1',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
title: '测试日志2',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
title: '测试日志3',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
}
|
||||
);
|
||||
json.data = {
|
||||
total: 10,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 1,
|
||||
records: list,
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeApiDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '3',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
title: '测试日志3',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
|
||||
remoteIp: '0:0:0:0:0:0:0:1',
|
||||
methodClass: 'org.springblade.auth.controller.AuthController',
|
||||
methodName: 'token',
|
||||
time: '19',
|
||||
params: "{'account':'test'}",
|
||||
createBy: 'admin',
|
||||
createTime: '2018-12-21 12:00:00',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeErrorList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
id: '1',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
}
|
||||
);
|
||||
json.data = {
|
||||
total: 10,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 1,
|
||||
records: list,
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeErrorDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '3',
|
||||
serviceId: 'blade-auth',
|
||||
serverHost: 'blade',
|
||||
serverIp: '192.168.0.1',
|
||||
env: 'dev',
|
||||
method: 'get',
|
||||
requestUri: '/token',
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
|
||||
stackTrace:
|
||||
'java.lang.ArithmeticException: / by zero\n' +
|
||||
'\tat org.springblade.auth.controller.AuthController.token(AuthController.java:58)\n' +
|
||||
'\tat org.springblade.auth.controller.AuthController$$FastClassBySpringCGLIB$$98d484bd.invoke()\n' +
|
||||
'\tat org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)\n' +
|
||||
'\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)\n' +
|
||||
'\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n' +
|
||||
'\tat org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)\n' +
|
||||
'\tat org.springblade.core.log.aspect.ApiLogAspect.around(ApiLogAspect.java:42)\n' +
|
||||
'\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n' +
|
||||
'\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n' +
|
||||
'\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n' +
|
||||
'\tat java.lang.reflect.Method.invoke(Method.java:498)\n' +
|
||||
'\tat org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n' +
|
||||
'\tat org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n' +
|
||||
'\tat org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n' +
|
||||
'\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)\n' +
|
||||
'\tat org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)\n' +
|
||||
'\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n' +
|
||||
'\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)\n' +
|
||||
'\tat org.springblade.auth.controller.AuthController$$EnhancerBySpringCGLIB$$e4cbcf2e.token()\n' +
|
||||
'\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n' +
|
||||
'\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n' +
|
||||
'\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n' +
|
||||
'\tat java.lang.reflect.Method.invoke(Method.java:498)\n' +
|
||||
'\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)\n' +
|
||||
'\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)\n' +
|
||||
'\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n' +
|
||||
'\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891)\n' +
|
||||
'\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\n' +
|
||||
'\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n' +
|
||||
'\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)\n' +
|
||||
'\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)\n' +
|
||||
'\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)\n' +
|
||||
'\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)\n' +
|
||||
'\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:707)\n' +
|
||||
'\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)\n' +
|
||||
'\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:790)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)\n' +
|
||||
'\tat org.springblade.core.tool.support.xss.XssFilter.doFilter(XssFilter.java:40)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)\n' +
|
||||
'\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n' +
|
||||
'\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)\n' +
|
||||
'\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\n' +
|
||||
'\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:155)\n' +
|
||||
'\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:123)\n' +
|
||||
'\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108)\n' +
|
||||
'\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n' +
|
||||
'\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n' +
|
||||
'\tat io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)\n' +
|
||||
'\tat io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)\n' +
|
||||
'\tat io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:65)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)\n' +
|
||||
'\tat io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)\n' +
|
||||
'\tat io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)\n' +
|
||||
'\tat io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)\n' +
|
||||
'\tat io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)\n' +
|
||||
'\tat io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)\n' +
|
||||
'\tat io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)\n' +
|
||||
'\tat io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)\n' +
|
||||
'\tat io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)\n' +
|
||||
'\tat io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)\n' +
|
||||
'\tat io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)\n' +
|
||||
'\tat io.undertow.servlet.handlers.SessionRestoringHandler.handleRequest(SessionRestoringHandler.java:119)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)\n' +
|
||||
'\tat io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)\n' +
|
||||
'\tat io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)\n' +
|
||||
'\tat io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)\n' +
|
||||
'\tat io.undertow.server.Connectors.executeRootHandler(Connectors.java:336)\n' +
|
||||
'\tat io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)\n' +
|
||||
'\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n' +
|
||||
'\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n' +
|
||||
'\tat java.lang.Thread.run(Thread.java:748)\n',
|
||||
exceptionName: 'java.lang.ArithmeticException',
|
||||
message: '/ by zero',
|
||||
lineNumber: '58',
|
||||
methodClass: 'org.springblade.auth.controller.AuthController',
|
||||
fileName: 'AuthController.java',
|
||||
methodName: 'token',
|
||||
params: "{name:'test'}",
|
||||
createBy: 'admin',
|
||||
createTime: '2018-12-21 12:00:00',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-log/usual/list': getFakeUsualList,
|
||||
'GET /api/blade-log/usual/detail': getFakeUsualDetail,
|
||||
'GET /api/blade-log/api/list': getFakeApiList,
|
||||
'GET /api/blade-log/api/detail': getFakeApiDetail,
|
||||
'GET /api/blade-log/error/list': getFakeErrorList,
|
||||
'GET /api/blade-log/error/detail': getFakeErrorDetail,
|
||||
};
|
||||
export default delay(proxy, 500);
|
878
mock/menu.js
Normal file
878
mock/menu.js
Normal file
@ -0,0 +1,878 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeRoutes(req, res) {
|
||||
const json = {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
path: '/desk',
|
||||
code: 'desk',
|
||||
source: 'desktop',
|
||||
children: [
|
||||
{
|
||||
path: '/desk/notice',
|
||||
code: 'notice',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system',
|
||||
code: 'system',
|
||||
source: 'setting',
|
||||
children: [
|
||||
{
|
||||
path: '/system/user',
|
||||
code: 'user',
|
||||
},
|
||||
{
|
||||
path: '/system/dept',
|
||||
code: 'dept',
|
||||
},
|
||||
{
|
||||
path: '/system/dict',
|
||||
code: 'dict',
|
||||
},
|
||||
{
|
||||
path: '/system/menu',
|
||||
code: 'menu',
|
||||
},
|
||||
{
|
||||
path: '/system/role',
|
||||
code: 'role',
|
||||
},
|
||||
{
|
||||
path: '/system/param',
|
||||
code: 'param',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/monitor',
|
||||
code: 'monitor',
|
||||
source: 'fund',
|
||||
children: [
|
||||
{
|
||||
path: 'http://localhost/doc.html',
|
||||
target: '_blank',
|
||||
code: 'doc',
|
||||
},
|
||||
{
|
||||
path: 'http://localhost:7002',
|
||||
target: '_blank',
|
||||
code: 'admin',
|
||||
},
|
||||
{
|
||||
path: '/monitor/log',
|
||||
code: 'log',
|
||||
children: [
|
||||
{
|
||||
path: '/monitor/log/usual',
|
||||
code: 'log_usual',
|
||||
},
|
||||
{
|
||||
path: '/monitor/log/api',
|
||||
code: 'log_api',
|
||||
},
|
||||
{
|
||||
path: '/monitor/log/error',
|
||||
code: 'log_error',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/tool',
|
||||
code: 'tool',
|
||||
source: 'tool',
|
||||
children: [
|
||||
{
|
||||
path: '/tool/code',
|
||||
code: 'code',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
msg: '操作成功',
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeButtons(req, res) {
|
||||
const json = {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
code: 'notice',
|
||||
children: [
|
||||
{
|
||||
code: 'notice_add',
|
||||
name: '新增',
|
||||
path: '/desk/notice/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'notice_edit',
|
||||
name: '修改',
|
||||
path: '/desk/notice/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'notice_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/dept/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'notice_view',
|
||||
name: '查看',
|
||||
path: '/desk/notice/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'user',
|
||||
children: [
|
||||
{
|
||||
code: 'user_add',
|
||||
name: '新增',
|
||||
path: '/system/user/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'user_edit',
|
||||
name: '修改',
|
||||
path: '/system/user/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'user_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/user/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'user_role',
|
||||
name: '角色配置',
|
||||
path: '',
|
||||
source: 'user-add',
|
||||
action: 1,
|
||||
alias: 'role',
|
||||
},
|
||||
{
|
||||
code: 'user_reset',
|
||||
name: '密码重置',
|
||||
path: '/api/blade-system/user/reset-password',
|
||||
source: 'retweet',
|
||||
action: 1,
|
||||
alias: 'reset-password',
|
||||
},
|
||||
{
|
||||
code: 'user_view',
|
||||
name: '查看',
|
||||
path: '/system/user/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'dept',
|
||||
children: [
|
||||
{
|
||||
code: 'dept_add',
|
||||
name: '新增',
|
||||
path: '/system/dept/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'dept_edit',
|
||||
name: '修改',
|
||||
path: '/system/dept/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'dept_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/dept/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'dept_view',
|
||||
name: '查看',
|
||||
path: '/system/dept/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'dict',
|
||||
children: [
|
||||
{
|
||||
code: 'dict_add',
|
||||
name: '新增',
|
||||
path: '/system/dict/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'dict_edit',
|
||||
name: '修改',
|
||||
path: '/system/dict/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'dict_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/dict/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'dict_view',
|
||||
name: '查看',
|
||||
path: '/system/dict/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'menu',
|
||||
children: [
|
||||
{
|
||||
code: 'menu_add',
|
||||
name: '新增',
|
||||
path: '/system/menu/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'menu_edit',
|
||||
name: '修改',
|
||||
path: '/system/menu/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'menu_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/menu/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'menu_view',
|
||||
name: '查看',
|
||||
path: '/system/menu/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'role',
|
||||
children: [
|
||||
{
|
||||
code: 'role_add',
|
||||
name: '新增',
|
||||
path: '/system/role/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'role_edit',
|
||||
name: '修改',
|
||||
path: '/system/role/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'role_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/role/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'role_view',
|
||||
name: '查看',
|
||||
path: '/system/role/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'param',
|
||||
children: [
|
||||
{
|
||||
code: 'param_add',
|
||||
name: '新增',
|
||||
path: '/system/param/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'param_edit',
|
||||
name: '修改',
|
||||
path: '/system/param/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'param_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-system/param/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'param_view',
|
||||
name: '查看',
|
||||
path: '/system/param/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'log_usual',
|
||||
children: [
|
||||
{
|
||||
code: 'log_usual_view',
|
||||
name: '查看',
|
||||
path: '/monitor/log/usual/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'log_error',
|
||||
children: [
|
||||
{
|
||||
code: 'log_error_view',
|
||||
name: '查看',
|
||||
path: '/monitor/log/error/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'log_api',
|
||||
children: [
|
||||
{
|
||||
code: 'log_api_view',
|
||||
name: '查看',
|
||||
path: '/monitor/log/api/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'code',
|
||||
children: [
|
||||
{
|
||||
code: 'code_add',
|
||||
name: '新增',
|
||||
path: '/tool/code/add',
|
||||
source: 'plus',
|
||||
action: 1,
|
||||
alias: 'add',
|
||||
},
|
||||
{
|
||||
code: 'code_edit',
|
||||
name: '修改',
|
||||
path: '/tool/code/edit',
|
||||
source: 'form',
|
||||
action: 2,
|
||||
alias: 'edit',
|
||||
},
|
||||
{
|
||||
code: 'code_delete',
|
||||
name: '删除',
|
||||
path: '/api/blade-develop/code/remove',
|
||||
source: 'delete',
|
||||
action: 3,
|
||||
alias: 'delete',
|
||||
},
|
||||
{
|
||||
code: 'code_view',
|
||||
name: '查看',
|
||||
path: '/tool/code/view',
|
||||
source: 'file-text',
|
||||
action: 2,
|
||||
alias: 'view',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
msg: '操作成功',
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const data = [];
|
||||
data.push(
|
||||
{
|
||||
id: '1',
|
||||
code: 'desk',
|
||||
parentId: '',
|
||||
name: '工作台',
|
||||
path: '/desk',
|
||||
source: 'desktop',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '1',
|
||||
children: [
|
||||
{
|
||||
id: '2',
|
||||
code: 'notice',
|
||||
parentId: 'desk',
|
||||
name: '通知公告',
|
||||
path: '/desk/notice',
|
||||
source: 'desktop',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
code: 'system',
|
||||
parentId: '',
|
||||
name: '系统管理',
|
||||
path: '/system',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '3',
|
||||
children: [
|
||||
{
|
||||
id: '4',
|
||||
code: 'user',
|
||||
parentId: 'system',
|
||||
name: '用户管理',
|
||||
path: '/system/user',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '4',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
code: 'dept',
|
||||
parentId: 'system',
|
||||
name: '部门管理',
|
||||
path: '/system/dept',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '5',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
code: 'dict',
|
||||
parentId: 'system',
|
||||
name: '字典管理',
|
||||
path: '/system/dict',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '6',
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
code: 'menu',
|
||||
parentId: 'system',
|
||||
name: '菜单管理',
|
||||
path: '/system/menu',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '7',
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
code: 'role',
|
||||
parentId: 'system',
|
||||
name: '角色管理',
|
||||
path: '/system/role',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '8',
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
code: 'parameter',
|
||||
parentId: 'system',
|
||||
name: '参数管理',
|
||||
path: '/system/param',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '9',
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
code: 'log',
|
||||
parentId: 'system',
|
||||
name: '日志管理',
|
||||
path: '/system/log',
|
||||
source: 'setting',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
sort: '10',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
json.data = data;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '2',
|
||||
code: 'notice',
|
||||
parentId: 'desk',
|
||||
parentName: '顶级',
|
||||
name: '通知公告',
|
||||
alias: 'menu',
|
||||
path: '/desk/notice',
|
||||
source: 'desktop',
|
||||
category: '1',
|
||||
categoryName: '菜单',
|
||||
action: 1,
|
||||
actionName: '否',
|
||||
isOpen: 1,
|
||||
isOpenName: '否',
|
||||
sort: '2',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeTree(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
value: 'desk',
|
||||
key: 'desk',
|
||||
title: '工作台',
|
||||
children: [
|
||||
{
|
||||
value: 'notice',
|
||||
key: 'notice',
|
||||
title: '通知公告',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'system',
|
||||
key: 'system',
|
||||
title: '系统管理',
|
||||
children: [
|
||||
{
|
||||
value: 'user',
|
||||
key: 'user',
|
||||
title: '用户管理',
|
||||
},
|
||||
{
|
||||
value: 'dept',
|
||||
key: 'dept',
|
||||
title: '部门管理',
|
||||
},
|
||||
{
|
||||
value: 'dict',
|
||||
key: 'dict',
|
||||
title: '字典管理',
|
||||
},
|
||||
{
|
||||
value: 'menu',
|
||||
key: 'menu',
|
||||
title: '菜单管理',
|
||||
},
|
||||
{
|
||||
value: 'role',
|
||||
key: 'role',
|
||||
title: '角色管理',
|
||||
},
|
||||
{
|
||||
value: 'param',
|
||||
key: 'param',
|
||||
title: '参数管理',
|
||||
},
|
||||
{
|
||||
value: 'log',
|
||||
key: 'log',
|
||||
title: '日志管理',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
json.data = list;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeGrantTree(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
key: 'desk',
|
||||
title: '工作台',
|
||||
children: [
|
||||
{
|
||||
key: 'notice',
|
||||
title: '通知公告',
|
||||
children: [
|
||||
{
|
||||
key: 'notice_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'notice_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'notice_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'notice_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'system',
|
||||
title: '系统管理',
|
||||
children: [
|
||||
{
|
||||
key: 'user',
|
||||
title: '用户管理',
|
||||
children: [
|
||||
{
|
||||
key: 'user_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'user_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'user_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'user_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'dept',
|
||||
title: '部门管理',
|
||||
children: [
|
||||
{
|
||||
key: 'dept_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'dept_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'dept_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'dept_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'dict',
|
||||
title: '字典管理',
|
||||
children: [
|
||||
{
|
||||
key: 'dict_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'dict_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'dict_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'dict_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'menu',
|
||||
title: '菜单管理',
|
||||
children: [
|
||||
{
|
||||
key: 'menu_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'menu_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'menu_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'menu_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'role',
|
||||
title: '角色管理',
|
||||
children: [
|
||||
{
|
||||
key: 'role_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'role_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'role_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'role_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'param',
|
||||
title: '参数管理',
|
||||
children: [
|
||||
{
|
||||
key: 'param_add',
|
||||
title: '新增',
|
||||
},
|
||||
{
|
||||
key: 'param_edit',
|
||||
title: '修改',
|
||||
},
|
||||
{
|
||||
key: 'param_delete',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
key: 'param_view',
|
||||
title: '查看',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
json.data = list;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeAuthRoutes(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
json.data = {
|
||||
'/form/advanced-form': { authority: ['admin', 'user'] },
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeRoleTreeKeys(req, res) {
|
||||
const json = { code: 200, success: true, data: ['1'], msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-system/menu/routes': getFakeRoutes,
|
||||
'GET /api/blade-system/menu/buttons': getFakeButtons,
|
||||
'GET /api/blade-system/menu/list': getFakeList,
|
||||
'GET /api/blade-system/menu/detail': getFakeDetail,
|
||||
'GET /api/blade-system/menu/tree': getFakeTree,
|
||||
'GET /api/blade-system/menu/grant-tree': getFakeGrantTree,
|
||||
'GET /api/blade-system/menu/role-tree-keys': getFakeRoleTreeKeys,
|
||||
'GET /api/blade-system/menu/auth-routes': getFakeAuthRoutes,
|
||||
'POST /api/blade-system/menu/submit': fakeSuccess,
|
||||
'POST /api/blade-system/menu/remove': fakeSuccess,
|
||||
};
|
||||
export default delay(proxy, 500);
|
149
mock/notice.js
Normal file
149
mock/notice.js
Normal file
@ -0,0 +1,149 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-desk/notice/list': {
|
||||
code: 200,
|
||||
data: {
|
||||
total: 15,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 2,
|
||||
records: [
|
||||
{
|
||||
id: '1',
|
||||
title: '博客标题1',
|
||||
categoryName: '批转通知',
|
||||
content: '博客内容1',
|
||||
date: '2018-05-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '博客标题2',
|
||||
categoryName: '发布通知',
|
||||
content: '博客内容2',
|
||||
date: '2018-06-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '博客标题3',
|
||||
categoryName: '任免通知',
|
||||
content: '博客内容3',
|
||||
date: '2018-07-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: '博客标题4',
|
||||
categoryName: '指示通知',
|
||||
content: '博客内容4',
|
||||
date: '2018-08-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: '博客标题5',
|
||||
categoryName: '转发通知',
|
||||
content: '博客内容5',
|
||||
date: '2018-09-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: '博客标题1',
|
||||
categoryName: '批转通知',
|
||||
content: '博客内容1',
|
||||
date: '2018-05-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
title: '博客标题2',
|
||||
categoryName: '发布通知',
|
||||
content: '博客内容2',
|
||||
date: '2018-06-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
title: '博客标题3',
|
||||
categoryName: '任免通知',
|
||||
content: '博客内容3',
|
||||
date: '2018-07-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
title: '博客标题4',
|
||||
categoryName: '指示通知',
|
||||
content: '博客内容4',
|
||||
date: '2018-08-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
title: '博客标题5',
|
||||
categoryName: '转发通知',
|
||||
content: '博客内容5',
|
||||
date: '2018-09-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
title: '博客标题1',
|
||||
categoryName: '批转通知',
|
||||
content: '博客内容1',
|
||||
date: '2018-05-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '12',
|
||||
title: '博客标题2',
|
||||
categoryName: '发布通知',
|
||||
content: '博客内容2',
|
||||
date: '2018-06-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '13',
|
||||
title: '博客标题3',
|
||||
categoryName: '任免通知',
|
||||
content: '博客内容3',
|
||||
date: '2018-07-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '14',
|
||||
title: '博客标题4',
|
||||
categoryName: '指示通知',
|
||||
content: '博客内容4',
|
||||
date: '2018-08-08 12:00:00',
|
||||
},
|
||||
{
|
||||
id: '15',
|
||||
title: '博客标题5',
|
||||
categoryName: '转发通知',
|
||||
content: '博客内容5',
|
||||
date: '2018-09-08 12:00:00',
|
||||
},
|
||||
],
|
||||
},
|
||||
message: 'success',
|
||||
success: true,
|
||||
},
|
||||
'POST /api/blade-desk/notice/submit': {
|
||||
code: 200,
|
||||
data: {},
|
||||
message: 'success',
|
||||
success: true,
|
||||
},
|
||||
'POST /api/blade-desk/notice/remove': {
|
||||
code: 200,
|
||||
data: {},
|
||||
message: 'success',
|
||||
success: true,
|
||||
},
|
||||
'GET /api/blade-desk/notice/detail': {
|
||||
code: 200,
|
||||
data: {
|
||||
title: '通知标题详情',
|
||||
category: '3',
|
||||
categoryName: '转发通知',
|
||||
date: '2018-12-31 23:33:33',
|
||||
content: '通知公告内容详情',
|
||||
},
|
||||
message: 'success',
|
||||
success: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default delay(proxy, 500);
|
115
mock/notices.js
Normal file
115
mock/notices.js
Normal file
@ -0,0 +1,115 @@
|
||||
const fakeNotices = [
|
||||
{
|
||||
id: '000000001',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
|
||||
title: '你收到了 14 份新周报',
|
||||
datetime: '2017-08-09',
|
||||
type: 'notification',
|
||||
},
|
||||
{
|
||||
id: '000000002',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
|
||||
title: '你推荐的 曲妮妮 已通过第三轮面试',
|
||||
datetime: '2017-08-08',
|
||||
type: 'notification',
|
||||
},
|
||||
{
|
||||
id: '000000003',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
|
||||
title: '这种模板可以区分多种通知类型',
|
||||
datetime: '2017-08-07',
|
||||
read: true,
|
||||
type: 'notification',
|
||||
},
|
||||
{
|
||||
id: '000000004',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
|
||||
title: '左侧图标用于区分不同的类型',
|
||||
datetime: '2017-08-07',
|
||||
type: 'notification',
|
||||
},
|
||||
{
|
||||
id: '000000005',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
|
||||
title: '内容不要超过两行字,超出时自动截断',
|
||||
datetime: '2017-08-07',
|
||||
type: 'notification',
|
||||
},
|
||||
{
|
||||
id: '000000006',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
|
||||
title: '曲丽丽 评论了你',
|
||||
description: '描述信息描述信息描述信息',
|
||||
datetime: '2017-08-07',
|
||||
type: 'message',
|
||||
clickClose: true,
|
||||
},
|
||||
{
|
||||
id: '000000007',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
|
||||
title: '朱偏右 回复了你',
|
||||
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
|
||||
datetime: '2017-08-07',
|
||||
type: 'message',
|
||||
clickClose: true,
|
||||
},
|
||||
{
|
||||
id: '000000008',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
|
||||
title: '标题',
|
||||
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
|
||||
datetime: '2017-08-07',
|
||||
type: 'message',
|
||||
clickClose: true,
|
||||
},
|
||||
{
|
||||
id: '000000009',
|
||||
title: '任务名称',
|
||||
description: '任务需要在 2017-01-12 20:00 前启动',
|
||||
extra: '未开始',
|
||||
status: 'todo',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
id: '000000010',
|
||||
title: '第三方紧急代码变更',
|
||||
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
|
||||
extra: '马上到期',
|
||||
status: 'urgent',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
id: '000000011',
|
||||
title: '信息安全考试',
|
||||
description: '指派竹尔于 2017-01-09 前完成更新并发布',
|
||||
extra: '已耗时 8 天',
|
||||
status: 'doing',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
id: '000000012',
|
||||
title: 'ABCD 版本发布',
|
||||
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
|
||||
extra: '进行中',
|
||||
status: 'processing',
|
||||
type: 'event',
|
||||
},
|
||||
];
|
||||
|
||||
const getNotices = (req, res) => {
|
||||
if (req.query && req.query.type) {
|
||||
const startFrom = parseInt(req.query.lastItemId, 10) + 1;
|
||||
const result = fakeNotices
|
||||
.filter(({ type }) => type === req.query.type)
|
||||
.map((notice, index) => ({
|
||||
...notice,
|
||||
id: `0000000${startFrom + index}`,
|
||||
}));
|
||||
return res.json(startFrom > 24 ? result.concat(null) : result);
|
||||
}
|
||||
return res.json(fakeNotices);
|
||||
};
|
||||
|
||||
export default {
|
||||
'GET /api/blade-desk/notice/my-notices': getNotices,
|
||||
};
|
57
mock/param.js
Normal file
57
mock/param.js
Normal file
@ -0,0 +1,57 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
id: '1',
|
||||
paramName: '是否开启注册功能',
|
||||
paramKey: 'account.registerUser',
|
||||
paramValue: 'true',
|
||||
remark: '描述',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
paramName: '账号初始密码',
|
||||
paramKey: 'account.initPassword',
|
||||
paramValue: '123456',
|
||||
remark: '描述',
|
||||
}
|
||||
);
|
||||
json.data = {
|
||||
total: 10,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 1,
|
||||
records: list,
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '1',
|
||||
paramName: '是否开启注册功能',
|
||||
paramKey: 'account.registerUser',
|
||||
paramValue: 'true',
|
||||
remark: '描述',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-system/param/list': getFakeList,
|
||||
'GET /api/blade-system/param/detail': getFakeDetail,
|
||||
'POST /api/blade-system/param/submit': fakeSuccess,
|
||||
'POST /api/blade-system/param/remove': fakeSuccess,
|
||||
};
|
||||
export default delay(proxy, 500);
|
99
mock/role.js
Normal file
99
mock/role.js
Normal file
@ -0,0 +1,99 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const data = [];
|
||||
data.push(
|
||||
{
|
||||
id: '1',
|
||||
roleName: '超级管理员',
|
||||
roleAlias: 'administrator',
|
||||
sort: '1',
|
||||
children: [
|
||||
{
|
||||
id: '2',
|
||||
roleName: '管理员',
|
||||
roleAlias: 'admin',
|
||||
sort: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
roleName: '用户',
|
||||
roleAlias: 'user',
|
||||
sort: '2',
|
||||
children: [
|
||||
{
|
||||
id: '4',
|
||||
roleName: '普通用户',
|
||||
roleAlias: 'user',
|
||||
sort: '1',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
roleName: '访客',
|
||||
roleAlias: 'guest',
|
||||
sort: '2',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
json.data = data;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: 2,
|
||||
parentId: 1,
|
||||
parentName: '超级管理员',
|
||||
roleName: '用户',
|
||||
roleAlias: 'user',
|
||||
sort: 1,
|
||||
nextSort: 4,
|
||||
remark: '测试备注',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeTree(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push({
|
||||
title: '超级管理员',
|
||||
value: '1',
|
||||
key: '1',
|
||||
children: [
|
||||
{
|
||||
title: '用户',
|
||||
value: '2',
|
||||
key: '2',
|
||||
},
|
||||
{
|
||||
title: '测试',
|
||||
value: '3',
|
||||
key: '3',
|
||||
},
|
||||
],
|
||||
});
|
||||
json.data = list;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
'GET /api/blade-system/role/list': getFakeList,
|
||||
'GET /api/blade-system/role/detail': getFakeDetail,
|
||||
'GET /api/blade-system/role/tree': getFakeTree,
|
||||
'POST /api/blade-system/role/submit': fakeSuccess,
|
||||
'POST /api/blade-system/role/remove': fakeSuccess,
|
||||
'POST /api/blade-system/role/grant': fakeSuccess,
|
||||
};
|
||||
export default delay(proxy, 500);
|
206
mock/user.js
Normal file
206
mock/user.js
Normal file
@ -0,0 +1,206 @@
|
||||
import { delay } from 'roadhog-api-doc';
|
||||
|
||||
function getFakeList(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const list = [];
|
||||
list.push(
|
||||
{
|
||||
id: '1',
|
||||
account: 'admin',
|
||||
name: '超级管理员',
|
||||
realName: '管理员',
|
||||
phone: '13888888888',
|
||||
email: 'admin@springblade.org',
|
||||
roleName: '超级管理员',
|
||||
deptName: '刀锋科技',
|
||||
statusName: '启用',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
account: 'user',
|
||||
name: '系统用户',
|
||||
realName: '用户',
|
||||
phone: '13666666666',
|
||||
email: 'user@springblade.org',
|
||||
roleName: '用户',
|
||||
deptName: '刀锋科技',
|
||||
statusName: '启用',
|
||||
}
|
||||
);
|
||||
json.data = {
|
||||
total: 10,
|
||||
size: 10,
|
||||
current: 1,
|
||||
searchCount: true,
|
||||
pages: 1,
|
||||
records: list,
|
||||
};
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function getFakeDetail(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
const detail = {
|
||||
id: '1',
|
||||
account: 'admin',
|
||||
name: '超级管理员',
|
||||
realName: '管理员',
|
||||
phone: '13888888888',
|
||||
email: 'admin@springblade.org',
|
||||
roleId: 1,
|
||||
roleName: '超级管理员',
|
||||
deptId: 1,
|
||||
deptName: '刀锋科技',
|
||||
sex: 1,
|
||||
sexName: '男',
|
||||
birthday: '2018-12-31 23:33:33',
|
||||
statusName: '启用',
|
||||
};
|
||||
json.data = detail;
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
function fakeSuccess(req, res) {
|
||||
const json = { code: 200, success: true, msg: '操作成功' };
|
||||
return res.json(json);
|
||||
}
|
||||
|
||||
// 代码中会兼容本地 service mock 以及部署站点的静态数据
|
||||
const proxy = {
|
||||
'GET /api/blade-user/list': getFakeList,
|
||||
'GET /api/blade-user/detail': getFakeDetail,
|
||||
'POST /api/blade-user/grant': fakeSuccess,
|
||||
'POST /api/blade-user/reset-password': fakeSuccess,
|
||||
'POST /api/blade-user/submit': fakeSuccess,
|
||||
'POST /api/blade-user/remove': fakeSuccess,
|
||||
|
||||
// 支持值为 Object 和 Array
|
||||
'GET /api/currentUser': {
|
||||
name: 'Serati Ma',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||
userid: '00000001',
|
||||
email: 'antdesign@alipay.com',
|
||||
signature: '海纳百川,有容乃大',
|
||||
title: '交互专家',
|
||||
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
|
||||
tags: [
|
||||
{
|
||||
key: '0',
|
||||
label: '很有想法的',
|
||||
},
|
||||
{
|
||||
key: '1',
|
||||
label: '专注设计',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: '辣~',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: '大长腿',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
label: '川妹子',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
label: '海纳百川',
|
||||
},
|
||||
],
|
||||
notifyCount: 12,
|
||||
unreadCount: 11,
|
||||
country: 'China',
|
||||
geographic: {
|
||||
province: {
|
||||
label: '浙江省',
|
||||
key: '330000',
|
||||
},
|
||||
city: {
|
||||
label: '杭州市',
|
||||
key: '330100',
|
||||
},
|
||||
},
|
||||
address: '西湖区工专路 77 号',
|
||||
phone: '0752-268888888',
|
||||
},
|
||||
// GET POST 可省略
|
||||
'GET /api/users': [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
},
|
||||
],
|
||||
'POST /api/blade-auth/token': (req, res) => {
|
||||
res.send({
|
||||
code: 200,
|
||||
success: true,
|
||||
data: {
|
||||
accessToken:
|
||||
'eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1c2VyIiwiYXVkIjoiYXVkaWVuY2UiLCJyb2xlSWQiOiIxIiwicm9sZU5hbWUiOiJhZG1pbiIsInVzZXJOYW1lIjoi566h55CG5ZGYIiwidXNlcklkIjoiMSIsImFjY291bnQiOiJhZG1pbiIsImV4cCI6MTU0NDEyMjc5OSwibmJmIjoxNTQ0MDkxOTE3fQ.STze1uvHEoDI-FAAoFaOXufML_75MY6A_r6ZIzdYzYk',
|
||||
tokenType: 'bearer',
|
||||
authority: 'admin',
|
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||
userName: '管理员',
|
||||
account: 'admin',
|
||||
license: 'made by blade',
|
||||
},
|
||||
msg: '操作成功',
|
||||
});
|
||||
},
|
||||
'POST /api/register': (req, res) => {
|
||||
res.send({ status: 'ok', currentAuthority: 'user' });
|
||||
},
|
||||
'GET /api/500': (req, res) => {
|
||||
res.status(500).send({
|
||||
timestamp: 1513932555104,
|
||||
status: 500,
|
||||
error: 'error',
|
||||
message: 'error',
|
||||
path: '/base/category/list',
|
||||
});
|
||||
},
|
||||
'GET /api/404': (req, res) => {
|
||||
res.status(404).send({
|
||||
timestamp: 1513932643431,
|
||||
status: 404,
|
||||
error: 'Not Found',
|
||||
message: 'No message available',
|
||||
path: '/base/category/list/2121212',
|
||||
});
|
||||
},
|
||||
'GET /api/403': (req, res) => {
|
||||
res.status(403).send({
|
||||
timestamp: 1513932555104,
|
||||
status: 403,
|
||||
error: 'Unauthorized',
|
||||
message: 'Unauthorized',
|
||||
path: '/base/category/list',
|
||||
});
|
||||
},
|
||||
'GET /api/401': (req, res) => {
|
||||
res.status(401).send({
|
||||
timestamp: 1513932555104,
|
||||
status: 401,
|
||||
error: 'Unauthorized',
|
||||
message: 'Unauthorized',
|
||||
path: '/base/category/list',
|
||||
});
|
||||
},
|
||||
};
|
||||
export default delay(proxy, 500);
|
13
netlify.toml
Normal file
13
netlify.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[[redirects]]
|
||||
from = "/api/*"
|
||||
to = "https://us-central1-antd-pro.cloudfunctions.net/api/api/:splat"
|
||||
status = 200
|
||||
force = true
|
||||
[redirects.headers]
|
||||
X-From = "Netlify"
|
||||
X-Api-Key = "some-api-key-string"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
128
package.json
Normal file
128
package.json
Normal file
@ -0,0 +1,128 @@
|
||||
{
|
||||
"name": "sword",
|
||||
"version": "1.0.0",
|
||||
"description": "An out-of-box UI solution for enterprise applications",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"presite": "cd functions && npm install",
|
||||
"start": "cross-env APP_TYPE=site PORT=8888 umi dev",
|
||||
"start:no-mock": "cross-env MOCK=none PORT=8888 umi dev",
|
||||
"build": "umi build",
|
||||
"site": "npm run presite && cross-env APP_TYPE=site npm run build && firebase deploy && npm run docker:push",
|
||||
"analyze": "cross-env ANALYZE=1 umi build",
|
||||
"lint:style": "stylelint 'src/**/*.less' --syntax less",
|
||||
"lint:prettier": "check-prettier lint",
|
||||
"lint": "eslint --ext .js src mock tests && npm run lint:style && npm run lint:prettier",
|
||||
"lint:fix": "eslint --fix --ext .js src mock tests && stylelint --fix 'src/**/*.less' --syntax less",
|
||||
"lint-staged": "lint-staged",
|
||||
"lint-staged:js": "eslint --ext .js",
|
||||
"tslint": "npm run tslint:fix",
|
||||
"tslint:fix": "tslint --fix 'src/**/*.ts*'",
|
||||
"test": "umi test",
|
||||
"test:component": "umi test ./src/components",
|
||||
"test:all": "node ./tests/run-tests.js",
|
||||
"prettier": "node ./scripts/prettier.js",
|
||||
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
|
||||
"docker:build": "docker-compose -f ./docker/docker-compose.dev.yml build",
|
||||
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
|
||||
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
|
||||
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
|
||||
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
|
||||
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.10.1",
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"antd": "^3.13.0",
|
||||
"bizcharts": "^3.4.3",
|
||||
"bizcharts-plugin-slider": "^2.1.1-beta.1",
|
||||
"classnames": "^2.2.6",
|
||||
"dva": "^2.4.1",
|
||||
"enquire-js": "^0.2.1",
|
||||
"hash.js": "^1.1.7",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash-decorators": "^6.0.1",
|
||||
"memoize-one": "^5.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"numeral": "^2.0.6",
|
||||
"nzh": "^1.0.4",
|
||||
"omit.js": "^1.0.0",
|
||||
"path-to-regexp": "^3.0.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"qs": "^6.6.0",
|
||||
"rc-animate": "^2.6.0",
|
||||
"react": "^16.7.0",
|
||||
"react-container-query": "^0.11.0",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
"react-document-title": "^2.0.3",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-fittext": "^1.0.0",
|
||||
"react-media": "^1.9.2",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"roadhog-api-doc": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.8.1",
|
||||
"@types/react-dom": "^16.0.11",
|
||||
"antd-pro-merge-less": "^1.0.0",
|
||||
"antd-theme-webpack-plugin": "^1.2.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"check-prettier": "^1.0.1",
|
||||
"cross-env": "^5.2.0",
|
||||
"cross-port-killer": "^1.0.1",
|
||||
"enzyme": "3.8.0",
|
||||
"eslint": "^5.13.0",
|
||||
"eslint-config-airbnb": "^17.1.0",
|
||||
"eslint-config-prettier": "^4.0.0",
|
||||
"eslint-plugin-babel": "^5.3.0",
|
||||
"eslint-plugin-compat": "^2.6.3",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.0",
|
||||
"eslint-plugin-markdown": "^1.0.0",
|
||||
"eslint-plugin-react": "^7.12.4",
|
||||
"gh-pages": "^2.0.1",
|
||||
"jest-puppeteer": "^3.9.0",
|
||||
"less": "^3.9.0",
|
||||
"lint-staged": "^8.1.1",
|
||||
"merge-umi-mock-data": "^1.0.4",
|
||||
"mockjs": "^1.0.1-beta3",
|
||||
"prettier": "1.16.3",
|
||||
"slash2": "^2.0.0",
|
||||
"stylelint": "^9.10.1",
|
||||
"stylelint-config-prettier": "^4.0.0",
|
||||
"stylelint-config-standard": "^18.2.0",
|
||||
"tslint": "^5.12.1",
|
||||
"tslint-config-prettier": "^1.17.0",
|
||||
"tslint-react": "^3.6.0",
|
||||
"umi": "^2.4.4",
|
||||
"umi-plugin-ga": "^1.1.3",
|
||||
"umi-plugin-react": "^1.4.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"puppeteer": "^1.12.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,ts,tsx,json,jsx,less}": [
|
||||
"node ./scripts/lint-prettier.js",
|
||||
"git add"
|
||||
],
|
||||
"**/*.{js,jsx}": "npm run lint-staged:js",
|
||||
"**/*.less": "stylelint --syntax less"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 10"
|
||||
],
|
||||
"checkFiles": [
|
||||
"src/**/*.js*",
|
||||
"src/**/*.ts*",
|
||||
"src/**/*.less",
|
||||
"config/**/*.js*",
|
||||
"scripts/**/*.js"
|
||||
]
|
||||
}
|
BIN
public/favicon.png
Normal file
BIN
public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
public/icons/icon-128x128.png
Normal file
BIN
public/icons/icon-128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
public/icons/icon-192x192.png
Normal file
BIN
public/icons/icon-192x192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
public/icons/icon-512x512.png
Normal file
BIN
public/icons/icon-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
3
scripts/generateMock.js
Normal file
3
scripts/generateMock.js
Normal file
@ -0,0 +1,3 @@
|
||||
const generateMock = require('merge-umi-mock-data');
|
||||
const path = require('path');
|
||||
generateMock(path.join(__dirname, '../mock'), path.join(__dirname, '../functions/mock/index.js'));
|
23
scripts/getPrettierFiles.js
Normal file
23
scripts/getPrettierFiles.js
Normal file
@ -0,0 +1,23 @@
|
||||
const glob = require('glob');
|
||||
|
||||
const getPrettierFiles = () => {
|
||||
let files = [];
|
||||
const configFiles = glob.sync('config/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] });
|
||||
const mockFiles = glob.sync('mock/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] });
|
||||
const jsFiles = glob.sync('src/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] });
|
||||
const scriptFiles = glob.sync('scripts/**/*.js');
|
||||
const tsFiles = glob.sync('src/**/*.ts*', { ignore: ['**/node_modules/**', 'build/**'] });
|
||||
const lessFiles = glob.sync('src/**/*.less*', { ignore: ['**/node_modules/**', 'build/**'] });
|
||||
files = files.concat(configFiles);
|
||||
files = files.concat(mockFiles);
|
||||
files = files.concat(jsFiles);
|
||||
files = files.concat(scriptFiles);
|
||||
files = files.concat(tsFiles);
|
||||
files = files.concat(lessFiles);
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
return files;
|
||||
};
|
||||
|
||||
module.exports = getPrettierFiles;
|
50
scripts/lint-prettier.js
Normal file
50
scripts/lint-prettier.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
|
||||
* prettier api doc https://prettier.io/docs/en/api.html
|
||||
*----------*****--------------
|
||||
* lint file is prettier
|
||||
*----------*****--------------
|
||||
*/
|
||||
|
||||
const prettier = require('prettier');
|
||||
const fs = require('fs');
|
||||
const chalk = require('chalk');
|
||||
const prettierConfigPath = require.resolve('../.prettierrc');
|
||||
|
||||
const files = process.argv.slice(2);
|
||||
|
||||
let didError = false;
|
||||
|
||||
files.forEach(file => {
|
||||
Promise.all([
|
||||
prettier.resolveConfig(file, {
|
||||
config: prettierConfigPath,
|
||||
}),
|
||||
prettier.getFileInfo(file),
|
||||
])
|
||||
.then(resolves => {
|
||||
const [options, fileInfo] = resolves;
|
||||
if (fileInfo.ignored) {
|
||||
return;
|
||||
}
|
||||
const input = fs.readFileSync(file, 'utf8');
|
||||
const withParserOptions = {
|
||||
...options,
|
||||
parser: fileInfo.inferredParser,
|
||||
};
|
||||
const output = prettier.format(input, withParserOptions);
|
||||
if (output !== input) {
|
||||
fs.writeFileSync(file, output, 'utf8');
|
||||
console.log(chalk.green(`${file} is prettier`));
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
didError = true;
|
||||
})
|
||||
.finally(() => {
|
||||
if (didError) {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(chalk.hex('#1890FF')('prettier success!'));
|
||||
});
|
||||
});
|
46
scripts/prettier.js
Normal file
46
scripts/prettier.js
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
|
||||
* prettier api doc https://prettier.io/docs/en/api.html
|
||||
*----------*****--------------
|
||||
* prettier all js and all ts.
|
||||
*----------*****--------------
|
||||
*/
|
||||
|
||||
const prettier = require('prettier');
|
||||
const fs = require('fs');
|
||||
const getPrettierFiles = require('./getPrettierFiles');
|
||||
const prettierConfigPath = require.resolve('../.prettierrc');
|
||||
const chalk = require('chalk');
|
||||
|
||||
let didError = false;
|
||||
|
||||
const files = getPrettierFiles();
|
||||
|
||||
files.forEach(file => {
|
||||
const options = prettier.resolveConfig.sync(file, {
|
||||
config: prettierConfigPath,
|
||||
});
|
||||
const fileInfo = prettier.getFileInfo.sync(file);
|
||||
if (fileInfo.ignored) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const input = fs.readFileSync(file, 'utf8');
|
||||
const withParserOptions = {
|
||||
...options,
|
||||
parser: fileInfo.inferredParser,
|
||||
};
|
||||
const output = prettier.format(input, withParserOptions);
|
||||
if (output !== input) {
|
||||
fs.writeFileSync(file, output, 'utf8');
|
||||
console.log(chalk.green(`${file} is prettier`));
|
||||
}
|
||||
} catch (e) {
|
||||
didError = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (didError) {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(chalk.hex('#1890FF')('prettier success!'));
|
36
src/actions/code.js
Normal file
36
src/actions/code.js
Normal file
@ -0,0 +1,36 @@
|
||||
export const CODE_NAMESPACE = 'code';
|
||||
|
||||
export function CODE_LIST(payload) {
|
||||
return {
|
||||
type: `${CODE_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function CODE_DETAIL(id) {
|
||||
return {
|
||||
type: `${CODE_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function CODE_CLEAR_DETAIL() {
|
||||
return {
|
||||
type: `${CODE_NAMESPACE}/clearDetail`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function CODE_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${CODE_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function CODE_REMOVE(payload) {
|
||||
return {
|
||||
type: `${CODE_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
43
src/actions/dept.js
Normal file
43
src/actions/dept.js
Normal file
@ -0,0 +1,43 @@
|
||||
export const DEPT_NAMESPACE = 'dept';
|
||||
|
||||
export function DEPT_LIST(payload) {
|
||||
return {
|
||||
type: `${DEPT_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function DEPT_INIT() {
|
||||
return {
|
||||
type: `${DEPT_NAMESPACE}/fetchInit`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function DEPT_DETAIL(id) {
|
||||
return {
|
||||
type: `${DEPT_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function DEPT_CLEAR_DETAIL() {
|
||||
return {
|
||||
type: `${DEPT_NAMESPACE}/clearDetail`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function DEPT_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${DEPT_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function DEPT_REMOVE(payload) {
|
||||
return {
|
||||
type: `${DEPT_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
43
src/actions/dict.js
Normal file
43
src/actions/dict.js
Normal file
@ -0,0 +1,43 @@
|
||||
export const DICT_NAMESPACE = 'dict';
|
||||
|
||||
export function DICT_LIST(payload) {
|
||||
return {
|
||||
type: `${DICT_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function DICT_INIT() {
|
||||
return {
|
||||
type: `${DICT_NAMESPACE}/fetchInit`,
|
||||
payload: { code: 'DICT' },
|
||||
};
|
||||
}
|
||||
|
||||
export function DICT_DETAIL(id) {
|
||||
return {
|
||||
type: `${DICT_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function DICT_CLEAR_DETAIL() {
|
||||
return {
|
||||
type: `${DICT_NAMESPACE}/clearDetail`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function DICT_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${DICT_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function DICT_REMOVE(payload) {
|
||||
return {
|
||||
type: `${DICT_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
43
src/actions/log.js
Normal file
43
src/actions/log.js
Normal file
@ -0,0 +1,43 @@
|
||||
export const LOG_NAMESPACE = 'log';
|
||||
|
||||
export function LOG_USUAL_LIST(payload) {
|
||||
return {
|
||||
type: `${LOG_NAMESPACE}/fetchUsualList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function LOG_USUAL_DETAIL(id) {
|
||||
return {
|
||||
type: `${LOG_NAMESPACE}/fetchUsualDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function LOG_API_LIST(payload) {
|
||||
return {
|
||||
type: `${LOG_NAMESPACE}/fetchApiList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function LOG_API_DETAIL(id) {
|
||||
return {
|
||||
type: `${LOG_NAMESPACE}/fetchApiDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function LOG_ERROR_LIST(payload) {
|
||||
return {
|
||||
type: `${LOG_NAMESPACE}/fetchErrorList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function LOG_ERROR_DETAIL(id) {
|
||||
return {
|
||||
type: `${LOG_NAMESPACE}/fetchErrorDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
61
src/actions/menu.js
Normal file
61
src/actions/menu.js
Normal file
@ -0,0 +1,61 @@
|
||||
import { getAuthority } from '../utils/authority';
|
||||
|
||||
export const MENU_NAMESPACE = 'menu';
|
||||
|
||||
export function MENU_REFRESH_DATA() {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/fetchMenuData`,
|
||||
payload: { authority: getAuthority() },
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_LIST(payload) {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_INIT() {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/fetchInit`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_DETAIL(id) {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_CLEAR_DETAIL() {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/clearDetail`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_REMOVE(payload) {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function MENU_SELECT_ICON(icon) {
|
||||
return {
|
||||
type: `${MENU_NAMESPACE}/selectIcon`,
|
||||
payload: {
|
||||
source: icon,
|
||||
},
|
||||
};
|
||||
}
|
36
src/actions/notice.js
Normal file
36
src/actions/notice.js
Normal file
@ -0,0 +1,36 @@
|
||||
export const NOTICE_NAMESPACE = 'notice';
|
||||
|
||||
export function NOTICE_LIST(payload) {
|
||||
return {
|
||||
type: `${NOTICE_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function NOTICE_INIT() {
|
||||
return {
|
||||
type: `${NOTICE_NAMESPACE}/fetchInit`,
|
||||
payload: { code: 'notice' },
|
||||
};
|
||||
}
|
||||
|
||||
export function NOTICE_DETAIL(id) {
|
||||
return {
|
||||
type: `${NOTICE_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function NOTICE_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${NOTICE_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function NOTICE_REMOVE(payload) {
|
||||
return {
|
||||
type: `${NOTICE_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
36
src/actions/param.js
Normal file
36
src/actions/param.js
Normal file
@ -0,0 +1,36 @@
|
||||
export const PARAM_NAMESPACE = 'param';
|
||||
|
||||
export function PARAM_LIST(payload) {
|
||||
return {
|
||||
type: `${PARAM_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function PARAM_DETAIL(id) {
|
||||
return {
|
||||
type: `${PARAM_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function PARAM_CLEAR_DETAIL() {
|
||||
return {
|
||||
type: `${PARAM_NAMESPACE}/clearDetail`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function PARAM_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${PARAM_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function PARAM_REMOVE(payload) {
|
||||
return {
|
||||
type: `${PARAM_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
72
src/actions/role.js
Normal file
72
src/actions/role.js
Normal file
@ -0,0 +1,72 @@
|
||||
export const ROLE_NAMESPACE = 'role';
|
||||
|
||||
export function ROLE_LIST(payload) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_INIT() {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/fetchInit`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_DETAIL(id) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_CLEAR_DETAIL() {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/clearDetail`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_GRANT_TREE(payload) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/grantTree`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_TREE_KEYS(payload) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/roleTreeKeys`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_SET_TREE_KEYS(payload) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/setRoleTreeKeys`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_GRANT(payload, callback) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/grant`,
|
||||
payload,
|
||||
callback,
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function ROLE_REMOVE(payload) {
|
||||
return {
|
||||
type: `${ROLE_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
44
src/actions/user.js
Normal file
44
src/actions/user.js
Normal file
@ -0,0 +1,44 @@
|
||||
export const USER_NAMESPACE = 'user';
|
||||
|
||||
export function USER_LIST(payload) {
|
||||
return {
|
||||
type: `${USER_NAMESPACE}/fetchList`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function USER_INIT() {
|
||||
return {
|
||||
type: `${USER_NAMESPACE}/fetchInit`,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function USER_DETAIL(id) {
|
||||
return {
|
||||
type: `${USER_NAMESPACE}/fetchDetail`,
|
||||
payload: { id },
|
||||
};
|
||||
}
|
||||
|
||||
export function USER_ROLE_GRANT(payload, callback) {
|
||||
return {
|
||||
type: `${USER_NAMESPACE}/grant`,
|
||||
payload,
|
||||
callback,
|
||||
};
|
||||
}
|
||||
|
||||
export function USER_SUBMIT(payload) {
|
||||
return {
|
||||
type: `${USER_NAMESPACE}/submit`,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
export function USER_REMOVE(payload) {
|
||||
return {
|
||||
type: `${USER_NAMESPACE}/remove`,
|
||||
payload,
|
||||
};
|
||||
}
|
41
src/app.js
Normal file
41
src/app.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { routesAuthority } from './services/menu';
|
||||
|
||||
export const dva = {
|
||||
config: {
|
||||
onError(err) {
|
||||
err.preventDefault();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let authRoutes = {
|
||||
'/form/advanced-form': { authority: ['admin', 'user'] },
|
||||
};
|
||||
|
||||
function ergodicRoutes(routes, authKey, authority) {
|
||||
routes.forEach(element => {
|
||||
if (element.path === authKey) {
|
||||
if (!element.authority) element.authority = []; // eslint-disable-line
|
||||
Object.assign(element.authority, authority || []);
|
||||
} else if (element.routes) {
|
||||
ergodicRoutes(element.routes, authKey, authority);
|
||||
}
|
||||
return element;
|
||||
});
|
||||
}
|
||||
|
||||
export function patchRoutes(routes) {
|
||||
Object.keys(authRoutes).map(authKey =>
|
||||
ergodicRoutes(routes, authKey, authRoutes[authKey].authority)
|
||||
);
|
||||
window.g_routes = routes;
|
||||
}
|
||||
|
||||
export function render(oldRender) {
|
||||
routesAuthority().then(response => {
|
||||
if (response) {
|
||||
authRoutes = response.data;
|
||||
}
|
||||
oldRender();
|
||||
});
|
||||
}
|
160
src/assets/logo.svg
Normal file
160
src/assets/logo.svg
Normal file
@ -0,0 +1,160 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="87px" height="86px" viewBox="0 0 87 86" enable-background="new 0 0 87 86" xml:space="preserve"> <image id="image0" width="87" height="86" x="0" y="0"
|
||||
xlink:href="data:image/png;base64,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
|
||||
MzErMDg6MDC8c1XdAAAAAElFTkSuQmCC" />
|
||||
</svg>
|
101
src/components/ActiveChart/index.js
Normal file
101
src/components/ActiveChart/index.js
Normal file
@ -0,0 +1,101 @@
|
||||
import React, { Component } from 'react';
|
||||
import { MiniArea } from '../Charts';
|
||||
import NumberInfo from '../NumberInfo';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
function fixedZero(val) {
|
||||
return val * 1 < 10 ? `0${val}` : val;
|
||||
}
|
||||
|
||||
function getActiveData() {
|
||||
const activeData = [];
|
||||
for (let i = 0; i < 24; i += 1) {
|
||||
activeData.push({
|
||||
x: `${fixedZero(i)}:00`,
|
||||
y: Math.floor(Math.random() * 200) + i * 50,
|
||||
});
|
||||
}
|
||||
return activeData;
|
||||
}
|
||||
|
||||
export default class ActiveChart extends Component {
|
||||
state = {
|
||||
activeData: getActiveData(),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.loopData();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timer);
|
||||
cancelAnimationFrame(this.requestRef);
|
||||
}
|
||||
|
||||
loopData = () => {
|
||||
this.requestRef = requestAnimationFrame(() => {
|
||||
this.timer = setTimeout(() => {
|
||||
this.setState(
|
||||
{
|
||||
activeData: getActiveData(),
|
||||
},
|
||||
() => {
|
||||
this.loopData();
|
||||
}
|
||||
);
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { activeData = [] } = this.state;
|
||||
|
||||
return (
|
||||
<div className={styles.activeChart}>
|
||||
<NumberInfo subTitle="目标评估" total="有望达到预期" />
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<MiniArea
|
||||
animate={false}
|
||||
line
|
||||
borderWidth={2}
|
||||
height={84}
|
||||
scale={{
|
||||
y: {
|
||||
tickCount: 3,
|
||||
},
|
||||
}}
|
||||
yAxis={{
|
||||
tickLine: false,
|
||||
label: false,
|
||||
title: false,
|
||||
line: false,
|
||||
}}
|
||||
data={activeData}
|
||||
/>
|
||||
</div>
|
||||
{activeData && (
|
||||
<div>
|
||||
<div className={styles.activeChartGrid}>
|
||||
<p>{[...activeData].sort()[activeData.length - 1].y + 200} 亿元</p>
|
||||
<p>{[...activeData].sort()[Math.floor(activeData.length / 2)].y} 亿元</p>
|
||||
</div>
|
||||
<div className={styles.dashedLine}>
|
||||
<div className={styles.line} />
|
||||
</div>
|
||||
<div className={styles.dashedLine}>
|
||||
<div className={styles.line} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{activeData && (
|
||||
<div className={styles.activeChartLegend}>
|
||||
<span>00:00</span>
|
||||
<span>{activeData[Math.floor(activeData.length / 2)].x}</span>
|
||||
<span>{activeData[activeData.length - 1].x}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
51
src/components/ActiveChart/index.less
Normal file
51
src/components/ActiveChart/index.less
Normal file
@ -0,0 +1,51 @@
|
||||
.activeChart {
|
||||
position: relative;
|
||||
}
|
||||
.activeChartGrid {
|
||||
p {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
}
|
||||
p:last-child {
|
||||
top: 115px;
|
||||
}
|
||||
}
|
||||
.activeChartLegend {
|
||||
position: relative;
|
||||
height: 20px;
|
||||
margin-top: 8px;
|
||||
font-size: 0;
|
||||
line-height: 20px;
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 33.33%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
span:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
span:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.dashedLine {
|
||||
position: relative;
|
||||
top: -70px;
|
||||
left: -3px;
|
||||
height: 1px;
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(to right, transparent 50%, #e9e9e9 50%);
|
||||
background-size: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.dashedLine:last-child {
|
||||
top: -36px;
|
||||
}
|
54
src/components/AdvancedTable/withExpandRow.js
Normal file
54
src/components/AdvancedTable/withExpandRow.js
Normal file
@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
|
||||
export default ({
|
||||
expendAll = false,
|
||||
expandedRowRender,
|
||||
updateExpandRowKeys,
|
||||
initExpandedRowKeys,
|
||||
}) => {
|
||||
return WrappedComponent => {
|
||||
return class extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
expandedRowKeys: initExpandedRowKeys || [],
|
||||
expandRowByClick: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (initExpandedRowKeys) {
|
||||
this.onExpandedRowsChange(initExpandedRowKeys);
|
||||
}
|
||||
}
|
||||
|
||||
onExpandedRowsChange = rows => {
|
||||
if (updateExpandRowKeys) updateExpandRowKeys(rows);
|
||||
this.setState({
|
||||
expandedRowKeys: rows,
|
||||
});
|
||||
};
|
||||
|
||||
expandRow = row => {
|
||||
this.setState({
|
||||
expandedRowKeys: row ? [row] : [],
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { expandedRowKeys } = this.state;
|
||||
// const expandRowByClick = true;
|
||||
return (
|
||||
<WrappedComponent
|
||||
expandedRowRender={expandedRowRender}
|
||||
defaultExpandAllRows={expendAll}
|
||||
expandedRowKeys={expandedRowKeys}
|
||||
onExpandedRowsChange={this.onExpandedRowsChange}
|
||||
expandRow={this.expandRow}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
17
src/components/ArticleListContent/index.js
Normal file
17
src/components/ArticleListContent/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { Avatar } from 'antd';
|
||||
import styles from './index.less';
|
||||
|
||||
const ArticleListContent = ({ data: { content, updatedAt, avatar, owner, href } }) => (
|
||||
<div className={styles.listContent}>
|
||||
<div className={styles.description}>{content}</div>
|
||||
<div className={styles.extra}>
|
||||
<Avatar src={avatar} size="small" />
|
||||
<a href={href}>{owner}</a> 发布在 <a href={href}>{href}</a>
|
||||
<em>{moment(updatedAt).format('YYYY-MM-DD HH:mm')}</em>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ArticleListContent;
|
38
src/components/ArticleListContent/index.less
Normal file
38
src/components/ArticleListContent/index.less
Normal file
@ -0,0 +1,38 @@
|
||||
@import '~antd/lib/style/themes/default.less';
|
||||
|
||||
.listContent {
|
||||
.description {
|
||||
max-width: 720px;
|
||||
line-height: 22px;
|
||||
}
|
||||
.extra {
|
||||
margin-top: 16px;
|
||||
color: @text-color-secondary;
|
||||
line-height: 22px;
|
||||
& > :global(.ant-avatar) {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 8px;
|
||||
vertical-align: top;
|
||||
}
|
||||
& > em {
|
||||
margin-left: 16px;
|
||||
color: @disabled-color;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: @screen-xs) {
|
||||
.listContent {
|
||||
.extra {
|
||||
& > em {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/components/Authorized/Authorized.js
Normal file
8
src/components/Authorized/Authorized.js
Normal file
@ -0,0 +1,8 @@
|
||||
import CheckPermissions from './CheckPermissions';
|
||||
|
||||
const Authorized = ({ children, authority, noMatch = null }) => {
|
||||
const childrenRender = typeof children === 'undefined' ? null : children;
|
||||
return CheckPermissions(authority, childrenRender, noMatch);
|
||||
};
|
||||
|
||||
export default Authorized;
|
13
src/components/Authorized/AuthorizedRoute.d.ts
vendored
Normal file
13
src/components/Authorized/AuthorizedRoute.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { RouteProps } from 'react-router';
|
||||
|
||||
type authorityFN = (currentAuthority?: string) => boolean;
|
||||
|
||||
type authority = string | string[] | authorityFN | Promise<any>;
|
||||
|
||||
export interface IAuthorizedRouteProps extends RouteProps {
|
||||
authority: authority;
|
||||
}
|
||||
export { authority };
|
||||
|
||||
export class AuthorizedRoute extends React.Component<IAuthorizedRouteProps, any> {}
|
15
src/components/Authorized/AuthorizedRoute.js
Normal file
15
src/components/Authorized/AuthorizedRoute.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import Authorized from './Authorized';
|
||||
|
||||
// TODO: umi只会返回render和rest
|
||||
const AuthorizedRoute = ({ component: Component, render, authority, redirectPath, ...rest }) => (
|
||||
<Authorized
|
||||
authority={authority}
|
||||
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
|
||||
>
|
||||
<Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />
|
||||
</Authorized>
|
||||
);
|
||||
|
||||
export default AuthorizedRoute;
|
88
src/components/Authorized/CheckPermissions.js
Normal file
88
src/components/Authorized/CheckPermissions.js
Normal file
@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import PromiseRender from './PromiseRender';
|
||||
import { CURRENT } from './renderAuthorize';
|
||||
|
||||
function isPromise(obj) {
|
||||
return (
|
||||
!!obj &&
|
||||
(typeof obj === 'object' || typeof obj === 'function') &&
|
||||
typeof obj.then === 'function'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用权限检查方法
|
||||
* Common check permissions method
|
||||
* @param { 权限判定 Permission judgment type string |array | Promise | Function } authority
|
||||
* @param { 你的权限 Your permission description type:string} currentAuthority
|
||||
* @param { 通过的组件 Passing components } target
|
||||
* @param { 未通过的组件 no pass components } Exception
|
||||
*/
|
||||
const checkPermissions = (authority, currentAuthority, target, Exception) => {
|
||||
// 没有判定权限.默认查看所有
|
||||
// Retirement authority, return target;
|
||||
if (!authority) {
|
||||
return target;
|
||||
}
|
||||
// 数组处理
|
||||
if (Array.isArray(authority)) {
|
||||
if (authority.indexOf(currentAuthority) >= 0) {
|
||||
return target;
|
||||
}
|
||||
if (Array.isArray(currentAuthority)) {
|
||||
for (let i = 0; i < currentAuthority.length; i += 1) {
|
||||
const element = currentAuthority[i];
|
||||
if (authority.indexOf(element) >= 0) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Exception;
|
||||
}
|
||||
|
||||
// string 处理
|
||||
if (typeof authority === 'string') {
|
||||
if (authority === currentAuthority) {
|
||||
return target;
|
||||
}
|
||||
if (Array.isArray(currentAuthority)) {
|
||||
for (let i = 0; i < currentAuthority.length; i += 1) {
|
||||
const element = currentAuthority[i];
|
||||
if (authority === element) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Exception;
|
||||
}
|
||||
|
||||
// Promise 处理
|
||||
if (isPromise(authority)) {
|
||||
return <PromiseRender ok={target} error={Exception} promise={authority} />;
|
||||
}
|
||||
|
||||
// Function 处理
|
||||
if (typeof authority === 'function') {
|
||||
try {
|
||||
const bool = authority(currentAuthority);
|
||||
// 函数执行后返回值是 Promise
|
||||
if (isPromise(bool)) {
|
||||
return <PromiseRender ok={target} error={Exception} promise={bool} />;
|
||||
}
|
||||
if (bool) {
|
||||
return target;
|
||||
}
|
||||
return Exception;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
throw new Error('unsupported parameters');
|
||||
};
|
||||
|
||||
export { checkPermissions };
|
||||
|
||||
const check = (authority, target, Exception) =>
|
||||
checkPermissions(authority, CURRENT, target, Exception);
|
||||
|
||||
export default check;
|
55
src/components/Authorized/CheckPermissions.test.js
Normal file
55
src/components/Authorized/CheckPermissions.test.js
Normal file
@ -0,0 +1,55 @@
|
||||
import { checkPermissions } from './CheckPermissions';
|
||||
|
||||
const target = 'ok';
|
||||
const error = 'error';
|
||||
|
||||
describe('test CheckPermissions', () => {
|
||||
it('Correct string permission authentication', () => {
|
||||
expect(checkPermissions('user', 'user', target, error)).toEqual('ok');
|
||||
});
|
||||
it('Correct string permission authentication', () => {
|
||||
expect(checkPermissions('user', 'NULL', target, error)).toEqual('error');
|
||||
});
|
||||
it('authority is undefined , return ok', () => {
|
||||
expect(checkPermissions(null, 'NULL', target, error)).toEqual('ok');
|
||||
});
|
||||
it('currentAuthority is undefined , return error', () => {
|
||||
expect(checkPermissions('admin', null, target, error)).toEqual('error');
|
||||
});
|
||||
it('Wrong string permission authentication', () => {
|
||||
expect(checkPermissions('admin', 'user', target, error)).toEqual('error');
|
||||
});
|
||||
it('Correct Array permission authentication', () => {
|
||||
expect(checkPermissions(['user', 'admin'], 'user', target, error)).toEqual('ok');
|
||||
});
|
||||
it('Wrong Array permission authentication,currentAuthority error', () => {
|
||||
expect(checkPermissions(['user', 'admin'], 'user,admin', target, error)).toEqual('error');
|
||||
});
|
||||
it('Wrong Array permission authentication', () => {
|
||||
expect(checkPermissions(['user', 'admin'], 'guest', target, error)).toEqual('error');
|
||||
});
|
||||
it('Wrong Function permission authentication', () => {
|
||||
expect(checkPermissions(() => false, 'guest', target, error)).toEqual('error');
|
||||
});
|
||||
it('Correct Function permission authentication', () => {
|
||||
expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok');
|
||||
});
|
||||
it('authority is string, currentAuthority is array, return ok', () => {
|
||||
expect(checkPermissions('user', ['user'], target, error)).toEqual('ok');
|
||||
});
|
||||
it('authority is string, currentAuthority is array, return ok', () => {
|
||||
expect(checkPermissions('user', ['user', 'admin'], target, error)).toEqual('ok');
|
||||
});
|
||||
it('authority is array, currentAuthority is array, return ok', () => {
|
||||
expect(checkPermissions(['user', 'admin'], ['user', 'admin'], target, error)).toEqual('ok');
|
||||
});
|
||||
it('Wrong Function permission authentication', () => {
|
||||
expect(checkPermissions(() => false, ['user'], target, error)).toEqual('error');
|
||||
});
|
||||
it('Correct Function permission authentication', () => {
|
||||
expect(checkPermissions(() => true, ['user'], target, error)).toEqual('ok');
|
||||
});
|
||||
it('authority is undefined , return ok', () => {
|
||||
expect(checkPermissions(null, ['user'], target, error)).toEqual('ok');
|
||||
});
|
||||
});
|
65
src/components/Authorized/PromiseRender.js
Normal file
65
src/components/Authorized/PromiseRender.js
Normal file
@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import { Spin } from 'antd';
|
||||
|
||||
export default class PromiseRender extends React.PureComponent {
|
||||
state = {
|
||||
component: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.setRenderComponent(this.props);
|
||||
}
|
||||
|
||||
componentDidUpdate(nextProps) {
|
||||
// new Props enter
|
||||
this.setRenderComponent(nextProps);
|
||||
}
|
||||
|
||||
// set render Component : ok or error
|
||||
setRenderComponent(props) {
|
||||
const ok = this.checkIsInstantiation(props.ok);
|
||||
const error = this.checkIsInstantiation(props.error);
|
||||
props.promise
|
||||
.then(() => {
|
||||
this.setState({
|
||||
component: ok,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState({
|
||||
component: error,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Determine whether the incoming component has been instantiated
|
||||
// AuthorizedRoute is already instantiated
|
||||
// Authorized render is already instantiated, children is no instantiated
|
||||
// Secured is not instantiated
|
||||
checkIsInstantiation = target => {
|
||||
if (!React.isValidElement(target)) {
|
||||
return target;
|
||||
}
|
||||
return () => target;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { component: Component } = this.state;
|
||||
const { ok, error, promise, ...rest } = this.props;
|
||||
return Component ? (
|
||||
<Component {...rest} />
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
margin: 'auto',
|
||||
paddingTop: 50,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
55
src/components/Authorized/Secured.js
Normal file
55
src/components/Authorized/Secured.js
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import Exception from '../Exception';
|
||||
import CheckPermissions from './CheckPermissions';
|
||||
/**
|
||||
* 默认不能访问任何页面
|
||||
* default is "NULL"
|
||||
*/
|
||||
const Exception403 = () => <Exception type="403" />;
|
||||
|
||||
// Determine whether the incoming component has been instantiated
|
||||
// AuthorizedRoute is already instantiated
|
||||
// Authorized render is already instantiated, children is no instantiated
|
||||
// Secured is not instantiated
|
||||
const checkIsInstantiation = target => {
|
||||
if (!React.isValidElement(target)) {
|
||||
return target;
|
||||
}
|
||||
return () => target;
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于判断是否拥有权限访问此view权限
|
||||
* authority 支持传入 string, function:()=>boolean|Promise
|
||||
* e.g. 'user' 只有user用户能访问
|
||||
* e.g. 'user,admin' user和 admin 都能访问
|
||||
* e.g. ()=>boolean 返回true能访问,返回false不能访问
|
||||
* e.g. Promise then 能访问 catch不能访问
|
||||
* e.g. authority support incoming string, function: () => boolean | Promise
|
||||
* e.g. 'user' only user user can access
|
||||
* e.g. 'user, admin' user and admin can access
|
||||
* e.g. () => boolean true to be able to visit, return false can not be accessed
|
||||
* e.g. Promise then can not access the visit to catch
|
||||
* @param {string | function | Promise} authority
|
||||
* @param {ReactNode} error 非必需参数
|
||||
*/
|
||||
const authorize = (authority, error) => {
|
||||
/**
|
||||
* conversion into a class
|
||||
* 防止传入字符串时找不到staticContext造成报错
|
||||
* String parameters can cause staticContext not found error
|
||||
*/
|
||||
let classError = false;
|
||||
if (error) {
|
||||
classError = () => error;
|
||||
}
|
||||
if (!authority) {
|
||||
throw new Error('authority is required');
|
||||
}
|
||||
return function decideAuthority(target) {
|
||||
const component = CheckPermissions(authority, target, classError || Exception403);
|
||||
return checkIsInstantiation(component);
|
||||
};
|
||||
};
|
||||
|
||||
export default authorize;
|
23
src/components/Authorized/demo/AuthorizedArray.md
Normal file
23
src/components/Authorized/demo/AuthorizedArray.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 使用数组作为参数
|
||||
en-US: Use Array as a parameter
|
||||
---
|
||||
|
||||
Use Array as a parameter
|
||||
|
||||
```jsx
|
||||
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const Authorized = RenderAuthorized('user');
|
||||
const noMatch = <Alert message="No permission." type="error" showIcon />;
|
||||
|
||||
ReactDOM.render(
|
||||
<Authorized authority={['user', 'admin']} noMatch={noMatch}>
|
||||
<Alert message="Use Array as a parameter passed!" type="success" showIcon />
|
||||
</Authorized>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
31
src/components/Authorized/demo/AuthorizedFunction.md
Normal file
31
src/components/Authorized/demo/AuthorizedFunction.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 使用方法作为参数
|
||||
en-US: Use function as a parameter
|
||||
---
|
||||
|
||||
Use Function as a parameter
|
||||
|
||||
```jsx
|
||||
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const Authorized = RenderAuthorized('user');
|
||||
const noMatch = <Alert message="No permission." type="error" showIcon />;
|
||||
|
||||
const havePermission = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<Authorized authority={havePermission} noMatch={noMatch}>
|
||||
<Alert
|
||||
message="Use Function as a parameter passed!"
|
||||
type="success"
|
||||
showIcon
|
||||
/>
|
||||
</Authorized>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
25
src/components/Authorized/demo/basic.md
Normal file
25
src/components/Authorized/demo/basic.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 基本使用
|
||||
en-US: Basic use
|
||||
---
|
||||
|
||||
Basic use
|
||||
|
||||
```jsx
|
||||
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const Authorized = RenderAuthorized('user');
|
||||
const noMatch = <Alert message="No permission." type="error" showIcon />;
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<Authorized authority="admin" noMatch={noMatch}>
|
||||
<Alert message="user Passed!" type="success" showIcon />
|
||||
</Authorized>
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
28
src/components/Authorized/demo/secured.md
Normal file
28
src/components/Authorized/demo/secured.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 注解基本使用
|
||||
en-US: Basic use secured
|
||||
---
|
||||
|
||||
secured demo used
|
||||
|
||||
```jsx
|
||||
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const { Secured } = RenderAuthorized('user');
|
||||
|
||||
@Secured('admin')
|
||||
class TestSecuredString extends React.Component {
|
||||
render() {
|
||||
<Alert message="user Passed!" type="success" showIcon />;
|
||||
}
|
||||
}
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<TestSecuredString />
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
32
src/components/Authorized/index.d.ts
vendored
Normal file
32
src/components/Authorized/index.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import AuthorizedRoute, { authority } from './AuthorizedRoute';
|
||||
export type IReactComponent<P = any> =
|
||||
| React.StatelessComponent<P>
|
||||
| React.ComponentClass<P>
|
||||
| React.ClassicComponentClass<P>;
|
||||
|
||||
type Secured = (
|
||||
authority: authority,
|
||||
error?: React.ReactNode
|
||||
) => <T extends IReactComponent>(target: T) => T;
|
||||
|
||||
type check = <T extends IReactComponent, S extends IReactComponent>(
|
||||
authority: authority,
|
||||
target: T,
|
||||
Exception: S
|
||||
) => T | S;
|
||||
|
||||
export interface IAuthorizedProps {
|
||||
authority: authority;
|
||||
noMatch?: React.ReactNode;
|
||||
}
|
||||
|
||||
export class Authorized extends React.Component<IAuthorizedProps, any> {
|
||||
public static Secured: Secured;
|
||||
public static AuthorizedRoute: typeof AuthorizedRoute;
|
||||
public static check: check;
|
||||
}
|
||||
|
||||
declare function renderAuthorize(currentAuthority: string): typeof Authorized;
|
||||
|
||||
export default renderAuthorize;
|
11
src/components/Authorized/index.js
Normal file
11
src/components/Authorized/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import Authorized from './Authorized';
|
||||
import AuthorizedRoute from './AuthorizedRoute';
|
||||
import Secured from './Secured';
|
||||
import check from './CheckPermissions';
|
||||
import renderAuthorize from './renderAuthorize';
|
||||
|
||||
Authorized.Secured = Secured;
|
||||
Authorized.AuthorizedRoute = AuthorizedRoute;
|
||||
Authorized.check = check;
|
||||
|
||||
export default renderAuthorize(Authorized);
|
58
src/components/Authorized/index.md
Normal file
58
src/components/Authorized/index.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
title:
|
||||
en-US: Authorized
|
||||
zh-CN: Authorized
|
||||
subtitle: 权限
|
||||
cols: 1
|
||||
order: 15
|
||||
---
|
||||
|
||||
权限组件,通过比对现有权限与准入权限,决定相关元素的展示。
|
||||
|
||||
## API
|
||||
|
||||
### RenderAuthorized
|
||||
|
||||
`RenderAuthorized: (currentAuthority: string | () => string) => Authorized`
|
||||
|
||||
权限组件默认 export RenderAuthorized 函数,它接收当前权限作为参数,返回一个权限对象,该对象提供以下几种使用方式。
|
||||
|
||||
|
||||
### Authorized
|
||||
|
||||
最基础的权限控制。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| children | 正常渲染的元素,权限判断通过时展示 | ReactNode | - |
|
||||
| authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean | Promise` | - |
|
||||
| noMatch | 权限异常渲染元素,权限判断不通过时展示 | ReactNode | - |
|
||||
|
||||
### Authorized.AuthorizedRoute
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| authority | 准入权限/权限判断 | `string | array | Promise | (currentAuthority) => boolean | Promise` | - |
|
||||
| redirectPath | 权限异常时重定向的页面路由 | string | - |
|
||||
|
||||
其余参数与 `Route` 相同。
|
||||
|
||||
### Authorized.Secured
|
||||
|
||||
注解方式,`@Authorized.Secured(authority, error)`
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean | Promise` | - |
|
||||
| error | 权限异常时渲染元素 | ReactNode | <Exception type="403" /> |
|
||||
|
||||
### Authorized.check
|
||||
|
||||
函数形式的 Authorized,用于某些不能被 HOC 包裹的组件。 `Authorized.check(authority, target, Exception)`
|
||||
注意:传入一个 Promise 时,无论正确还是错误返回的都是一个 ReactClass。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| authority | 准入权限/权限判断 | `string | Promise | (currentAuthority) => boolean | Promise` | - |
|
||||
| target | 权限判断通过时渲染的元素 | ReactNode | - |
|
||||
| Exception | 权限异常时渲染元素 | ReactNode | - |
|
25
src/components/Authorized/renderAuthorize.js
Normal file
25
src/components/Authorized/renderAuthorize.js
Normal file
@ -0,0 +1,25 @@
|
||||
/* eslint-disable import/no-mutable-exports */
|
||||
let CURRENT = 'NULL';
|
||||
/**
|
||||
* use authority or getAuthority
|
||||
* @param {string|()=>String} currentAuthority
|
||||
*/
|
||||
const renderAuthorize = Authorized => currentAuthority => {
|
||||
if (currentAuthority) {
|
||||
if (typeof currentAuthority === 'function') {
|
||||
CURRENT = currentAuthority();
|
||||
}
|
||||
if (
|
||||
Object.prototype.toString.call(currentAuthority) === '[object String]' ||
|
||||
Array.isArray(currentAuthority)
|
||||
) {
|
||||
CURRENT = currentAuthority;
|
||||
}
|
||||
} else {
|
||||
CURRENT = 'NULL';
|
||||
}
|
||||
return Authorized;
|
||||
};
|
||||
|
||||
export { CURRENT };
|
||||
export default Authorized => renderAuthorize(Authorized);
|
10
src/components/AvatarList/AvatarItem.d.ts
vendored
Normal file
10
src/components/AvatarList/AvatarItem.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
export interface IAvatarItemProps {
|
||||
tips: React.ReactNode;
|
||||
src: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class AvatarItem extends React.Component<IAvatarItemProps, any> {
|
||||
constructor(props: IAvatarItemProps);
|
||||
}
|
24
src/components/AvatarList/demo/maxLength.md
Normal file
24
src/components/AvatarList/demo/maxLength.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 要显示的最大项目
|
||||
en-US: Max Items to Show
|
||||
---
|
||||
|
||||
`maxLength` attribute specifies the maximum number of items to show while `excessItemsStyle` style the excess
|
||||
item component.
|
||||
|
||||
````jsx
|
||||
import AvatarList from 'ant-design-pro/lib/AvatarList';
|
||||
|
||||
ReactDOM.render(
|
||||
<AvatarList size="mini" maxLength={3} excessItemsStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>
|
||||
<AvatarList.Item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
|
||||
<AvatarList.Item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
|
||||
<AvatarList.Item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<AvatarList.Item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<AvatarList.Item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<AvatarList.Item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
</AvatarList>
|
||||
, mountNode);
|
||||
````
|
20
src/components/AvatarList/demo/simple.md
Normal file
20
src/components/AvatarList/demo/simple.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 基础样例
|
||||
en-US: Basic Usage
|
||||
---
|
||||
|
||||
Simplest of usage.
|
||||
|
||||
````jsx
|
||||
import AvatarList from 'ant-design-pro/lib/AvatarList';
|
||||
|
||||
ReactDOM.render(
|
||||
<AvatarList size="mini">
|
||||
<AvatarList.Item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
|
||||
<AvatarList.Item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
|
||||
<AvatarList.Item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
</AvatarList>
|
||||
, mountNode);
|
||||
````
|
14
src/components/AvatarList/index.d.ts
vendored
Normal file
14
src/components/AvatarList/index.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import AvatarItem from './AvatarItem';
|
||||
|
||||
export interface IAvatarListProps {
|
||||
size?: 'large' | 'small' | 'mini' | 'default';
|
||||
maxLength?: number;
|
||||
excessItemsStyle?: React.CSSProperties;
|
||||
style?: React.CSSProperties;
|
||||
children: React.ReactElement<AvatarItem> | Array<React.ReactElement<AvatarItem>>;
|
||||
}
|
||||
|
||||
export default class AvatarList extends React.Component<IAvatarListProps, any> {
|
||||
public static Item: typeof AvatarItem;
|
||||
}
|
24
src/components/AvatarList/index.en-US.md
Normal file
24
src/components/AvatarList/index.en-US.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: AvatarList
|
||||
order: 1
|
||||
cols: 1
|
||||
---
|
||||
|
||||
A list of user's avatar for project or group member list frequently. If a large or small AvatarList is desired, set the `size` property to either `large` or `small` and `mini` respectively. Omit the `size` property for a AvatarList with the default size.
|
||||
|
||||
## API
|
||||
|
||||
### AvatarList
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| ---------------- | --------------------- | ---------------------------------- | --------- |
|
||||
| size | size of list | `large`、`small` 、`mini`, `default` | `default` |
|
||||
| maxLength | max items to show | number | - |
|
||||
| excessItemsStyle | the excess item style | CSSProperties | - |
|
||||
|
||||
### AvatarList.Item
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | -------------------------------------------- | --------- | ------- |
|
||||
| tips | title tips for avatar item | ReactNode | - |
|
||||
| src | the address of the image for an image avatar | string | - |
|
61
src/components/AvatarList/index.js
Normal file
61
src/components/AvatarList/index.js
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { Tooltip, Avatar } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const avatarSizeToClassName = size =>
|
||||
classNames(styles.avatarItem, {
|
||||
[styles.avatarItemLarge]: size === 'large',
|
||||
[styles.avatarItemSmall]: size === 'small',
|
||||
[styles.avatarItemMini]: size === 'mini',
|
||||
});
|
||||
|
||||
const AvatarList = ({ children, size, maxLength, excessItemsStyle, ...other }) => {
|
||||
const numOfChildren = React.Children.count(children);
|
||||
const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength;
|
||||
|
||||
const childrenWithProps = React.Children.toArray(children)
|
||||
.slice(0, numToShow)
|
||||
.map(child =>
|
||||
React.cloneElement(child, {
|
||||
size,
|
||||
})
|
||||
);
|
||||
|
||||
if (numToShow < numOfChildren) {
|
||||
const cls = avatarSizeToClassName(size);
|
||||
|
||||
childrenWithProps.push(
|
||||
<li key="exceed" className={cls}>
|
||||
<Avatar size={size} style={excessItemsStyle}>{`+${numOfChildren - maxLength}`}</Avatar>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...other} className={styles.avatarList}>
|
||||
<ul> {childrenWithProps} </ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ src, size, tips, onClick = () => {} }) => {
|
||||
const cls = avatarSizeToClassName(size);
|
||||
|
||||
return (
|
||||
<li className={cls} onClick={onClick}>
|
||||
{tips ? (
|
||||
<Tooltip title={tips}>
|
||||
<Avatar src={src} size={size} style={{ cursor: 'pointer' }} />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Avatar src={src} size={size} />
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
AvatarList.Item = Item;
|
||||
|
||||
export default AvatarList;
|
50
src/components/AvatarList/index.less
Normal file
50
src/components/AvatarList/index.less
Normal file
@ -0,0 +1,50 @@
|
||||
@import '~antd/lib/style/themes/default.less';
|
||||
|
||||
.avatarList {
|
||||
display: inline-block;
|
||||
ul {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
font-size: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.avatarItem {
|
||||
display: inline-block;
|
||||
width: @avatar-size-base;
|
||||
height: @avatar-size-base;
|
||||
margin-left: -8px;
|
||||
font-size: @font-size-base;
|
||||
:global {
|
||||
.ant-avatar {
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatarItemLarge {
|
||||
width: @avatar-size-lg;
|
||||
height: @avatar-size-lg;
|
||||
}
|
||||
|
||||
.avatarItemSmall {
|
||||
width: @avatar-size-sm;
|
||||
height: @avatar-size-sm;
|
||||
}
|
||||
|
||||
.avatarItemMini {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
:global {
|
||||
.ant-avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
|
||||
.ant-avatar-string {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
src/components/AvatarList/index.test.js
Normal file
29
src/components/AvatarList/index.test.js
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import range from 'lodash/range';
|
||||
import { mount } from 'enzyme';
|
||||
import AvatarList from './index';
|
||||
|
||||
const renderItems = numItems =>
|
||||
range(numItems).map(i => (
|
||||
<AvatarList.Item
|
||||
key={i}
|
||||
tips="Jake"
|
||||
src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png"
|
||||
/>
|
||||
));
|
||||
|
||||
describe('AvatarList', () => {
|
||||
it('renders all items', () => {
|
||||
const wrapper = mount(<AvatarList>{renderItems(4)}</AvatarList>);
|
||||
expect(wrapper.find('AvatarList').length).toBe(1);
|
||||
expect(wrapper.find('Item').length).toBe(4);
|
||||
expect(wrapper.findWhere(node => node.key() === 'exceed').length).toBe(0);
|
||||
});
|
||||
|
||||
it('renders max of 3 items', () => {
|
||||
const wrapper = mount(<AvatarList maxLength={3}>{renderItems(4)}</AvatarList>);
|
||||
expect(wrapper.find('AvatarList').length).toBe(1);
|
||||
expect(wrapper.find('Item').length).toBe(3);
|
||||
expect(wrapper.findWhere(node => node.key() === 'exceed').length).toBe(1);
|
||||
});
|
||||
});
|
25
src/components/AvatarList/index.zh-CN.md
Normal file
25
src/components/AvatarList/index.zh-CN.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: AvatarList
|
||||
subtitle: 用户头像列表
|
||||
order: 1
|
||||
cols: 1
|
||||
---
|
||||
|
||||
一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。
|
||||
|
||||
## API
|
||||
|
||||
### AvatarList
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ---------------- | -------- | ---------------------------------- | --------- |
|
||||
| size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` |
|
||||
| maxLength | 要显示的最大项目 | number | - |
|
||||
| excessItemsStyle | 多余的项目风格 | CSSProperties | - |
|
||||
|
||||
### AvatarList.Item
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ---- | ------ | --------- | --- |
|
||||
| tips | 头像展示文案 | ReactNode | - |
|
||||
| src | 头像图片连接 | string | - |
|
15
src/components/Charts/Bar/index.d.ts
vendored
Normal file
15
src/components/Charts/Bar/index.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
export interface IBarProps {
|
||||
title: React.ReactNode;
|
||||
color?: string;
|
||||
padding?: [number, number, number, number];
|
||||
height: number;
|
||||
data: Array<{
|
||||
x: string;
|
||||
y: number;
|
||||
}>;
|
||||
autoLabel?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class Bar extends React.Component<IBarProps, any> {}
|
113
src/components/Charts/Bar/index.js
Normal file
113
src/components/Charts/Bar/index.js
Normal file
@ -0,0 +1,113 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Chart, Axis, Tooltip, Geom } from 'bizcharts';
|
||||
import Debounce from 'lodash-decorators/debounce';
|
||||
import Bind from 'lodash-decorators/bind';
|
||||
import autoHeight from '../autoHeight';
|
||||
import styles from '../index.less';
|
||||
|
||||
@autoHeight()
|
||||
class Bar extends Component {
|
||||
state = {
|
||||
autoHideXLabels: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this.resize, { passive: true });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.resize);
|
||||
}
|
||||
|
||||
handleRoot = n => {
|
||||
this.root = n;
|
||||
};
|
||||
|
||||
handleRef = n => {
|
||||
this.node = n;
|
||||
};
|
||||
|
||||
@Bind()
|
||||
@Debounce(400)
|
||||
resize() {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
const canvasWidth = this.node.parentNode.clientWidth;
|
||||
const { data = [], autoLabel = true } = this.props;
|
||||
if (!autoLabel) {
|
||||
return;
|
||||
}
|
||||
const minWidth = data.length * 30;
|
||||
const { autoHideXLabels } = this.state;
|
||||
|
||||
if (canvasWidth <= minWidth) {
|
||||
if (!autoHideXLabels) {
|
||||
this.setState({
|
||||
autoHideXLabels: true,
|
||||
});
|
||||
}
|
||||
} else if (autoHideXLabels) {
|
||||
this.setState({
|
||||
autoHideXLabels: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
height,
|
||||
title,
|
||||
forceFit = true,
|
||||
data,
|
||||
color = 'rgba(24, 144, 255, 0.85)',
|
||||
padding,
|
||||
} = this.props;
|
||||
|
||||
const { autoHideXLabels } = this.state;
|
||||
|
||||
const scale = {
|
||||
x: {
|
||||
type: 'cat',
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y,
|
||||
}),
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.chart} style={{ height }} ref={this.handleRoot}>
|
||||
<div ref={this.handleRef}>
|
||||
{title && <h4 style={{ marginBottom: 20 }}>{title}</h4>}
|
||||
<Chart
|
||||
scale={scale}
|
||||
height={title ? height - 41 : height}
|
||||
forceFit={forceFit}
|
||||
data={data}
|
||||
padding={padding || 'auto'}
|
||||
>
|
||||
<Axis
|
||||
name="x"
|
||||
title={false}
|
||||
label={autoHideXLabels ? false : {}}
|
||||
tickLine={autoHideXLabels ? false : {}}
|
||||
/>
|
||||
<Axis name="y" min={0} />
|
||||
<Tooltip showTitle={false} crosshairs={false} />
|
||||
<Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
|
||||
</Chart>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Bar;
|
14
src/components/Charts/ChartCard/index.d.ts
vendored
Normal file
14
src/components/Charts/ChartCard/index.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import { CardProps } from 'antd/lib/card';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IChartCardProps extends CardProps {
|
||||
title: React.ReactNode;
|
||||
action?: React.ReactNode;
|
||||
total?: React.ReactNode | number | (() => React.ReactNode | number);
|
||||
footer?: React.ReactNode;
|
||||
contentHeight?: number;
|
||||
avatar?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class ChartCard extends React.Component<IChartCardProps, any> {}
|
82
src/components/Charts/ChartCard/index.js
Normal file
82
src/components/Charts/ChartCard/index.js
Normal file
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const renderTotal = total => {
|
||||
let totalDom;
|
||||
switch (typeof total) {
|
||||
case 'undefined':
|
||||
totalDom = null;
|
||||
break;
|
||||
case 'function':
|
||||
totalDom = <div className={styles.total}>{total()}</div>;
|
||||
break;
|
||||
default:
|
||||
totalDom = <div className={styles.total}>{total}</div>;
|
||||
}
|
||||
return totalDom;
|
||||
};
|
||||
|
||||
class ChartCard extends React.PureComponent {
|
||||
renderConnet = () => {
|
||||
const { contentHeight, title, avatar, action, total, footer, children, loading } = this.props;
|
||||
if (loading) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
<div className={styles.chartCard}>
|
||||
<div
|
||||
className={classNames(styles.chartTop, {
|
||||
[styles.chartTopMargin]: !children && !footer,
|
||||
})}
|
||||
>
|
||||
<div className={styles.avatar}>{avatar}</div>
|
||||
<div className={styles.metaWrap}>
|
||||
<div className={styles.meta}>
|
||||
<span className={styles.title}>{title}</span>
|
||||
<span className={styles.action}>{action}</span>
|
||||
</div>
|
||||
{renderTotal(total)}
|
||||
</div>
|
||||
</div>
|
||||
{children && (
|
||||
<div className={styles.content} style={{ height: contentHeight || 'auto' }}>
|
||||
<div className={contentHeight && styles.contentFixed}>{children}</div>
|
||||
</div>
|
||||
)}
|
||||
{footer && (
|
||||
<div
|
||||
className={classNames(styles.footer, {
|
||||
[styles.footerMargin]: !children,
|
||||
})}
|
||||
>
|
||||
{footer}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
loading = false,
|
||||
contentHeight,
|
||||
title,
|
||||
avatar,
|
||||
action,
|
||||
total,
|
||||
footer,
|
||||
children,
|
||||
...rest
|
||||
} = this.props;
|
||||
return (
|
||||
<Card loading={loading} bodyStyle={{ padding: '20px 24px 8px 24px' }} {...rest}>
|
||||
{this.renderConnet()}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChartCard;
|
75
src/components/Charts/ChartCard/index.less
Normal file
75
src/components/Charts/ChartCard/index.less
Normal file
@ -0,0 +1,75 @@
|
||||
@import '~antd/lib/style/themes/default.less';
|
||||
|
||||
.chartCard {
|
||||
position: relative;
|
||||
.chartTop {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
.chartTopMargin {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.chartTopHasMargin {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.metaWrap {
|
||||
float: left;
|
||||
}
|
||||
.avatar {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
img {
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
.meta {
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
line-height: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.action {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
.total {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
color: @heading-color;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
font-size: 30px;
|
||||
line-height: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
.content {
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.contentFixed {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.footer {
|
||||
border-top: 1px solid @border-color-split;
|
||||
padding-top: 9px;
|
||||
margin-top: 8px;
|
||||
& > * {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
.footerMargin {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
8
src/components/Charts/Field/index.d.ts
vendored
Normal file
8
src/components/Charts/Field/index.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
export interface IFieldProps {
|
||||
label: React.ReactNode;
|
||||
value: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class Field extends React.Component<IFieldProps, any> {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user