mirror of
https://github.com/chillzhuang/Sword
synced 2024-11-22 02:09:26 +08:00
🎉 2.6.1发布,增加登陆验证码,支持Seata1.0
This commit is contained in:
parent
79d58cd5c0
commit
2bae7753bd
@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/Release-V2.6.0-green.svg" alt="Downloads">
|
<img src="https://img.shields.io/badge/Release-V2.6.1-green.svg" alt="Downloads">
|
||||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status">
|
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status">
|
||||||
<img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status">
|
<img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status">
|
||||||
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR1-blue.svg" alt="Coverage Status">
|
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR1-blue.svg" alt="Coverage Status">
|
||||||
@ -56,8 +56,9 @@ SpringBlade
|
|||||||
* 官网地址:[https://bladex.vip](https://bladex.vip)
|
* 官网地址:[https://bladex.vip](https://bladex.vip)
|
||||||
* 问答社区:[https://sns.bladex.vip](https://sns.bladex.vip)
|
* 问答社区:[https://sns.bladex.vip](https://sns.bladex.vip)
|
||||||
* 会员计划:[SpringBlade会员计划](https://gitee.com/smallc/SpringBlade/wikis/SpringBlade会员计划)
|
* 会员计划:[SpringBlade会员计划](https://gitee.com/smallc/SpringBlade/wikis/SpringBlade会员计划)
|
||||||
* 交流一群:`477853168`
|
* 交流一群:`477853168`(满)
|
||||||
* 交流二群:`751253339`
|
* 交流二群:`751253339`(满)
|
||||||
|
* 交流三群:`784729540`
|
||||||
|
|
||||||
## 在线演示
|
## 在线演示
|
||||||
* Saber-基于Vue:[https://saber.bladex.vip](https://saber.bladex.vip)
|
* Saber-基于Vue:[https://saber.bladex.vip](https://saber.bladex.vip)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sword",
|
"name": "sword",
|
||||||
"version": "2.6.0",
|
"version": "2.6.1",
|
||||||
"description": "An out-of-box UI solution for enterprise applications",
|
"description": "An out-of-box UI solution for enterprise applications",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
@ -1,3 +0,0 @@
|
|||||||
const generateMock = require('merge-umi-mock-data');
|
|
||||||
const path = require('path');
|
|
||||||
generateMock(path.join(__dirname, '../mock'), path.join(__dirname, '../functions/mock/index.js'));
|
|
@ -1,23 +0,0 @@
|
|||||||
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;
|
|
@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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!'));
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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!'));
|
|
@ -1,36 +0,0 @@
|
|||||||
export const CLIENT_NAMESPACE = 'client';
|
|
||||||
|
|
||||||
export function CLIENT_LIST(payload) {
|
|
||||||
return {
|
|
||||||
type: `${CLIENT_NAMESPACE}/fetchList`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CLIENT_DETAIL(id) {
|
|
||||||
return {
|
|
||||||
type: `${CLIENT_NAMESPACE}/fetchDetail`,
|
|
||||||
payload: { id },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CLIENT_CLEAR_DETAIL() {
|
|
||||||
return {
|
|
||||||
type: `${CLIENT_NAMESPACE}/clearDetail`,
|
|
||||||
payload: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CLIENT_SUBMIT(payload) {
|
|
||||||
return {
|
|
||||||
type: `${CLIENT_NAMESPACE}/submit`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CLIENT_REMOVE(payload) {
|
|
||||||
return {
|
|
||||||
type: `${CLIENT_NAMESPACE}/remove`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
export const CODE_NAMESPACE = 'code';
|
|
||||||
|
|
||||||
export function CODE_LIST(payload) {
|
|
||||||
return {
|
|
||||||
type: `${CODE_NAMESPACE}/fetchList`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CODE_INIT() {
|
|
||||||
return {
|
|
||||||
type: `${CODE_NAMESPACE}/fetchInit`,
|
|
||||||
payload: { code: 'yes_no' },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
export const DATASOURCE_NAMESPACE = 'datasource';
|
|
||||||
|
|
||||||
export function DATASOURCE_LIST(payload) {
|
|
||||||
return {
|
|
||||||
type: `${DATASOURCE_NAMESPACE}/fetchList`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DATASOURCE_DETAIL(id) {
|
|
||||||
return {
|
|
||||||
type: `${DATASOURCE_NAMESPACE}/fetchDetail`,
|
|
||||||
payload: { id },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DATASOURCE_CLEAR_DETAIL() {
|
|
||||||
return {
|
|
||||||
type: `${DATASOURCE_NAMESPACE}/clearDetail`,
|
|
||||||
payload: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DATASOURCE_SUBMIT(payload) {
|
|
||||||
return {
|
|
||||||
type: `${DATASOURCE_NAMESPACE}/submit`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DATASOURCE_REMOVE(payload) {
|
|
||||||
return {
|
|
||||||
type: `${DATASOURCE_NAMESPACE}/remove`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
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 },
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
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,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
export const TENANT_NAMESPACE = 'tenant';
|
|
||||||
|
|
||||||
export function TENANT_LIST(payload) {
|
|
||||||
return {
|
|
||||||
type: `${TENANT_NAMESPACE}/fetchList`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TENANT_DETAIL(id) {
|
|
||||||
return {
|
|
||||||
type: `${TENANT_NAMESPACE}/fetchDetail`,
|
|
||||||
payload: { id },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TENANT_CLEAR_DETAIL() {
|
|
||||||
return {
|
|
||||||
type: `${TENANT_NAMESPACE}/clearDetail`,
|
|
||||||
payload: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TENANT_SUBMIT(payload) {
|
|
||||||
return {
|
|
||||||
type: `${TENANT_NAMESPACE}/submit`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TENANT_REMOVE(payload) {
|
|
||||||
return {
|
|
||||||
type: `${TENANT_NAMESPACE}/remove`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
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_CHANGE_INIT(payload) {
|
|
||||||
return {
|
|
||||||
type: `${USER_NAMESPACE}/fetchChangeInit`,
|
|
||||||
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_UPDATE(payload) {
|
|
||||||
return {
|
|
||||||
type: `${USER_NAMESPACE}/update`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function USER_REMOVE(payload) {
|
|
||||||
return {
|
|
||||||
type: `${USER_NAMESPACE}/remove`,
|
|
||||||
payload,
|
|
||||||
};
|
|
||||||
}
|
|
43
src/app.js
43
src/app.js
@ -1,43 +0,0 @@
|
|||||||
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) {
|
|
||||||
if (authRoutes !== null && authRoutes !== undefined) {
|
|
||||||
Object.keys(authRoutes).map(authKey =>
|
|
||||||
ergodicRoutes(routes, authKey, authRoutes[authKey].authority)
|
|
||||||
);
|
|
||||||
window.g_routes = routes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function render(oldRender) {
|
|
||||||
routesAuthority().then(response => {
|
|
||||||
if (response && response.success) {
|
|
||||||
authRoutes = response.data;
|
|
||||||
}
|
|
||||||
oldRender();
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,160 +0,0 @@
|
|||||||
<?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>
|
|
@ -1,101 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
.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;
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
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}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,17 +0,0 @@
|
|||||||
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;
|
|
@ -1,38 +0,0 @@
|
|||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
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
13
src/components/Authorized/AuthorizedRoute.d.ts
vendored
@ -1,13 +0,0 @@
|
|||||||
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> {}
|
|
@ -1,15 +0,0 @@
|
|||||||
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;
|
|
@ -1,88 +0,0 @@
|
|||||||
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;
|
|
@ -1,55 +0,0 @@
|
|||||||
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');
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,65 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
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;
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
```
|
|
@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
```
|
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
```
|
|
@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
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
32
src/components/Authorized/index.d.ts
vendored
@ -1,32 +0,0 @@
|
|||||||
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;
|
|
@ -1,11 +0,0 @@
|
|||||||
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);
|
|
@ -1,58 +0,0 @@
|
|||||||
---
|
|
||||||
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 | - |
|
|
@ -1,25 +0,0 @@
|
|||||||
/* 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
10
src/components/AvatarList/AvatarItem.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
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);
|
|
||||||
````
|
|
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
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
14
src/components/AvatarList/index.d.ts
vendored
@ -1,14 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
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 | - |
|
|
@ -1,61 +0,0 @@
|
|||||||
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;
|
|
@ -1,50 +0,0 @@
|
|||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
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
15
src/components/Charts/Bar/index.d.ts
vendored
@ -1,15 +0,0 @@
|
|||||||
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> {}
|
|
@ -1,113 +0,0 @@
|
|||||||
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
14
src/components/Charts/ChartCard/index.d.ts
vendored
@ -1,14 +0,0 @@
|
|||||||
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> {}
|
|
@ -1,82 +0,0 @@
|
|||||||
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;
|
|
@ -1,75 +0,0 @@
|
|||||||
@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
8
src/components/Charts/Field/index.d.ts
vendored
@ -1,8 +0,0 @@
|
|||||||
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> {}
|
|
@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const Field = ({ label, value, ...rest }) => (
|
|
||||||
<div className={styles.field} {...rest}>
|
|
||||||
<span className={styles.label}>{label}</span>
|
|
||||||
<span className={styles.number}>{value}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Field;
|
|
@ -1,17 +0,0 @@
|
|||||||
@import '~antd/lib/style/themes/default.less';
|
|
||||||
|
|
||||||
.field {
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
.label,
|
|
||||||
.number {
|
|
||||||
font-size: @font-size-base;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
.number {
|
|
||||||
margin-left: 8px;
|
|
||||||
color: @heading-color;
|
|
||||||
}
|
|
||||||
}
|
|
11
src/components/Charts/Gauge/index.d.ts
vendored
11
src/components/Charts/Gauge/index.d.ts
vendored
@ -1,11 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface IGaugeProps {
|
|
||||||
title: React.ReactNode;
|
|
||||||
color?: string;
|
|
||||||
height: number;
|
|
||||||
bgColor?: number;
|
|
||||||
percent: number;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Gauge extends React.Component<IGaugeProps, any> {}
|
|
@ -1,167 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Chart, Geom, Axis, Coord, Guide, Shape } from 'bizcharts';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
|
|
||||||
const { Arc, Html, Line } = Guide;
|
|
||||||
|
|
||||||
const defaultFormatter = val => {
|
|
||||||
switch (val) {
|
|
||||||
case '2':
|
|
||||||
return '差';
|
|
||||||
case '4':
|
|
||||||
return '中';
|
|
||||||
case '6':
|
|
||||||
return '良';
|
|
||||||
case '8':
|
|
||||||
return '优';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Shape.registerShape('point', 'pointer', {
|
|
||||||
drawShape(cfg, group) {
|
|
||||||
let point = cfg.points[0];
|
|
||||||
point = this.parsePoint(point);
|
|
||||||
const center = this.parsePoint({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
});
|
|
||||||
group.addShape('line', {
|
|
||||||
attrs: {
|
|
||||||
x1: center.x,
|
|
||||||
y1: center.y,
|
|
||||||
x2: point.x,
|
|
||||||
y2: point.y,
|
|
||||||
stroke: cfg.color,
|
|
||||||
lineWidth: 2,
|
|
||||||
lineCap: 'round',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return group.addShape('circle', {
|
|
||||||
attrs: {
|
|
||||||
x: center.x,
|
|
||||||
y: center.y,
|
|
||||||
r: 6,
|
|
||||||
stroke: cfg.color,
|
|
||||||
lineWidth: 3,
|
|
||||||
fill: '#fff',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
@autoHeight()
|
|
||||||
class Gauge extends React.Component {
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
height,
|
|
||||||
percent,
|
|
||||||
forceFit = true,
|
|
||||||
formatter = defaultFormatter,
|
|
||||||
color = '#2F9CFF',
|
|
||||||
bgColor = '#F0F2F5',
|
|
||||||
} = this.props;
|
|
||||||
const cols = {
|
|
||||||
value: {
|
|
||||||
type: 'linear',
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
tickCount: 6,
|
|
||||||
nice: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const data = [{ value: percent / 10 }];
|
|
||||||
return (
|
|
||||||
<Chart height={height} data={data} scale={cols} padding={[-16, 0, 16, 0]} forceFit={forceFit}>
|
|
||||||
<Coord type="polar" startAngle={-1.25 * Math.PI} endAngle={0.25 * Math.PI} radius={0.8} />
|
|
||||||
<Axis name="1" line={null} />
|
|
||||||
<Axis
|
|
||||||
line={null}
|
|
||||||
tickLine={null}
|
|
||||||
subTickLine={null}
|
|
||||||
name="value"
|
|
||||||
zIndex={2}
|
|
||||||
gird={null}
|
|
||||||
label={{
|
|
||||||
offset: -12,
|
|
||||||
formatter,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: 12,
|
|
||||||
fill: 'rgba(0, 0, 0, 0.65)',
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Guide>
|
|
||||||
<Line
|
|
||||||
start={[3, 0.905]}
|
|
||||||
end={[3, 0.85]}
|
|
||||||
lineStyle={{
|
|
||||||
stroke: color,
|
|
||||||
lineDash: null,
|
|
||||||
lineWidth: 2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Line
|
|
||||||
start={[5, 0.905]}
|
|
||||||
end={[5, 0.85]}
|
|
||||||
lineStyle={{
|
|
||||||
stroke: color,
|
|
||||||
lineDash: null,
|
|
||||||
lineWidth: 3,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Line
|
|
||||||
start={[7, 0.905]}
|
|
||||||
end={[7, 0.85]}
|
|
||||||
lineStyle={{
|
|
||||||
stroke: color,
|
|
||||||
lineDash: null,
|
|
||||||
lineWidth: 3,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Arc
|
|
||||||
zIndex={0}
|
|
||||||
start={[0, 0.965]}
|
|
||||||
end={[10, 0.965]}
|
|
||||||
style={{
|
|
||||||
stroke: bgColor,
|
|
||||||
lineWidth: 10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Arc
|
|
||||||
zIndex={1}
|
|
||||||
start={[0, 0.965]}
|
|
||||||
end={[data[0].value, 0.965]}
|
|
||||||
style={{
|
|
||||||
stroke: color,
|
|
||||||
lineWidth: 10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Html
|
|
||||||
position={['50%', '95%']}
|
|
||||||
html={() => `
|
|
||||||
<div style="width: 300px;text-align: center;font-size: 12px!important;">
|
|
||||||
<p style="font-size: 14px; color: rgba(0,0,0,0.43);margin: 0;">${title}</p>
|
|
||||||
<p style="font-size: 24px;color: rgba(0,0,0,0.85);margin: 0;">
|
|
||||||
${data[0].value * 10}%
|
|
||||||
</p>
|
|
||||||
</div>`}
|
|
||||||
/>
|
|
||||||
</Guide>
|
|
||||||
<Geom
|
|
||||||
line={false}
|
|
||||||
type="point"
|
|
||||||
position="value*1"
|
|
||||||
shape="pointer"
|
|
||||||
color={color}
|
|
||||||
active={false}
|
|
||||||
/>
|
|
||||||
</Chart>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Gauge;
|
|
29
src/components/Charts/MiniArea/index.d.ts
vendored
29
src/components/Charts/MiniArea/index.d.ts
vendored
@ -1,29 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
// g2已经更新到3.0
|
|
||||||
// 不带的写了
|
|
||||||
|
|
||||||
export interface IAxis {
|
|
||||||
title: any;
|
|
||||||
line: any;
|
|
||||||
gridAlign: any;
|
|
||||||
labels: any;
|
|
||||||
tickLine: any;
|
|
||||||
grid: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IMiniAreaProps {
|
|
||||||
color?: string;
|
|
||||||
height: number;
|
|
||||||
borderColor?: string;
|
|
||||||
line?: boolean;
|
|
||||||
animate?: boolean;
|
|
||||||
xAxis?: IAxis;
|
|
||||||
yAxis?: IAxis;
|
|
||||||
data: Array<{
|
|
||||||
x: number | string;
|
|
||||||
y: number;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MiniArea extends React.Component<IMiniAreaProps, any> {}
|
|
@ -1,108 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Chart, Axis, Tooltip, Geom } from 'bizcharts';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
import styles from '../index.less';
|
|
||||||
|
|
||||||
@autoHeight()
|
|
||||||
class MiniArea extends React.PureComponent {
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
height,
|
|
||||||
data = [],
|
|
||||||
forceFit = true,
|
|
||||||
color = 'rgba(24, 144, 255, 0.2)',
|
|
||||||
borderColor = '#1089ff',
|
|
||||||
scale = {},
|
|
||||||
borderWidth = 2,
|
|
||||||
line,
|
|
||||||
xAxis,
|
|
||||||
yAxis,
|
|
||||||
animate = true,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const padding = [36, 5, 30, 5];
|
|
||||||
|
|
||||||
const scaleProps = {
|
|
||||||
x: {
|
|
||||||
type: 'cat',
|
|
||||||
range: [0, 1],
|
|
||||||
...scale.x,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
min: 0,
|
|
||||||
...scale.y,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const tooltip = [
|
|
||||||
'x*y',
|
|
||||||
(x, y) => ({
|
|
||||||
name: x,
|
|
||||||
value: y,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const chartHeight = height + 54;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.miniChart} style={{ height }}>
|
|
||||||
<div className={styles.chartContent}>
|
|
||||||
{height > 0 && (
|
|
||||||
<Chart
|
|
||||||
animate={animate}
|
|
||||||
scale={scaleProps}
|
|
||||||
height={chartHeight}
|
|
||||||
forceFit={forceFit}
|
|
||||||
data={data}
|
|
||||||
padding={padding}
|
|
||||||
>
|
|
||||||
<Axis
|
|
||||||
key="axis-x"
|
|
||||||
name="x"
|
|
||||||
label={false}
|
|
||||||
line={false}
|
|
||||||
tickLine={false}
|
|
||||||
grid={false}
|
|
||||||
{...xAxis}
|
|
||||||
/>
|
|
||||||
<Axis
|
|
||||||
key="axis-y"
|
|
||||||
name="y"
|
|
||||||
label={false}
|
|
||||||
line={false}
|
|
||||||
tickLine={false}
|
|
||||||
grid={false}
|
|
||||||
{...yAxis}
|
|
||||||
/>
|
|
||||||
<Tooltip showTitle={false} crosshairs={false} />
|
|
||||||
<Geom
|
|
||||||
type="area"
|
|
||||||
position="x*y"
|
|
||||||
color={color}
|
|
||||||
tooltip={tooltip}
|
|
||||||
shape="smooth"
|
|
||||||
style={{
|
|
||||||
fillOpacity: 1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{line ? (
|
|
||||||
<Geom
|
|
||||||
type="line"
|
|
||||||
position="x*y"
|
|
||||||
shape="smooth"
|
|
||||||
color={borderColor}
|
|
||||||
size={borderWidth}
|
|
||||||
tooltip={false}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span style={{ display: 'none' }} />
|
|
||||||
)}
|
|
||||||
</Chart>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MiniArea;
|
|
12
src/components/Charts/MiniBar/index.d.ts
vendored
12
src/components/Charts/MiniBar/index.d.ts
vendored
@ -1,12 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface IMiniBarProps {
|
|
||||||
color?: string;
|
|
||||||
height: number;
|
|
||||||
data: Array<{
|
|
||||||
x: number | string;
|
|
||||||
y: number;
|
|
||||||
}>;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MiniBar extends React.Component<IMiniBarProps, any> {}
|
|
@ -1,51 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Chart, Tooltip, Geom } from 'bizcharts';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
import styles from '../index.less';
|
|
||||||
|
|
||||||
@autoHeight()
|
|
||||||
class MiniBar extends React.Component {
|
|
||||||
render() {
|
|
||||||
const { height, forceFit = true, color = '#1890FF', data = [] } = this.props;
|
|
||||||
|
|
||||||
const scale = {
|
|
||||||
x: {
|
|
||||||
type: 'cat',
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const padding = [36, 5, 30, 5];
|
|
||||||
|
|
||||||
const tooltip = [
|
|
||||||
'x*y',
|
|
||||||
(x, y) => ({
|
|
||||||
name: x,
|
|
||||||
value: y,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// for tooltip not to be hide
|
|
||||||
const chartHeight = height + 54;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.miniChart} style={{ height }}>
|
|
||||||
<div className={styles.chartContent}>
|
|
||||||
<Chart
|
|
||||||
scale={scale}
|
|
||||||
height={chartHeight}
|
|
||||||
forceFit={forceFit}
|
|
||||||
data={data}
|
|
||||||
padding={padding}
|
|
||||||
>
|
|
||||||
<Tooltip showTitle={false} crosshairs={false} />
|
|
||||||
<Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
|
|
||||||
</Chart>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default MiniBar;
|
|
10
src/components/Charts/MiniProgress/index.d.ts
vendored
10
src/components/Charts/MiniProgress/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface IMiniProgressProps {
|
|
||||||
target: number;
|
|
||||||
color?: string;
|
|
||||||
strokeWidth?: number;
|
|
||||||
percent?: number;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MiniProgress extends React.Component<IMiniProgressProps, any> {}
|
|
@ -1,27 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Tooltip } from 'antd';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => (
|
|
||||||
<div className={styles.miniProgress}>
|
|
||||||
<Tooltip title={`目标值: ${target}%`}>
|
|
||||||
<div className={styles.target} style={{ left: target ? `${target}%` : null }}>
|
|
||||||
<span style={{ backgroundColor: color || null }} />
|
|
||||||
<span style={{ backgroundColor: color || null }} />
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
<div className={styles.progressWrap}>
|
|
||||||
<div
|
|
||||||
className={styles.progress}
|
|
||||||
style={{
|
|
||||||
backgroundColor: color || null,
|
|
||||||
width: percent ? `${percent}%` : null,
|
|
||||||
height: strokeWidth || null,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default MiniProgress;
|
|
@ -1,35 +0,0 @@
|
|||||||
@import '~antd/lib/style/themes/default.less';
|
|
||||||
|
|
||||||
.miniProgress {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px 0;
|
|
||||||
.progressWrap {
|
|
||||||
position: relative;
|
|
||||||
background-color: @background-color-base;
|
|
||||||
}
|
|
||||||
.progress {
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
background-color: @primary-color;
|
|
||||||
border-radius: 1px 0 0 1px;
|
|
||||||
transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
|
|
||||||
}
|
|
||||||
.target {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
span {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 2px;
|
|
||||||
height: 4px;
|
|
||||||
border-radius: 100px;
|
|
||||||
}
|
|
||||||
span:last-child {
|
|
||||||
top: auto;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
21
src/components/Charts/Pie/index.d.ts
vendored
21
src/components/Charts/Pie/index.d.ts
vendored
@ -1,21 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface IPieProps {
|
|
||||||
animate?: boolean;
|
|
||||||
color?: string;
|
|
||||||
colors?: string[];
|
|
||||||
height: number;
|
|
||||||
hasLegend?: boolean;
|
|
||||||
padding?: [number, number, number, number];
|
|
||||||
percent?: number;
|
|
||||||
data?: Array<{
|
|
||||||
x: string | string;
|
|
||||||
y: number;
|
|
||||||
}>;
|
|
||||||
total?: React.ReactNode | number | (() => React.ReactNode | number);
|
|
||||||
title?: React.ReactNode;
|
|
||||||
tooltip?: boolean;
|
|
||||||
valueFormat?: (value: string) => string | React.ReactNode;
|
|
||||||
subTitle?: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Pie extends React.Component<IPieProps, any> {}
|
|
@ -1,271 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import { Chart, Tooltip, Geom, Coord } from 'bizcharts';
|
|
||||||
import { DataView } from '@antv/data-set';
|
|
||||||
import { Divider } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import ReactFitText from 'react-fittext';
|
|
||||||
import Debounce from 'lodash-decorators/debounce';
|
|
||||||
import Bind from 'lodash-decorators/bind';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
/* eslint react/no-danger:0 */
|
|
||||||
@autoHeight()
|
|
||||||
class Pie extends Component {
|
|
||||||
state = {
|
|
||||||
legendData: [],
|
|
||||||
legendBlock: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.addEventListener(
|
|
||||||
'resize',
|
|
||||||
() => {
|
|
||||||
this.requestRef = requestAnimationFrame(() => this.resize());
|
|
||||||
},
|
|
||||||
{ passive: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(preProps) {
|
|
||||||
const { data } = this.props;
|
|
||||||
if (data !== preProps.data) {
|
|
||||||
// because of charts data create when rendered
|
|
||||||
// so there is a trick for get rendered time
|
|
||||||
this.getLegendData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
window.cancelAnimationFrame(this.requestRef);
|
|
||||||
window.removeEventListener('resize', this.resize);
|
|
||||||
this.resize.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
getG2Instance = chart => {
|
|
||||||
this.chart = chart;
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
this.getLegendData();
|
|
||||||
this.resize();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// for custom lengend view
|
|
||||||
getLegendData = () => {
|
|
||||||
if (!this.chart) return;
|
|
||||||
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
|
|
||||||
if (!geom) return;
|
|
||||||
const items = geom.get('dataArray') || []; // 获取图形对应的
|
|
||||||
|
|
||||||
const legendData = items.map(item => {
|
|
||||||
/* eslint no-underscore-dangle:0 */
|
|
||||||
const origin = item[0]._origin;
|
|
||||||
origin.color = item[0].color;
|
|
||||||
origin.checked = true;
|
|
||||||
return origin;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
legendData,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleRoot = n => {
|
|
||||||
this.root = n;
|
|
||||||
};
|
|
||||||
|
|
||||||
handleLegendClick = (item, i) => {
|
|
||||||
const newItem = item;
|
|
||||||
newItem.checked = !newItem.checked;
|
|
||||||
|
|
||||||
const { legendData } = this.state;
|
|
||||||
legendData[i] = newItem;
|
|
||||||
|
|
||||||
const filteredLegendData = legendData.filter(l => l.checked).map(l => l.x);
|
|
||||||
|
|
||||||
if (this.chart) {
|
|
||||||
this.chart.filter('x', val => filteredLegendData.indexOf(val) > -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
legendData,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// for window resize auto responsive legend
|
|
||||||
@Bind()
|
|
||||||
@Debounce(300)
|
|
||||||
resize() {
|
|
||||||
const { hasLegend } = this.props;
|
|
||||||
const { legendBlock } = this.state;
|
|
||||||
if (!hasLegend || !this.root) {
|
|
||||||
window.removeEventListener('resize', this.resize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.root.parentNode.clientWidth <= 380) {
|
|
||||||
if (!legendBlock) {
|
|
||||||
this.setState({
|
|
||||||
legendBlock: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (legendBlock) {
|
|
||||||
this.setState({
|
|
||||||
legendBlock: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
valueFormat,
|
|
||||||
subTitle,
|
|
||||||
total,
|
|
||||||
hasLegend = false,
|
|
||||||
className,
|
|
||||||
style,
|
|
||||||
height,
|
|
||||||
forceFit = true,
|
|
||||||
percent,
|
|
||||||
color,
|
|
||||||
inner = 0.75,
|
|
||||||
animate = true,
|
|
||||||
colors,
|
|
||||||
lineWidth = 1,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { legendData, legendBlock } = this.state;
|
|
||||||
const pieClassName = classNames(styles.pie, className, {
|
|
||||||
[styles.hasLegend]: !!hasLegend,
|
|
||||||
[styles.legendBlock]: legendBlock,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: propsData,
|
|
||||||
selected: propsSelected = true,
|
|
||||||
tooltip: propsTooltip = true,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let data = propsData || [];
|
|
||||||
let selected = propsSelected;
|
|
||||||
let tooltip = propsTooltip;
|
|
||||||
|
|
||||||
const defaultColors = colors;
|
|
||||||
data = data || [];
|
|
||||||
selected = selected || true;
|
|
||||||
tooltip = tooltip || true;
|
|
||||||
let formatColor;
|
|
||||||
|
|
||||||
const scale = {
|
|
||||||
x: {
|
|
||||||
type: 'cat',
|
|
||||||
range: [0, 1],
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (percent || percent === 0) {
|
|
||||||
selected = false;
|
|
||||||
tooltip = false;
|
|
||||||
formatColor = value => {
|
|
||||||
if (value === '占比') {
|
|
||||||
return color || 'rgba(24, 144, 255, 0.85)';
|
|
||||||
}
|
|
||||||
return '#F0F2F5';
|
|
||||||
};
|
|
||||||
|
|
||||||
data = [
|
|
||||||
{
|
|
||||||
x: '占比',
|
|
||||||
y: parseFloat(percent),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: '反比',
|
|
||||||
y: 100 - parseFloat(percent),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const tooltipFormat = [
|
|
||||||
'x*percent',
|
|
||||||
(x, p) => ({
|
|
||||||
name: x,
|
|
||||||
value: `${(p * 100).toFixed(2)}%`,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const padding = [12, 0, 12, 0];
|
|
||||||
|
|
||||||
const dv = new DataView();
|
|
||||||
dv.source(data).transform({
|
|
||||||
type: 'percent',
|
|
||||||
field: 'y',
|
|
||||||
dimension: 'x',
|
|
||||||
as: 'percent',
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={this.handleRoot} className={pieClassName} style={style}>
|
|
||||||
<ReactFitText maxFontSize={25}>
|
|
||||||
<div className={styles.chart}>
|
|
||||||
<Chart
|
|
||||||
scale={scale}
|
|
||||||
height={height}
|
|
||||||
forceFit={forceFit}
|
|
||||||
data={dv}
|
|
||||||
padding={padding}
|
|
||||||
animate={animate}
|
|
||||||
onGetG2Instance={this.getG2Instance}
|
|
||||||
>
|
|
||||||
{!!tooltip && <Tooltip showTitle={false} />}
|
|
||||||
<Coord type="theta" innerRadius={inner} />
|
|
||||||
<Geom
|
|
||||||
style={{ lineWidth, stroke: '#fff' }}
|
|
||||||
tooltip={tooltip && tooltipFormat}
|
|
||||||
type="intervalStack"
|
|
||||||
position="percent"
|
|
||||||
color={['x', percent || percent === 0 ? formatColor : defaultColors]}
|
|
||||||
selected={selected}
|
|
||||||
/>
|
|
||||||
</Chart>
|
|
||||||
|
|
||||||
{(subTitle || total) && (
|
|
||||||
<div className={styles.total}>
|
|
||||||
{subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
|
|
||||||
{/* eslint-disable-next-line */}
|
|
||||||
{total && (
|
|
||||||
<div className="pie-stat">{typeof total === 'function' ? total() : total}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ReactFitText>
|
|
||||||
|
|
||||||
{hasLegend && (
|
|
||||||
<ul className={styles.legend}>
|
|
||||||
{legendData.map((item, i) => (
|
|
||||||
<li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
|
|
||||||
<span
|
|
||||||
className={styles.dot}
|
|
||||||
style={{
|
|
||||||
backgroundColor: !item.checked ? '#aaa' : item.color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className={styles.legendTitle}>{item.x}</span>
|
|
||||||
<Divider type="vertical" />
|
|
||||||
<span className={styles.percent}>
|
|
||||||
{`${(Number.isNaN(item.percent) ? 0 : item.percent * 100).toFixed(2)}%`}
|
|
||||||
</span>
|
|
||||||
<span className={styles.value}>{valueFormat ? valueFormat(item.y) : item.y}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Pie;
|
|
@ -1,94 +0,0 @@
|
|||||||
@import '~antd/lib/style/themes/default.less';
|
|
||||||
|
|
||||||
.pie {
|
|
||||||
position: relative;
|
|
||||||
.chart {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
&.hasLegend .chart {
|
|
||||||
width: ~'calc(100% - 240px)';
|
|
||||||
}
|
|
||||||
.legend {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 0;
|
|
||||||
min-width: 200px;
|
|
||||||
margin: 0 20px;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
li {
|
|
||||||
height: 22px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
line-height: 22px;
|
|
||||||
cursor: pointer;
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dot {
|
|
||||||
position: relative;
|
|
||||||
top: -1px;
|
|
||||||
display: inline-block;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
margin-right: 8px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
.line {
|
|
||||||
display: inline-block;
|
|
||||||
width: 1px;
|
|
||||||
height: 16px;
|
|
||||||
margin-right: 8px;
|
|
||||||
background-color: @border-color-split;
|
|
||||||
}
|
|
||||||
.legendTitle {
|
|
||||||
color: @text-color;
|
|
||||||
}
|
|
||||||
.percent {
|
|
||||||
color: @text-color-secondary;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.total {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
max-height: 62px;
|
|
||||||
text-align: center;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
& > h4 {
|
|
||||||
height: 22px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: @text-color-secondary;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
& > p {
|
|
||||||
display: block;
|
|
||||||
height: 32px;
|
|
||||||
color: @heading-color;
|
|
||||||
font-size: 1.2em;
|
|
||||||
line-height: 32px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.legendBlock {
|
|
||||||
&.hasLegend .chart {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 0 32px 0;
|
|
||||||
}
|
|
||||||
.legend {
|
|
||||||
position: relative;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
15
src/components/Charts/Radar/index.d.ts
vendored
15
src/components/Charts/Radar/index.d.ts
vendored
@ -1,15 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface IRadarProps {
|
|
||||||
title?: React.ReactNode;
|
|
||||||
height: number;
|
|
||||||
padding?: [number, number, number, number];
|
|
||||||
hasLegend?: boolean;
|
|
||||||
data: Array<{
|
|
||||||
name: string;
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}>;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Radar extends React.Component<IRadarProps, any> {}
|
|
@ -1,184 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import { Chart, Tooltip, Geom, Coord, Axis } from 'bizcharts';
|
|
||||||
import { Row, Col } from 'antd';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
/* eslint react/no-danger:0 */
|
|
||||||
@autoHeight()
|
|
||||||
class Radar extends Component {
|
|
||||||
state = {
|
|
||||||
legendData: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.getLegendData();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(preProps) {
|
|
||||||
const { data } = this.props;
|
|
||||||
if (data !== preProps.data) {
|
|
||||||
this.getLegendData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getG2Instance = chart => {
|
|
||||||
this.chart = chart;
|
|
||||||
};
|
|
||||||
|
|
||||||
// for custom lengend view
|
|
||||||
getLegendData = () => {
|
|
||||||
if (!this.chart) return;
|
|
||||||
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
|
|
||||||
if (!geom) return;
|
|
||||||
const items = geom.get('dataArray') || []; // 获取图形对应的
|
|
||||||
|
|
||||||
const legendData = items.map(item => {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const origins = item.map(t => t._origin);
|
|
||||||
const result = {
|
|
||||||
name: origins[0].name,
|
|
||||||
color: item[0].color,
|
|
||||||
checked: true,
|
|
||||||
value: origins.reduce((p, n) => p + n.value, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
legendData,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleRef = n => {
|
|
||||||
this.node = n;
|
|
||||||
};
|
|
||||||
|
|
||||||
handleLegendClick = (item, i) => {
|
|
||||||
const newItem = item;
|
|
||||||
newItem.checked = !newItem.checked;
|
|
||||||
|
|
||||||
const { legendData } = this.state;
|
|
||||||
legendData[i] = newItem;
|
|
||||||
|
|
||||||
const filteredLegendData = legendData.filter(l => l.checked).map(l => l.name);
|
|
||||||
|
|
||||||
if (this.chart) {
|
|
||||||
this.chart.filter('name', val => filteredLegendData.indexOf(val) > -1);
|
|
||||||
this.chart.repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
legendData,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const defaultColors = [
|
|
||||||
'#1890FF',
|
|
||||||
'#FACC14',
|
|
||||||
'#2FC25B',
|
|
||||||
'#8543E0',
|
|
||||||
'#F04864',
|
|
||||||
'#13C2C2',
|
|
||||||
'#fa8c16',
|
|
||||||
'#a0d911',
|
|
||||||
];
|
|
||||||
|
|
||||||
const {
|
|
||||||
data = [],
|
|
||||||
height = 0,
|
|
||||||
title,
|
|
||||||
hasLegend = false,
|
|
||||||
forceFit = true,
|
|
||||||
tickCount = 5,
|
|
||||||
padding = [35, 30, 16, 30],
|
|
||||||
animate = true,
|
|
||||||
colors = defaultColors,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { legendData } = this.state;
|
|
||||||
|
|
||||||
const scale = {
|
|
||||||
value: {
|
|
||||||
min: 0,
|
|
||||||
tickCount,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartHeight = height - (hasLegend ? 80 : 22);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.radar} style={{ height }}>
|
|
||||||
{title && <h4>{title}</h4>}
|
|
||||||
<Chart
|
|
||||||
scale={scale}
|
|
||||||
height={chartHeight}
|
|
||||||
forceFit={forceFit}
|
|
||||||
data={data}
|
|
||||||
padding={padding}
|
|
||||||
animate={animate}
|
|
||||||
onGetG2Instance={this.getG2Instance}
|
|
||||||
>
|
|
||||||
<Tooltip />
|
|
||||||
<Coord type="polar" />
|
|
||||||
<Axis
|
|
||||||
name="label"
|
|
||||||
line={null}
|
|
||||||
tickLine={null}
|
|
||||||
grid={{
|
|
||||||
lineStyle: {
|
|
||||||
lineDash: null,
|
|
||||||
},
|
|
||||||
hideFirstLine: false,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Axis
|
|
||||||
name="value"
|
|
||||||
grid={{
|
|
||||||
type: 'polygon',
|
|
||||||
lineStyle: {
|
|
||||||
lineDash: null,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Geom type="line" position="label*value" color={['name', colors]} size={1} />
|
|
||||||
<Geom
|
|
||||||
type="point"
|
|
||||||
position="label*value"
|
|
||||||
color={['name', colors]}
|
|
||||||
shape="circle"
|
|
||||||
size={3}
|
|
||||||
/>
|
|
||||||
</Chart>
|
|
||||||
{hasLegend && (
|
|
||||||
<Row className={styles.legend}>
|
|
||||||
{legendData.map((item, i) => (
|
|
||||||
<Col
|
|
||||||
span={24 / legendData.length}
|
|
||||||
key={item.name}
|
|
||||||
onClick={() => this.handleLegendClick(item, i)}
|
|
||||||
>
|
|
||||||
<div className={styles.legendItem}>
|
|
||||||
<p>
|
|
||||||
<span
|
|
||||||
className={styles.dot}
|
|
||||||
style={{
|
|
||||||
backgroundColor: !item.checked ? '#aaa' : item.color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>{item.name}</span>
|
|
||||||
</p>
|
|
||||||
<h6>{item.value}</h6>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Radar;
|
|
@ -1,46 +0,0 @@
|
|||||||
@import '~antd/lib/style/themes/default.less';
|
|
||||||
|
|
||||||
.radar {
|
|
||||||
.legend {
|
|
||||||
margin-top: 16px;
|
|
||||||
.legendItem {
|
|
||||||
position: relative;
|
|
||||||
color: @text-color-secondary;
|
|
||||||
line-height: 22px;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
h6 {
|
|
||||||
margin-top: 4px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-left: 16px;
|
|
||||||
color: @heading-color;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
right: 0;
|
|
||||||
width: 1px;
|
|
||||||
height: 40px;
|
|
||||||
background-color: @border-color-split;
|
|
||||||
content: '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> :last-child .legendItem::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.dot {
|
|
||||||
position: relative;
|
|
||||||
top: -1px;
|
|
||||||
display: inline-block;
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
margin-right: 6px;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
src/components/Charts/TagCloud/index.d.ts
vendored
11
src/components/Charts/TagCloud/index.d.ts
vendored
@ -1,11 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface ITagCloudProps {
|
|
||||||
data: Array<{
|
|
||||||
name: string;
|
|
||||||
value: number;
|
|
||||||
}>;
|
|
||||||
height: number;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class TagCloud extends React.Component<ITagCloudProps, any> {}
|
|
@ -1,182 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import { Chart, Geom, Coord, Shape, Tooltip } from 'bizcharts';
|
|
||||||
import DataSet from '@antv/data-set';
|
|
||||||
import Debounce from 'lodash-decorators/debounce';
|
|
||||||
import Bind from 'lodash-decorators/bind';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
/* eslint no-underscore-dangle: 0 */
|
|
||||||
/* eslint no-param-reassign: 0 */
|
|
||||||
|
|
||||||
const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
|
|
||||||
|
|
||||||
@autoHeight()
|
|
||||||
class TagCloud extends Component {
|
|
||||||
state = {
|
|
||||||
dv: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
this.initTagCloud();
|
|
||||||
this.renderChart();
|
|
||||||
});
|
|
||||||
window.addEventListener('resize', this.resize, { passive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(preProps) {
|
|
||||||
const { data } = this.props;
|
|
||||||
if (JSON.stringify(preProps.data) !== JSON.stringify(data)) {
|
|
||||||
this.renderChart(this.props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.isUnmount = true;
|
|
||||||
window.cancelAnimationFrame(this.requestRef);
|
|
||||||
window.removeEventListener('resize', this.resize);
|
|
||||||
}
|
|
||||||
|
|
||||||
resize = () => {
|
|
||||||
this.requestRef = requestAnimationFrame(() => {
|
|
||||||
this.renderChart();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
saveRootRef = node => {
|
|
||||||
this.root = node;
|
|
||||||
};
|
|
||||||
|
|
||||||
initTagCloud = () => {
|
|
||||||
function getTextAttrs(cfg) {
|
|
||||||
return Object.assign(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
fillOpacity: cfg.opacity,
|
|
||||||
fontSize: cfg.origin._origin.size,
|
|
||||||
rotate: cfg.origin._origin.rotate,
|
|
||||||
text: cfg.origin._origin.text,
|
|
||||||
textAlign: 'center',
|
|
||||||
fontFamily: cfg.origin._origin.font,
|
|
||||||
fill: cfg.color,
|
|
||||||
textBaseline: 'Alphabetic',
|
|
||||||
},
|
|
||||||
cfg.style
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 给point注册一个词云的shape
|
|
||||||
Shape.registerShape('point', 'cloud', {
|
|
||||||
drawShape(cfg, container) {
|
|
||||||
const attrs = getTextAttrs(cfg);
|
|
||||||
return container.addShape('text', {
|
|
||||||
attrs: Object.assign(attrs, {
|
|
||||||
x: cfg.x,
|
|
||||||
y: cfg.y,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
@Bind()
|
|
||||||
@Debounce(500)
|
|
||||||
renderChart(nextProps) {
|
|
||||||
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
|
|
||||||
const { data, height } = nextProps || this.props;
|
|
||||||
|
|
||||||
if (data.length < 1 || !this.root) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const h = height;
|
|
||||||
const w = this.root.offsetWidth;
|
|
||||||
|
|
||||||
const onload = () => {
|
|
||||||
const dv = new DataSet.View().source(data);
|
|
||||||
const range = dv.range('value');
|
|
||||||
const [min, max] = range;
|
|
||||||
dv.transform({
|
|
||||||
type: 'tag-cloud',
|
|
||||||
fields: ['name', 'value'],
|
|
||||||
imageMask: this.imageMask,
|
|
||||||
font: 'Verdana',
|
|
||||||
size: [w, h], // 宽高设置最好根据 imageMask 做调整
|
|
||||||
padding: 0,
|
|
||||||
timeInterval: 5000, // max execute time
|
|
||||||
rotate() {
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
fontSize(d) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
return Math.pow((d.value - min) / (max - min), 2) * (17.5 - 5) + 5;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.isUnmount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
dv,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!this.imageMask) {
|
|
||||||
this.imageMask = new Image();
|
|
||||||
this.imageMask.crossOrigin = '';
|
|
||||||
this.imageMask.src = imgUrl;
|
|
||||||
|
|
||||||
this.imageMask.onload = onload;
|
|
||||||
} else {
|
|
||||||
onload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { className, height } = this.props;
|
|
||||||
const { dv, w, h } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(styles.tagCloud, className)}
|
|
||||||
style={{ width: '100%', height }}
|
|
||||||
ref={this.saveRootRef}
|
|
||||||
>
|
|
||||||
{dv && (
|
|
||||||
<Chart
|
|
||||||
width={w}
|
|
||||||
height={h}
|
|
||||||
data={dv}
|
|
||||||
padding={0}
|
|
||||||
scale={{
|
|
||||||
x: { nice: false },
|
|
||||||
y: { nice: false },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Tooltip showTitle={false} />
|
|
||||||
<Coord reflect="y" />
|
|
||||||
<Geom
|
|
||||||
type="point"
|
|
||||||
position="x*y"
|
|
||||||
color="text"
|
|
||||||
shape="cloud"
|
|
||||||
tooltip={[
|
|
||||||
'text*value',
|
|
||||||
function trans(text, value) {
|
|
||||||
return { name: text, value };
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Chart>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TagCloud;
|
|
@ -1,6 +0,0 @@
|
|||||||
.tagCloud {
|
|
||||||
overflow: hidden;
|
|
||||||
canvas {
|
|
||||||
transform-origin: 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
14
src/components/Charts/TimelineChart/index.d.ts
vendored
14
src/components/Charts/TimelineChart/index.d.ts
vendored
@ -1,14 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface ITimelineChartProps {
|
|
||||||
data: Array<{
|
|
||||||
x: number;
|
|
||||||
y1: number;
|
|
||||||
y2?: number;
|
|
||||||
}>;
|
|
||||||
titleMap: { y1: string; y2?: string };
|
|
||||||
padding?: [number, number, number, number];
|
|
||||||
height?: number;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class TimelineChart extends React.Component<ITimelineChartProps, any> {}
|
|
@ -1,120 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Chart, Tooltip, Geom, Legend, Axis } from 'bizcharts';
|
|
||||||
import DataSet from '@antv/data-set';
|
|
||||||
import Slider from 'bizcharts-plugin-slider';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
@autoHeight()
|
|
||||||
class TimelineChart extends React.Component {
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
height = 400,
|
|
||||||
padding = [60, 20, 40, 40],
|
|
||||||
titleMap = {
|
|
||||||
y1: 'y1',
|
|
||||||
y2: 'y2',
|
|
||||||
},
|
|
||||||
borderWidth = 2,
|
|
||||||
data: sourceData,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const data = Array.isArray(sourceData) ? sourceData : [{ x: 0, y1: 0, y2: 0 }];
|
|
||||||
|
|
||||||
data.sort((a, b) => a.x - b.x);
|
|
||||||
|
|
||||||
let max;
|
|
||||||
if (data[0] && data[0].y1 && data[0].y2) {
|
|
||||||
max = Math.max(
|
|
||||||
[...data].sort((a, b) => b.y1 - a.y1)[0].y1,
|
|
||||||
[...data].sort((a, b) => b.y2 - a.y2)[0].y2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ds = new DataSet({
|
|
||||||
state: {
|
|
||||||
start: data[0].x,
|
|
||||||
end: data[data.length - 1].x,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const dv = ds.createView();
|
|
||||||
dv.source(data)
|
|
||||||
.transform({
|
|
||||||
type: 'filter',
|
|
||||||
callback: obj => {
|
|
||||||
const date = obj.x;
|
|
||||||
return date <= ds.state.end && date >= ds.state.start;
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.transform({
|
|
||||||
type: 'map',
|
|
||||||
callback(row) {
|
|
||||||
const newRow = { ...row };
|
|
||||||
newRow[titleMap.y1] = row.y1;
|
|
||||||
newRow[titleMap.y2] = row.y2;
|
|
||||||
return newRow;
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.transform({
|
|
||||||
type: 'fold',
|
|
||||||
fields: [titleMap.y1, titleMap.y2], // 展开字段集
|
|
||||||
key: 'key', // key字段
|
|
||||||
value: 'value', // value字段
|
|
||||||
});
|
|
||||||
|
|
||||||
const timeScale = {
|
|
||||||
type: 'time',
|
|
||||||
tickInterval: 60 * 60 * 1000,
|
|
||||||
mask: 'HH:mm',
|
|
||||||
range: [0, 1],
|
|
||||||
};
|
|
||||||
|
|
||||||
const cols = {
|
|
||||||
x: timeScale,
|
|
||||||
value: {
|
|
||||||
max,
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const SliderGen = () => (
|
|
||||||
<Slider
|
|
||||||
padding={[0, padding[1] + 20, 0, padding[3]]}
|
|
||||||
width="auto"
|
|
||||||
height={26}
|
|
||||||
xAxis="x"
|
|
||||||
yAxis="y1"
|
|
||||||
scales={{ x: timeScale }}
|
|
||||||
data={data}
|
|
||||||
start={ds.state.start}
|
|
||||||
end={ds.state.end}
|
|
||||||
backgroundChart={{ type: 'line' }}
|
|
||||||
onChange={({ startValue, endValue }) => {
|
|
||||||
ds.setState('start', startValue);
|
|
||||||
ds.setState('end', endValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.timelineChart} style={{ height: height + 30 }}>
|
|
||||||
<div>
|
|
||||||
{title && <h4>{title}</h4>}
|
|
||||||
<Chart height={height} padding={padding} data={dv} scale={cols} forceFit>
|
|
||||||
<Axis name="x" />
|
|
||||||
<Tooltip />
|
|
||||||
<Legend name="key" position="top" />
|
|
||||||
<Geom type="line" position="x*value" size={borderWidth} color="key" />
|
|
||||||
</Chart>
|
|
||||||
<div style={{ marginRight: -20 }}>
|
|
||||||
<SliderGen />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TimelineChart;
|
|
@ -1,3 +0,0 @@
|
|||||||
.timelineChart {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
10
src/components/Charts/WaterWave/index.d.ts
vendored
10
src/components/Charts/WaterWave/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
export interface IWaterWaveProps {
|
|
||||||
title: React.ReactNode;
|
|
||||||
color?: string;
|
|
||||||
height: number;
|
|
||||||
percent: number;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class WaterWave extends React.Component<IWaterWaveProps, any> {}
|
|
@ -1,213 +0,0 @@
|
|||||||
import React, { PureComponent } from 'react';
|
|
||||||
import autoHeight from '../autoHeight';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
/* eslint no-return-assign: 0 */
|
|
||||||
/* eslint no-mixed-operators: 0 */
|
|
||||||
// riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90
|
|
||||||
|
|
||||||
@autoHeight()
|
|
||||||
class WaterWave extends PureComponent {
|
|
||||||
state = {
|
|
||||||
radio: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.renderChart();
|
|
||||||
this.resize();
|
|
||||||
window.addEventListener(
|
|
||||||
'resize',
|
|
||||||
() => {
|
|
||||||
requestAnimationFrame(() => this.resize());
|
|
||||||
},
|
|
||||||
{ passive: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(props) {
|
|
||||||
const { percent } = this.props;
|
|
||||||
if (props.percent !== percent) {
|
|
||||||
// 不加这个会造成绘制缓慢
|
|
||||||
this.renderChart('update');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
cancelAnimationFrame(this.timer);
|
|
||||||
if (this.node) {
|
|
||||||
this.node.innerHTML = '';
|
|
||||||
}
|
|
||||||
window.removeEventListener('resize', this.resize);
|
|
||||||
}
|
|
||||||
|
|
||||||
resize = () => {
|
|
||||||
if (this.root) {
|
|
||||||
const { height } = this.props;
|
|
||||||
const { offsetWidth } = this.root.parentNode;
|
|
||||||
this.setState({
|
|
||||||
radio: offsetWidth < height ? offsetWidth / height : 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderChart(type) {
|
|
||||||
const { percent, color = '#1890FF' } = this.props;
|
|
||||||
const data = percent / 100;
|
|
||||||
const self = this;
|
|
||||||
cancelAnimationFrame(this.timer);
|
|
||||||
|
|
||||||
if (!this.node || (data !== 0 && !data)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvas = this.node;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
const canvasWidth = canvas.width;
|
|
||||||
const canvasHeight = canvas.height;
|
|
||||||
const radius = canvasWidth / 2;
|
|
||||||
const lineWidth = 2;
|
|
||||||
const cR = radius - lineWidth;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.lineWidth = lineWidth * 2;
|
|
||||||
|
|
||||||
const axisLength = canvasWidth - lineWidth;
|
|
||||||
const unit = axisLength / 8;
|
|
||||||
const range = 0.2; // 振幅
|
|
||||||
let currRange = range;
|
|
||||||
const xOffset = lineWidth;
|
|
||||||
let sp = 0; // 周期偏移量
|
|
||||||
let currData = 0;
|
|
||||||
const waveupsp = 0.005; // 水波上涨速度
|
|
||||||
|
|
||||||
let arcStack = [];
|
|
||||||
const bR = radius - lineWidth;
|
|
||||||
const circleOffset = -(Math.PI / 2);
|
|
||||||
let circleLock = true;
|
|
||||||
|
|
||||||
for (let i = circleOffset; i < circleOffset + 2 * Math.PI; i += 1 / (8 * Math.PI)) {
|
|
||||||
arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cStartPoint = arcStack.shift();
|
|
||||||
ctx.strokeStyle = color;
|
|
||||||
ctx.moveTo(cStartPoint[0], cStartPoint[1]);
|
|
||||||
|
|
||||||
function drawSin() {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.save();
|
|
||||||
|
|
||||||
const sinStack = [];
|
|
||||||
for (let i = xOffset; i <= xOffset + axisLength; i += 20 / axisLength) {
|
|
||||||
const x = sp + (xOffset + i) / unit;
|
|
||||||
const y = Math.sin(x) * currRange;
|
|
||||||
const dx = i;
|
|
||||||
const dy = 2 * cR * (1 - currData) + (radius - cR) - unit * y;
|
|
||||||
|
|
||||||
ctx.lineTo(dx, dy);
|
|
||||||
sinStack.push([dx, dy]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const startPoint = sinStack.shift();
|
|
||||||
|
|
||||||
ctx.lineTo(xOffset + axisLength, canvasHeight);
|
|
||||||
ctx.lineTo(xOffset, canvasHeight);
|
|
||||||
ctx.lineTo(startPoint[0], startPoint[1]);
|
|
||||||
|
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
|
|
||||||
gradient.addColorStop(0, '#ffffff');
|
|
||||||
gradient.addColorStop(1, color);
|
|
||||||
ctx.fillStyle = gradient;
|
|
||||||
ctx.fill();
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
function render() {
|
|
||||||
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
||||||
if (circleLock && type !== 'update') {
|
|
||||||
if (arcStack.length) {
|
|
||||||
const temp = arcStack.shift();
|
|
||||||
ctx.lineTo(temp[0], temp[1]);
|
|
||||||
ctx.stroke();
|
|
||||||
} else {
|
|
||||||
circleLock = false;
|
|
||||||
ctx.lineTo(cStartPoint[0], cStartPoint[1]);
|
|
||||||
ctx.stroke();
|
|
||||||
arcStack = null;
|
|
||||||
|
|
||||||
ctx.globalCompositeOperation = 'destination-over';
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
ctx.arc(radius, radius, bR, 0, 2 * Math.PI, 1);
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.save();
|
|
||||||
ctx.arc(radius, radius, radius - 3 * lineWidth, 0, 2 * Math.PI, 1);
|
|
||||||
|
|
||||||
ctx.restore();
|
|
||||||
ctx.clip();
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (data >= 0.85) {
|
|
||||||
if (currRange > range / 4) {
|
|
||||||
const t = range * 0.01;
|
|
||||||
currRange -= t;
|
|
||||||
}
|
|
||||||
} else if (data <= 0.1) {
|
|
||||||
if (currRange < range * 1.5) {
|
|
||||||
const t = range * 0.01;
|
|
||||||
currRange += t;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currRange <= range) {
|
|
||||||
const t = range * 0.01;
|
|
||||||
currRange += t;
|
|
||||||
}
|
|
||||||
if (currRange >= range) {
|
|
||||||
const t = range * 0.01;
|
|
||||||
currRange -= t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data - currData > 0) {
|
|
||||||
currData += waveupsp;
|
|
||||||
}
|
|
||||||
if (data - currData < 0) {
|
|
||||||
currData -= waveupsp;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp += 0.07;
|
|
||||||
drawSin();
|
|
||||||
}
|
|
||||||
self.timer = requestAnimationFrame(render);
|
|
||||||
}
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { radio } = this.state;
|
|
||||||
const { percent, title, height } = this.props;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={styles.waterWave}
|
|
||||||
ref={n => (this.root = n)}
|
|
||||||
style={{ transform: `scale(${radio})` }}
|
|
||||||
>
|
|
||||||
<div style={{ width: height, height, overflow: 'hidden' }}>
|
|
||||||
<canvas
|
|
||||||
className={styles.waterWaveCanvasWrapper}
|
|
||||||
ref={n => (this.node = n)}
|
|
||||||
width={height * 2}
|
|
||||||
height={height * 2}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.text} style={{ width: height }}>
|
|
||||||
{title && <span>{title}</span>}
|
|
||||||
<h4>{percent}%</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WaterWave;
|
|
@ -1,28 +0,0 @@
|
|||||||
@import '~antd/lib/style/themes/default.less';
|
|
||||||
|
|
||||||
.waterWave {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
transform-origin: left;
|
|
||||||
.text {
|
|
||||||
position: absolute;
|
|
||||||
top: 32px;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
span {
|
|
||||||
color: @text-color-secondary;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
color: @heading-color;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.waterWaveCanvasWrapper {
|
|
||||||
transform: scale(0.5);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/* eslint eqeqeq: 0 */
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function computeHeight(node) {
|
|
||||||
const totalHeight = parseInt(getComputedStyle(node).height, 10);
|
|
||||||
const padding =
|
|
||||||
parseInt(getComputedStyle(node).paddingTop, 10) +
|
|
||||||
parseInt(getComputedStyle(node).paddingBottom, 10);
|
|
||||||
return totalHeight - padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAutoHeight(n) {
|
|
||||||
if (!n) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = n;
|
|
||||||
|
|
||||||
let height = computeHeight(node);
|
|
||||||
|
|
||||||
while (!height) {
|
|
||||||
node = node.parentNode;
|
|
||||||
if (node) {
|
|
||||||
height = computeHeight(node);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
const autoHeight = () => WrappedComponent =>
|
|
||||||
class extends React.Component {
|
|
||||||
state = {
|
|
||||||
computedHeight: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { height } = this.props;
|
|
||||||
if (!height) {
|
|
||||||
const h = getAutoHeight(this.root);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
this.setState({ computedHeight: h });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRoot = node => {
|
|
||||||
this.root = node;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { height } = this.props;
|
|
||||||
const { computedHeight } = this.state;
|
|
||||||
const h = height || computedHeight;
|
|
||||||
return (
|
|
||||||
<div ref={this.handleRoot}>{h > 0 && <WrappedComponent {...this.props} height={h} />}</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default autoHeight;
|
|
3
src/components/Charts/bizcharts.d.ts
vendored
3
src/components/Charts/bizcharts.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
import * as BizChart from 'bizcharts';
|
|
||||||
|
|
||||||
export = BizChart;
|
|
@ -1,3 +0,0 @@
|
|||||||
import * as BizChart from 'bizcharts';
|
|
||||||
|
|
||||||
export default BizChart;
|
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
order: 4
|
|
||||||
title: 柱状图
|
|
||||||
---
|
|
||||||
|
|
||||||
通过设置 `x`,`y` 属性,可以快速的构建出一个漂亮的柱状图,各种纬度的关系则是通过自定义的数据展现。
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { Bar } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
const salesData = [];
|
|
||||||
for (let i = 0; i < 12; i += 1) {
|
|
||||||
salesData.push({
|
|
||||||
x: `${i + 1}月`,
|
|
||||||
y: Math.floor(Math.random() * 1000) + 200,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Bar
|
|
||||||
height={200}
|
|
||||||
title="销售额趋势"
|
|
||||||
data={salesData}
|
|
||||||
/>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,95 +0,0 @@
|
|||||||
---
|
|
||||||
order: 1
|
|
||||||
title: 图表卡片
|
|
||||||
---
|
|
||||||
|
|
||||||
用于展示图表的卡片容器,可以方便的配合其它图表套件展示丰富信息。
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { ChartCard, yuan, Field } from 'ant-design-pro/lib/Charts';
|
|
||||||
import Trend from 'ant-design-pro/lib/Trend';
|
|
||||||
import { Row, Col, Icon, Tooltip } from 'antd';
|
|
||||||
import numeral from 'numeral';
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Row>
|
|
||||||
<Col span={24}>
|
|
||||||
<ChartCard
|
|
||||||
title="销售额"
|
|
||||||
action={
|
|
||||||
<Tooltip title="指标说明">
|
|
||||||
<Icon type="info-circle-o" />
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
total={() => (
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: yuan(126560) }} />
|
|
||||||
)}
|
|
||||||
footer={
|
|
||||||
<Field label="日均销售额" value={numeral(12423).format("0,0")} />
|
|
||||||
}
|
|
||||||
contentHeight={46}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
周同比
|
|
||||||
<Trend flag="up" style={{ marginLeft: 8, color: "rgba(0,0,0,.85)" }}>
|
|
||||||
12%
|
|
||||||
</Trend>
|
|
||||||
</span>
|
|
||||||
<span style={{ marginLeft: 16 }}>
|
|
||||||
日环比
|
|
||||||
<Trend
|
|
||||||
flag="down"
|
|
||||||
style={{ marginLeft: 8, color: "rgba(0,0,0,.85)" }}
|
|
||||||
>
|
|
||||||
11%
|
|
||||||
</Trend>
|
|
||||||
</span>
|
|
||||||
</ChartCard>
|
|
||||||
</Col>
|
|
||||||
<Col span={24} style={{ marginTop: 24 }}>
|
|
||||||
<ChartCard
|
|
||||||
title="移动指标"
|
|
||||||
avatar={
|
|
||||||
<img
|
|
||||||
style={{ width: 56, height: 56 }}
|
|
||||||
src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png"
|
|
||||||
alt="indicator"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
action={
|
|
||||||
<Tooltip title="指标说明">
|
|
||||||
<Icon type="info-circle-o" />
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
total={() => (
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: yuan(126560) }} />
|
|
||||||
)}
|
|
||||||
footer={
|
|
||||||
<Field label="日均销售额" value={numeral(12423).format("0,0")} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={24} style={{ marginTop: 24 }}>
|
|
||||||
<ChartCard
|
|
||||||
title="移动指标"
|
|
||||||
avatar={
|
|
||||||
<img
|
|
||||||
alt="indicator"
|
|
||||||
style={{ width: 56, height: 56 }}
|
|
||||||
src="https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
action={
|
|
||||||
<Tooltip title="指标说明">
|
|
||||||
<Icon type="info-circle-o" />
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
total={() => (
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: yuan(126560) }} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>,
|
|
||||||
mountNode,
|
|
||||||
);
|
|
||||||
```
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
order: 7
|
|
||||||
title: 仪表盘
|
|
||||||
---
|
|
||||||
|
|
||||||
仪表盘是一种进度展示方式,可以更直观的展示当前的进展情况,通常也可表示占比。
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { Gauge } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Gauge
|
|
||||||
title="核销率"
|
|
||||||
height={164}
|
|
||||||
percent={87}
|
|
||||||
/>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
order: 2
|
|
||||||
col: 2
|
|
||||||
title: 迷你区域图
|
|
||||||
---
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { MiniArea } from 'ant-design-pro/lib/Charts';
|
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
const visitData = [];
|
|
||||||
const beginDay = new Date().getTime();
|
|
||||||
for (let i = 0; i < 20; i += 1) {
|
|
||||||
visitData.push({
|
|
||||||
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
|
|
||||||
y: Math.floor(Math.random() * 100) + 10,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<MiniArea
|
|
||||||
line
|
|
||||||
color="#cceafe"
|
|
||||||
height={45}
|
|
||||||
data={visitData}
|
|
||||||
/>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
order: 2
|
|
||||||
col: 2
|
|
||||||
title: 迷你柱状图
|
|
||||||
---
|
|
||||||
|
|
||||||
迷你柱状图更适合展示简单的区间数据,简洁的表现方式可以很好的减少大数据量的视觉展现压力。
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { MiniBar } from 'ant-design-pro/lib/Charts';
|
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
const visitData = [];
|
|
||||||
const beginDay = new Date().getTime();
|
|
||||||
for (let i = 0; i < 20; i += 1) {
|
|
||||||
visitData.push({
|
|
||||||
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
|
|
||||||
y: Math.floor(Math.random() * 100) + 10,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<MiniBar
|
|
||||||
height={45}
|
|
||||||
data={visitData}
|
|
||||||
/>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
order: 6
|
|
||||||
title: 迷你饼状图
|
|
||||||
---
|
|
||||||
|
|
||||||
通过简化 `Pie` 属性的设置,可以快速的实现极简的饼状图,可配合 `ChartCard` 组合展
|
|
||||||
现更多业务场景。
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { Pie } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Pie percent={28} subTitle="中式快餐" total="28%" height={140} />,
|
|
||||||
mountNode
|
|
||||||
);
|
|
||||||
```
|
|
@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
order: 3
|
|
||||||
title: 迷你进度条
|
|
||||||
---
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { MiniProgress } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<MiniProgress percent={78} strokeWidth={8} target={80} />
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,84 +0,0 @@
|
|||||||
---
|
|
||||||
order: 0
|
|
||||||
title: 图表套件组合展示
|
|
||||||
---
|
|
||||||
|
|
||||||
利用 Ant Design Pro 提供的图表套件,可以灵活组合符合设计规范的图表来满足复杂的业务需求。
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { ChartCard, Field, MiniArea, MiniBar, MiniProgress } from 'ant-design-pro/lib/Charts';
|
|
||||||
import Trend from 'ant-design-pro/lib/Trend';
|
|
||||||
import NumberInfo from 'ant-design-pro/lib/NumberInfo';
|
|
||||||
import { Row, Col, Icon, Tooltip } from 'antd';
|
|
||||||
import numeral from 'numeral';
|
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
const visitData = [];
|
|
||||||
const beginDay = new Date().getTime();
|
|
||||||
for (let i = 0; i < 20; i += 1) {
|
|
||||||
visitData.push({
|
|
||||||
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
|
|
||||||
y: Math.floor(Math.random() * 100) + 10,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Row>
|
|
||||||
<Col span={24}>
|
|
||||||
<ChartCard
|
|
||||||
title="搜索用户数量"
|
|
||||||
total={numeral(8846).format('0,0')}
|
|
||||||
contentHeight={134}
|
|
||||||
>
|
|
||||||
<NumberInfo
|
|
||||||
subTitle={<span>本周访问</span>}
|
|
||||||
total={numeral(12321).format('0,0')}
|
|
||||||
status="up"
|
|
||||||
subTotal={17.1}
|
|
||||||
/>
|
|
||||||
<MiniArea
|
|
||||||
line
|
|
||||||
height={45}
|
|
||||||
data={visitData}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
</Col>
|
|
||||||
<Col span={24} style={{ marginTop: 24 }}>
|
|
||||||
<ChartCard
|
|
||||||
title="访问量"
|
|
||||||
action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
|
|
||||||
total={numeral(8846).format('0,0')}
|
|
||||||
footer={<Field label="日访问量" value={numeral(1234).format('0,0')} />}
|
|
||||||
contentHeight={46}
|
|
||||||
>
|
|
||||||
<MiniBar
|
|
||||||
height={46}
|
|
||||||
data={visitData}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
</Col>
|
|
||||||
<Col span={24} style={{ marginTop: 24 }}>
|
|
||||||
<ChartCard
|
|
||||||
title="线上购物转化率"
|
|
||||||
action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
|
|
||||||
total="78%"
|
|
||||||
footer={
|
|
||||||
<div>
|
|
||||||
<span>
|
|
||||||
周同比
|
|
||||||
<Trend flag="up" style={{ marginLeft: 8, color: 'rgba(0,0,0,.85)' }}>12%</Trend>
|
|
||||||
</span>
|
|
||||||
<span style={{ marginLeft: 16 }}>
|
|
||||||
日环比
|
|
||||||
<Trend flag="down" style={{ marginLeft: 8, color: 'rgba(0,0,0,.85)' }}>11%</Trend>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
contentHeight={46}
|
|
||||||
>
|
|
||||||
<MiniProgress percent={78} strokeWidth={8} target={80} />
|
|
||||||
</ChartCard>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,54 +0,0 @@
|
|||||||
---
|
|
||||||
order: 5
|
|
||||||
title: 饼状图
|
|
||||||
---
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { Pie, yuan } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
const salesPieData = [
|
|
||||||
{
|
|
||||||
x: '家用电器',
|
|
||||||
y: 4544,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: '食用酒水',
|
|
||||||
y: 3321,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: '个护健康',
|
|
||||||
y: 3113,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: '服饰箱包',
|
|
||||||
y: 2341,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: '母婴产品',
|
|
||||||
y: 1231,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: '其他',
|
|
||||||
y: 1231,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Pie
|
|
||||||
hasLegend
|
|
||||||
title="销售额"
|
|
||||||
subTitle="销售额"
|
|
||||||
total={() => (
|
|
||||||
<span
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: yuan(salesPieData.reduce((pre, now) => now.y + pre, 0))
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
data={salesPieData}
|
|
||||||
valueFormat={val => <span dangerouslySetInnerHTML={{ __html: yuan(val) }} />}
|
|
||||||
height={294}
|
|
||||||
/>,
|
|
||||||
mountNode,
|
|
||||||
);
|
|
||||||
```
|
|
@ -1,64 +0,0 @@
|
|||||||
---
|
|
||||||
order: 7
|
|
||||||
title: 雷达图
|
|
||||||
---
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { Radar, ChartCard } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
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],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<ChartCard title="数据比例">
|
|
||||||
<Radar
|
|
||||||
hasLegend
|
|
||||||
height={286}
|
|
||||||
data={radarData}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
order: 9
|
|
||||||
title: 标签云
|
|
||||||
---
|
|
||||||
|
|
||||||
标签云是一套相关的标签以及与此相应的权重展示方式,一般典型的标签云有 30 至 150 个标签,而权重影响使用的字体大小或其他视觉效果。
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { TagCloud } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
const tags = [];
|
|
||||||
for (let i = 0; i < 50; i += 1) {
|
|
||||||
tags.push({
|
|
||||||
name: `TagClout-Title-${i}`,
|
|
||||||
value: Math.floor((Math.random() * 50)) + 20,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<TagCloud
|
|
||||||
data={tags}
|
|
||||||
height={200}
|
|
||||||
/>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
order: 9
|
|
||||||
title: 带有时间轴的图表
|
|
||||||
---
|
|
||||||
|
|
||||||
使用 `TimelineChart` 组件可以实现带有时间轴的柱状图展现,而其中的 `x` 属性,则是时间值的指向,默认最多支持同时展现两个指标,分别是 `y1` 和 `y2`。
|
|
||||||
|
|
||||||
````jsx
|
|
||||||
import { TimelineChart } from 'ant-design-pro/lib/Charts';
|
|
||||||
|
|
||||||
const chartData = [];
|
|
||||||
for (let i = 0; i < 20; i += 1) {
|
|
||||||
chartData.push({
|
|
||||||
x: (new Date().getTime()) + (1000 * 60 * 30 * i),
|
|
||||||
y1: Math.floor(Math.random() * 100) + 1000,
|
|
||||||
y2: Math.floor(Math.random() * 100) + 10,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<TimelineChart
|
|
||||||
height={200}
|
|
||||||
data={chartData}
|
|
||||||
titleMap={{ y1: '客流量', y2: '支付笔数' }}
|
|
||||||
/>
|
|
||||||
, mountNode);
|
|
||||||
````
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user