Unverified Commit 6ef0c23f authored by 陈帅's avatar 陈帅 Committed by GitHub

fix all eslint-error (#28)

* run eslint fix

* import sorted

* fix some warning

* fix all eslint-error

* fix all eslint-error

* fix all eslint error

* remove .eslintcache
parent 8ae5c0b9
/functions/mock/**
/scripts
/config
\ No newline at end of file
/config
**/node_modules/**
_scripts
\ No newline at end of file
const fabric = require('@umijs/fabric');
module.exports = {
parser: 'babel-eslint',
extends: ['airbnb', 'prettier', 'plugin:compat/recommended'],
env: {
browser: true,
node: true,
es6: true,
mocha: true,
jest: true,
jasmine: true,
...fabric.default,
rules: {
...fabric.default.rules,
'@typescript-eslint/camelcase': 0,
'@typescript-eslint/class-name-casing': 0,
},
globals: {
APP_TYPE: true,
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
page: true,
},
rules: {
'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
'react/jsx-wrap-multilines': 0,
'react/prop-types': 0,
'react/forbid-prop-types': 0,
'react/jsx-one-expression-per-line': 0,
'import/no-unresolved': [2, { ignore: ['^@/', '^umi/'] }],
'import/no-extraneous-dependencies': [
2,
{
optionalDependencies: true,
devDependencies: ['**/tests/**.js', '/mock/**.js', '**/**.test.js'],
},
],
'jsx-a11y/no-noninteractive-element-interactions': 0,
'jsx-a11y/click-events-have-key-events': 0,
'jsx-a11y/no-static-element-interactions': 0,
'jsx-a11y/anchor-is-valid': 0,
'linebreak-style': 0,
},
settings: {
polyfills: ['fetch', 'promises', 'url'],
},
};
......@@ -35,4 +35,6 @@ functions/mock
# screenshot
screenshot
.firebase
\ No newline at end of file
.firebase
.eslintcache
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "never",
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
]
}
\ No newline at end of file
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.prettier,
};
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.stylelint,
};
{
"extends": [
"stylelint-config-standard",
"stylelint-config-css-modules",
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
"rules": {
"no-descending-specificity": null,
"plugin/declaration-block-no-ignored-properties": true
}
}
......@@ -11,16 +11,21 @@
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountcenter"
},
"dependencies": {
"@ant-design/pro-layout": "^4.5.5",
"antd": "^3.16.3",
"classnames": "^2.2.6",
"dva": "^2.4.0",
"moment": "^2.24.0",
"numeral": "^2.0.6",
"react": "^16.6.3",
"react-router": "^5.0.1",
"redux": "^4.0.1",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.6.9",
"umi-plugin-react": "^1.7.2",
"umi-plugin-block-dev": "^1.0.0"
"umi-plugin-block-dev": "^1.0.0",
"umi-plugin-react": "^1.7.2"
},
"license": "ISC",
"blockConfig": {
......
import { ListItemDataType } from './data';
import { ListItemDataType } from './data.d';
const titles = [
'Alipay',
......@@ -21,69 +21,6 @@ const avatars = [
'https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png', // Webpack
];
const getNotice = [
{
id: 'xxx1',
title: titles[0],
logo: avatars[0],
description: '那是一种内在的东西,他们到达不了,也无法触及的',
updatedAt: new Date(),
member: '科学搬砖组',
href: '',
memberLink: '',
},
{
id: 'xxx2',
title: titles[1],
logo: avatars[1],
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
updatedAt: new Date('2017-07-24'),
member: '全组都是吴彦祖',
href: '',
memberLink: '',
},
{
id: 'xxx3',
title: titles[2],
logo: avatars[2],
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
updatedAt: new Date(),
member: '中二少女团',
href: '',
memberLink: '',
},
{
id: 'xxx4',
title: titles[3],
logo: avatars[3],
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
updatedAt: new Date('2017-07-23'),
member: '程序员日常',
href: '',
memberLink: '',
},
{
id: 'xxx5',
title: titles[4],
logo: avatars[4],
description: '凛冬将至',
updatedAt: new Date('2017-07-23'),
member: '高逼格设计天团',
href: '',
memberLink: '',
},
{
id: 'xxx6',
title: titles[5],
logo: avatars[5],
description: '生命就像一盒巧克力,结果往往出人意料',
updatedAt: new Date('2017-07-23'),
member: '骗你来学计算机',
href: '',
memberLink: '',
},
];
const covers = [
'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png',
'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png',
......@@ -119,7 +56,7 @@ function fakeList(count: number): ListItemDataType[] {
owner: user[i % 10],
title: titles[i % 8],
avatar: avatars[i % 8],
cover: parseInt(i / 4 + '', 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],
cover: parseInt(`${i / 4}`, 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],
status: ['active', 'exception', 'normal'][i % 3] as
| 'normal'
| 'exception'
......
import { Avatar, Card, Dropdown, Icon, List, Menu, Tooltip } from 'antd';
import React, { Component } from 'react';
import { List, Card, Icon, Dropdown, Menu, Avatar, Tooltip } from 'antd';
import numeral from 'numeral';
import { connect } from 'dva';
import stylesApplications from './index.less';
import numeral from 'numeral';
import { ModalState } from '../../model';
import stylesApplications from './index.less';
export function formatWan(val: number) {
const v = val * 1;
......
import { Avatar } from 'antd';
import React from 'react';
import moment from 'moment';
import { Avatar } from 'antd';
import styles from './index.less';
export interface ApplicationsProps {
......
import { Icon, List, Tag } from 'antd';
import React, { Component } from 'react';
import { List, Icon, Tag } from 'antd';
import { connect } from 'dva';
import ArticleListContent from '../ArticleListContent';
import styles from './index.less';
import { ListItemDataType } from '../../data.d';
import { ModalState } from '../../model';
import { ListItemDataType } from '../../data';
import styles from './index.less';
@connect(({ BLOCK_NAME_CAMEL_CASE }: { BLOCK_NAME_CAMEL_CASE: ModalState }) => ({
list: BLOCK_NAME_CAMEL_CASE.list,
......
import { Avatar, Tooltip } from 'antd';
import React from 'react';
import { Tooltip, Avatar } from 'antd';
import classNames from 'classnames';
import styles from './index.less';
......@@ -19,10 +20,10 @@ export interface AvatarListProps {
maxLength?: number;
excessItemsStyle?: React.CSSProperties;
style?: React.CSSProperties;
children: React.ReactElement<AvatarItemProps> | Array<React.ReactElement<AvatarItemProps>>;
children: React.ReactElement<AvatarItemProps> | React.ReactElement<AvatarItemProps>[];
}
const avatarSizeToClassName = (size?: SizeType) =>
const avatarSizeToClassName = (size?: SizeType | 'mini') =>
classNames(styles.avatarItem, {
[styles.avatarItemLarge]: size === 'large',
[styles.avatarItemSmall]: size === 'small',
......@@ -54,9 +55,7 @@ const AvatarList: React.SFC<AvatarListProps> & { Item: typeof Item } = ({
}) => {
const numOfChildren = React.Children.count(children);
const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength;
const childrenArray = React.Children.toArray(children) as Array<
React.ReactElement<AvatarItemProps>
>;
const childrenArray = React.Children.toArray(children) as React.ReactElement<AvatarItemProps>[];
const childrenWithProps = childrenArray.slice(0, numToShow).map(child =>
React.cloneElement(child, {
size,
......
import { Card, List } from 'antd';
import React, { Component } from 'react';
import { List, Card } from 'antd';
import moment from 'moment';
import { connect } from 'dva';
import moment from 'moment';
import AvatarList from '../AvatarList';
import styles from './index.less';
import { ListItemDataType } from '../../data.d';
import { ModalState } from '../../model';
import { ListItemDataType } from '../../data';
import styles from './index.less';
@connect(({ BLOCK_NAME_CAMEL_CASE }: { BLOCK_NAME_CAMEL_CASE: ModalState }) => ({
list: BLOCK_NAME_CAMEL_CASE.list,
......
export interface ITag {
export interface TagType {
key: string;
label: string;
}
export interface IProvince {
export interface ProvinceType {
label: string;
key: string;
}
export interface ICity {
export interface CityType {
label: string;
key: string;
}
export interface IGeographic {
province: IProvince;
city: ICity;
export interface GeographicType {
province: ProvinceType;
city: CityType;
}
export interface INotice {
export interface NoticeType {
id: string;
title: string;
logo: string;
......@@ -33,16 +33,16 @@ export interface CurrentUser {
name: string;
avatar: string;
userid: string;
notice: INotice[];
notice: NoticeType[];
email: string;
signature: string;
title: string;
group: string;
tags: ITag[];
tags: TagType[];
notifyCount: number;
unreadCount: number;
country: string;
geographic: IGeographic;
geographic: GeographicType;
address: string;
phone: string;
}
......
import { Avatar, Card, Col, Divider, Icon, Input, Row, Tag } from 'antd';
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import { Dispatch } from 'redux';
import Link from 'umi/link';
import { GridContent } from '@ant-design/pro-layout';
import Link from 'umi/link';
import { RouteChildrenProps } from 'react-router';
import { Card, Row, Col, Icon, Avatar, Tag, Divider, Input } from 'antd';
import styles from './Center.less';
import { ITag, CurrentUser } from './data';
import { connect } from 'dva';
import { ModalState } from './model';
import Projects from './components/Projects';
import Articles from './components/Articles';
import Applications from './components/Applications';
import Projects from './components/Projects';
import { CurrentUser, TagType } from './data.d';
import styles from './Center.less';
const operationTabList = [
{
......@@ -45,7 +46,7 @@ interface BLOCK_NAME_CAMEL_CASEProps extends RouteChildrenProps {
currentUserLoading: boolean;
}
interface BLOCK_NAME_CAMEL_CASEState {
newTags: ITag[];
newTags: TagType[];
tabKey: 'articles' | 'applications' | 'projects';
inputVisible: boolean;
inputValue: string;
......@@ -56,7 +57,7 @@ interface BLOCK_NAME_CAMEL_CASEState {
loading,
BLOCK_NAME_CAMEL_CASE,
}: {
loading: { effects: any };
loading: { effects: { [key: string]: boolean } };
BLOCK_NAME_CAMEL_CASE: ModalState;
}) => ({
currentUser: BLOCK_NAME_CAMEL_CASE.currentUser,
......@@ -92,7 +93,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
tabKey: 'articles',
};
input: Input | null | undefined;
public input: Input | null | undefined = undefined;
componentDidMount() {
const { dispatch } = this.props;
......@@ -138,6 +139,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
inputValue: '',
});
};
renderChildrenByTabKey = (tabKey: BLOCK_NAME_CAMEL_CASEState['tabKey']) => {
if (tabKey === 'projects') {
return <Projects />;
......@@ -150,6 +152,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
}
return null;
};
render() {
const { newTags, inputVisible, inputValue, tabKey } = this.state;
const { currentUser, currentUserLoading } = this.props;
......@@ -184,9 +187,9 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
<Divider dashed />
<div className={styles.tags}>
<div className={styles.tagsTitle}>标签</div>
{currentUser.tags.concat(newTags).map(item => {
return <Tag key={item.key}>{item.label}</Tag>;
})}
{currentUser.tags.concat(newTags).map(item => (
<Tag key={item.key}>{item.label}</Tag>
))}
{inputVisible && (
<Input
ref={ref => this.saveInputRef(ref)}
......
import { AnyAction, Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { CurrentUser, ListItemDataType } from './data.d';
import { queryCurrent, queryFakeList } from './service';
import { CurrentUser, ListItemDataType } from './data';
export interface ModalState {
currentUser: Partial<CurrentUser>;
list: ListItemDataType[];
}
import { Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { AnyAction } from 'redux';
export type Effect = (
action: AnyAction,
effects: EffectsCommandMap & { select: <T>(func: (state: ModalState) => T) => T },
......@@ -56,13 +54,13 @@ const Model: ModelType = {
reducers: {
saveCurrentUser(state, action) {
return {
...state!,
...(state as ModalState),
currentUser: action.payload || {},
};
},
queryList(state, action) {
return {
...state!,
...(state as ModalState),
list: action.payload,
};
},
......
......@@ -5,7 +5,7 @@ export async function queryCurrent() {
}
export async function queryFakeList(params: { count: number }) {
return request(`/api/fake_list`, {
return request('/api/fake_list', {
params,
});
}
......@@ -12,15 +12,18 @@
"dev": "umi dev"
},
"dependencies": {
"@ant-design/pro-layout": "^4.5.5",
"antd": "^3.16.3",
"dva": "^2.4.0",
"react": "^16.6.3",
"umi-request": "^1.0.0"
"redux": "^4.0.1",
"umi-request": "^1.0.0",
"umi-plugin-react": "^1.8.4",
"umi": "^2.6.9"
},
"devDependencies": {
"umi": "^2.6.9",
"umi-plugin-block-dev": "^1.0.0",
"umi-plugin-react": "^1.3.0-beta.1"
"umi-plugin-block-dev": "^1.0.0"
},
"blockConfig": {
"specVersion": "0.1"
......
import React, { Component } from 'react';
import { Select, Spin } from 'antd';
import { Dispatch } from 'redux';
import { connect } from 'dva';
import { CityType, ProvinceType } from '../data.d';
import styles from './GeographicView.less';
import { Dispatch } from 'redux';
import { ProvinceData, CityData } from '../data';
const { Option } = Select;
interface SelectItem {
label: string;
key: string;
}
const nullSlectItem: SelectItem = {
const nullSelectItem: SelectItem = {
label: '',
key: '',
};
interface GeographicViewProps {
dispatch?: Dispatch<any>;
province?: ProvinceData[];
city?: CityData[];
province?: ProvinceType[];
city?: CityType[];
value?: {
province: SelectItem;
city: SelectItem;
......@@ -33,8 +35,8 @@ interface GeographicViewProps {
loading,
}: {
BLOCK_NAME_CAMEL_CASE: {
province: ProvinceData[];
city: CityData[];
province: ProvinceType[];
city: CityType[];
};
loading: any;
}) => {
......@@ -85,7 +87,7 @@ class GeographicView extends Component<GeographicViewProps> {
return [];
};
getOption = (list: CityData[] | ProvinceData[]) => {
getOption = (list: CityType[] | ProvinceType[]) => {
if (!list || list.length < 1) {
return (
<Option key={0} value={0}>
......@@ -93,9 +95,9 @@ class GeographicView extends Component<GeographicViewProps> {
</Option>
);
}
return (list as CityData[]).map(item => (
<Option key={item.id} value={item.id}>
{item.name}
return (list as CityType[]).map(item => (
<Option key={item.key} value={item.key}>
{item.label}
</Option>
));
};
......@@ -112,7 +114,7 @@ class GeographicView extends Component<GeographicViewProps> {
if (onChange) {
onChange({
province: item,
city: nullSlectItem,
city: nullSelectItem,
});
}
};
......@@ -131,14 +133,14 @@ class GeographicView extends Component<GeographicViewProps> {
const { value } = this.props;
if (!value) {
return {
province: nullSlectItem,
city: nullSlectItem,
province: nullSelectItem,
city: nullSelectItem,
};
}
const { province, city } = value;
return {
province: province || nullSlectItem,
city: city || nullSlectItem,
province: province || nullSelectItem,
city: city || nullSelectItem,
};
}
......
import React, { Fragment, PureComponent } from 'react';
import { Input } from 'antd';
import styles from './PhoneView.less';
......@@ -20,15 +21,17 @@ class PhoneView extends PureComponent<PhoneViewProps> {
className={styles.area_code}
value={values[0]}
onChange={e => {
// tslint:disable-next-line: no-unused-expression
onChange && onChange(`${e.target.value}-${values[1]}`);
if (onChange) {
onChange(`${e.target.value}-${values[1]}`);
}
}}
/>
<Input
className={styles.phone_number}
onChange={e => {
// tslint:disable-next-line: no-unused-expression
onChange && onChange(`${values[0]}-${e.target.value}`);
if (onChange) {
onChange(`${values[0]}-${e.target.value}`);
}
}}
value={values[1]}
/>
......
import { Button, Form, Input, Select, Upload, message } from 'antd';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import React, { Component, Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Form, Input, Upload, Select, Button, message } from 'antd';
import { FormComponentProps } from 'antd/es/form';
import { connect } from 'dva';
import styles from './BaseView.less';
import { CurrentUser } from '../data.d';
import GeographicView from './GeographicView';
import PhoneView from './PhoneView';
import { CurrentUser } from '../data';
import styles from './BaseView.less';
const FormItem = Form.Item;
const { Option } = Select;
......@@ -70,7 +72,8 @@ interface BaseViewProps extends FormComponentProps {
currentUser: BLOCK_NAME_CAMEL_CASE.currentUser,
}))
class BaseView extends Component<BaseViewProps> {
view: HTMLDivElement | undefined;
view: HTMLDivElement | undefined = undefined;
componentDidMount() {
this.setBaseInfo();
}
......@@ -102,10 +105,10 @@ class BaseView extends Component<BaseViewProps> {
this.view = ref;
};
handlerSubmit = (event: Event) => {
handlerSubmit = (event: React.MouseEvent) => {
event.preventDefault();
const { form } = this.props;
form.validateFields((err, values) => {
form.validateFields(err => {
if (!err) {
message.success(formatMessage({ id: 'BLOCK_NAME.basic.update.success' }));
}
......
import React, { Component, Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import { Icon, List } from 'antd';
import React, { Component, Fragment } from 'react';
class BindingView extends Component {
getData = () => [
......
import { List, Switch } from 'antd';
import React, { Component, Fragment } from 'react';
import { formatMessage } from 'umi-plugin-react/locale';
import { Switch, List } from 'antd';
type Unpacked<T> = T extends (infer U)[] ? U : T;
......
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import React, { Component, Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { List } from 'antd';
type Unpacked<T> = T extends (infer U)[] ? U : T;
......
export interface ITag {
export interface TagType {
key: string;
label: string;
}
export interface IProvince {
export interface ProvinceType {
label: string;
key: string;
}
export interface ICity {
export interface CityType {
label: string;
key: string;
}
export interface IGeographic {
province: IProvince;
city: ICity;
export interface GeographicType {
province: ProvinceType;
city: CityType;
}
export interface INotice {
export interface NoticeType {
id: string;
title: string;
logo: string;
......@@ -33,16 +33,16 @@ export interface CurrentUser {
name: string;
avatar: string;
userid: string;
notice: INotice[];
notice: NoticeType[];
email: string;
signature: string;
title: string;
group: string;
tags: ITag[];
tags: TagType[];
notifyCount: number;
unreadCount: number;
country: string;
geographic: IGeographic;
geographic: GeographicType;
address: string;
phone: string;
}
import React, { Component } from 'react';
import { connect } from 'dva';
import { Dispatch } from 'redux';
import { FormattedMessage } from 'umi-plugin-react/locale';
import { GridContent } from '@ant-design/pro-layout';
import { Menu } from 'antd';
import styles from './style.less';
import { connect } from 'dva';
import BaseView from './components/base';
import SecurityView from './components/security';
import BindingView from './components/binding';
import { CurrentUser } from './data.d';
import NotificationView from './components/notification';
import { CurrentUser } from './data';
import SecurityView from './components/security';
import styles from './style.less';
const { Item } = Menu;
......@@ -33,7 +34,8 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component<
PAGE_NAME_UPPER_CAMEL_CASEProps,
PAGE_NAME_UPPER_CAMEL_CASEState
> {
main: HTMLDivElement | undefined;
main: HTMLDivElement | undefined = undefined;
constructor(props: PAGE_NAME_UPPER_CAMEL_CASEProps) {
super(props);
const menuMap = {
......
import { query as queryUsers, queryCurrent, queryProvince, queryCity } from './service';
import { Reducer } from 'redux';
import { AnyAction, Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { AnyAction } from 'redux';
import { CurrentUser, City, Province } from './data';
import { CityType, CurrentUser, ProvinceType } from './data.d';
import { queryCity, queryCurrent, queryProvince, query as queryUsers } from './service';
export interface ModalState {
currentUser?: Partial<CurrentUser>;
province?: Province[];
city?: City[];
province?: ProvinceType[];
city?: CityType[];
isLoading?: boolean;
}
......
......@@ -12,22 +12,27 @@
"dev": "umi dev"
},
"dependencies": {
"@ant-design/pro-layout": "^4.5.5",
"@antv/data-set": "^0.10.2",
"antd": "^3.16.3",
"bizcharts": "^3.5.3-beta.0",
"bizcharts-plugin-slider": "^2.1.1-beta.1",
"dva": "^2.4.0",
"classnames": "^2.2.6",
"dva": "^2.4.0",
"@types/lodash.debounce": "^4.0.6",
"lodash.debounce": "^4.0.8",
"moment": "^2.22.2",
"numeral": "^2.0.6",
"react": "^16.6.3",
"react-fittext": "^1.0.0",
"redux": "^4.0.1",
"umi-plugin-react": "^1.7.2",
"umi-request": "^1.0.0"
},
"devDependencies": {
"@types/numeral": "^0.0.25",
"umi": "^2.6.9",
"umi-plugin-block-dev": "^1.1.0",
"umi-plugin-react": "^1.7.2",
"@types/numeral": "^0.0.25"
"umi-plugin-block-dev": "^1.1.0"
},
"blockConfig": {
"specVersion": "0.1"
......
import moment from 'moment';
import { IVisitData, IRadarData, IAnalysisData } from './data';
import { AnalysisData, RadarData, VisitDataType } from './data.d';
// mock data
const visitData: IVisitData[] = [];
const visitData: VisitDataType[] = [];
const beginDay = new Date().getTime();
const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5];
......@@ -159,7 +159,7 @@ const radarOriginData = [
},
];
const radarData: IRadarData[] = [];
const radarData: RadarData[] = [];
const radarTitleMap = {
ref: '引用',
koubei: '口碑',
......@@ -179,7 +179,7 @@ radarOriginData.forEach(item => {
});
});
const getFakeChartData: IAnalysisData = {
const getFakeChartData: AnalysisData = {
visitData,
visitData2,
salesData,
......
import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
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 Debounce from 'lodash.debounce';
import autoHeight from '../autoHeight';
import styles from '../index.less';
export interface IBarProps {
export interface BarProps {
title: React.ReactNode;
color?: string;
padding?: [number, number, number, number];
height?: number;
data: Array<{
data: {
x: string;
y: number;
}>;
}[];
forceFit?: boolean;
autoLabel?: boolean;
style?: React.CSSProperties;
}
class Bar extends Component<
IBarProps,
BarProps,
{
autoHideXLabels: boolean;
}
......@@ -28,26 +28,12 @@ class Bar extends Component<
state = {
autoHideXLabels: false,
};
root: HTMLDivElement | undefined;
node: HTMLDivElement | undefined;
componentDidMount() {
window.addEventListener('resize', this.resize, { passive: true });
}
root: HTMLDivElement | undefined = undefined;
componentWillUnmount() {
window.removeEventListener('resize', this.resize);
}
handleRoot = (n: HTMLDivElement) => {
this.root = n;
};
handleRef = (n: HTMLDivElement) => {
this.node = n;
};
node: HTMLDivElement | undefined = undefined;
@Bind()
@Debounce(400)
resize() {
resize = Debounce(() => {
if (!this.node || !this.node.parentNode) {
return;
}
......@@ -70,8 +56,24 @@ class Bar extends Component<
autoHideXLabels: false,
});
}
}, 500);
componentDidMount() {
window.addEventListener('resize', this.resize, { passive: true });
}
componentWillUnmount() {
window.removeEventListener('resize', this.resize);
}
handleRoot = (n: HTMLDivElement) => {
this.root = n;
};
handleRef = (n: HTMLDivElement) => {
this.node = n;
};
render() {
const {
height = 1,
......@@ -115,8 +117,8 @@ class Bar extends Component<
<Axis
name="x"
title={false}
label={autoHideXLabels ? false : {}}
tickLine={autoHideXLabels ? false : {}}
label={autoHideXLabels ? undefined : {}}
tickLine={autoHideXLabels ? undefined : {}}
/>
<Axis name="y" min={0} />
<Tooltip showTitle={false} crosshairs={false} />
......
import React from 'react';
import { Card } from 'antd';
import classNames from 'classnames';
import { CardProps } from 'antd/es/card';
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
type totalType = () => React.ReactNode;
const renderTotal = (total?: number | totalType | React.ReactNode) => {
if (!total) {
return;
return null;
}
let totalDom;
switch (typeof total) {
......@@ -25,7 +24,7 @@ const renderTotal = (total?: number | totalType | React.ReactNode) => {
return totalDom;
};
export interface IChartCardProps extends CardProps {
export interface ChartCardProps extends CardProps {
title: React.ReactNode;
action?: React.ReactNode;
total?: React.ReactNode | number | (() => React.ReactNode | number);
......@@ -35,7 +34,7 @@ export interface IChartCardProps extends CardProps {
style?: React.CSSProperties;
}
class ChartCard extends React.Component<IChartCardProps> {
class ChartCard extends React.Component<ChartCardProps> {
renderContent = () => {
const { contentHeight, title, avatar, action, total, footer, children, loading } = this.props;
if (loading) {
......
import React from 'react';
import styles from './index.less';
export interface IFieldProps {
export interface FieldProps {
label: React.ReactNode;
value: React.ReactNode;
style?: React.CSSProperties;
}
const Field: React.SFC<IFieldProps> = ({ label, value, ...rest }) => (
const Field: React.SFC<FieldProps> = ({ label, value, ...rest }) => (
<div className={styles.field} {...rest}>
<span className={styles.label}>{label}</span>
<span className={styles.number}>{value}</span>
......
import { Axis, Chart, Coord, Geom, Guide, Shape } from 'bizcharts';
import React from 'react';
import { Chart, Geom, Axis, Coord, Guide, Shape } from 'bizcharts';
import autoHeight from '../autoHeight';
const { Arc, Html, Line } = Guide;
export interface IGaugeProps {
export interface GaugeProps {
title: React.ReactNode;
color?: string;
height?: number;
......@@ -30,148 +31,149 @@ const defaultFormatter = (val: string): string => {
}
};
Shape.registerShape!('point', 'pointer', {
drawShape(cfg: any, group: any) {
let point = cfg.points[0];
point = (this as any).parsePoint(point);
const center = (this as any).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',
},
});
},
});
if (Shape.registerShape) {
Shape.registerShape('point', 'pointer', {
drawShape(cfg: any, group: any) {
let point = cfg.points[0];
point = (this as any).parsePoint(point);
const center = (this as any).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',
},
});
},
});
}
class Gauge extends React.Component<IGaugeProps> {
render() {
const {
title,
height = 1,
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 renderHtml = () => `
const Gauge: React.FC<GaugeProps> = props => {
const {
title,
height = 1,
percent,
forceFit = true,
formatter = defaultFormatter,
color = '#2F9CFF',
bgColor = '#F0F2F5',
} = props;
const cols = {
value: {
type: 'linear',
min: 0,
max: 10,
tickCount: 6,
nice: true,
},
};
const data = [{ value: percent / 10 }];
const renderHtml = () => `
<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).toFixed(2)}%
</p>
</div>`;
const data = [{ value: percent / 10 }];
const textStyle: {
fontSize: number;
fill: string;
textAlign: 'center';
} = {
fontSize: 12,
fill: 'rgba(0, 0, 0, 0.65)',
textAlign: 'center',
};
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={undefined} />
<Axis
line={undefined}
tickLine={undefined}
subTickLine={undefined}
name="value"
zIndex={2}
label={{
offset: -12,
formatter,
textStyle: textStyle,
const textStyle: {
fontSize: number;
fill: string;
textAlign: 'center';
} = {
fontSize: 12,
fill: 'rgba(0, 0, 0, 0.65)',
textAlign: 'center',
};
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={undefined} />
<Axis
line={undefined}
tickLine={undefined}
subTickLine={undefined}
name="value"
zIndex={2}
label={{
offset: -12,
formatter,
textStyle,
}}
/>
<Guide>
<Line
start={[3, 0.905]}
end={[3, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 2,
}}
/>
<Guide>
<Line
start={[3, 0.905]}
end={[3, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 2,
}}
/>
<Line
start={[5, 0.905]}
end={[5, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
<Line
start={[7, 0.905]}
end={[7, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
<Arc
start={[0, 0.965]}
end={[10, 0.965]}
style={{
stroke: bgColor,
lineWidth: 10,
}}
/>
<Arc
start={[0, 0.965]}
end={[data[0].value, 0.965]}
style={{
stroke: color,
lineWidth: 10,
}}
/>
<Html position={['50%', '95%']} html={renderHtml()} />
</Guide>
<Geom
line={false}
type="point"
position="value*1"
shape="pointer"
color={color}
active={false}
<Line
start={[5, 0.905]}
end={[5, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
</Chart>
);
}
}
<Line
start={[7, 0.905]}
end={[7, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
<Arc
start={[0, 0.965]}
end={[10, 0.965]}
style={{
stroke: bgColor,
lineWidth: 10,
}}
/>
<Arc
start={[0, 0.965]}
end={[data[0].value, 0.965]}
style={{
stroke: color,
lineWidth: 10,
}}
/>
<Html position={['50%', '95%']} html={renderHtml()} />
</Guide>
<Geom
line={false}
type="point"
position="value*1"
shape="pointer"
color={color}
active={false}
/>
</Chart>
);
};
export default autoHeight()(Gauge);
import { Axis, Chart, Geom, Tooltip, AxisProps } from 'bizcharts';
import React from 'react';
import { Chart, Axis, Tooltip, Geom } from 'bizcharts';
import autoHeight from '../autoHeight';
import styles from '../index.less';
export interface IAxis {
title: any;
line: any;
gridAlign: any;
labels: any;
tickLine: any;
grid: any;
}
export interface IMiniAreaProps {
export interface MiniAreaProps {
color?: string;
height?: number;
borderColor?: string;
line?: boolean;
animate?: boolean;
xAxis?: IAxis;
xAxis?: AxisProps;
forceFit?: boolean;
scale?: { x: any; y: any };
yAxis?: IAxis;
scale?: {
x?: {
tickCount: number;
};
y?: {
tickCount: number;
};
};
yAxis?: Partial<AxisProps>;
borderWidth?: number;
data: Array<{
data: {
x: number | string;
y: number;
}>;
}[];
}
class MiniArea extends React.Component<IMiniAreaProps> {
render() {
const {
height = 1,
data = [],
forceFit = true,
color = 'rgba(24, 144, 255, 0.2)',
borderColor = '#1089ff',
scale = { x: {}, y: {} },
borderWidth = 2,
line,
xAxis,
yAxis,
animate = true,
} = this.props;
const MiniArea: React.FC<MiniAreaProps> = props => {
const {
height = 1,
data = [],
forceFit = true,
color = 'rgba(24, 144, 255, 0.2)',
borderColor = '#1089ff',
scale = { x: {}, y: {} },
borderWidth = 2,
line,
xAxis,
yAxis,
animate = true,
} = props;
const padding: [number, number, number, number] = [36, 5, 30, 5];
const padding: [number, number, number, number] = [36, 5, 30, 5];
const scaleProps = {
x: {
type: 'cat',
range: [0, 1],
...scale!.x,
},
y: {
min: 0,
...scale!.y,
},
};
const scaleProps = {
x: {
type: 'cat',
range: [0, 1],
...scale.x,
},
y: {
min: 0,
...scale.y,
},
};
const tooltip: [string, (...args: any[]) => { name?: string; value: string }] = [
'x*y',
(x: string, y: string) => ({
name: x,
value: y,
}),
];
const tooltip: [string, (...args: any[]) => { name?: string; value: string }] = [
'x*y',
(x: string, y: string) => ({
name: x,
value: y,
}),
];
const chartHeight = height + 54;
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} />
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={undefined}
line={undefined}
tickLine={undefined}
grid={undefined}
{...xAxis}
/>
<Axis
key="axis-y"
name="y"
label={undefined}
line={undefined}
tickLine={undefined}
grid={undefined}
{...yAxis}
/>
<Tooltip showTitle={false} crosshairs={false} />
<Geom
type="area"
position="x*y"
color={color}
tooltip={tooltip}
shape="smooth"
style={{
fillOpacity: 1,
}}
/>
{line ? (
<Geom
type="area"
type="line"
position="x*y"
color={color}
tooltip={tooltip}
shape="smooth"
style={{
fillOpacity: 1,
}}
color={borderColor}
size={borderWidth}
tooltip={false}
/>
{line ? (
<Geom
type="line"
position="x*y"
shape="smooth"
color={borderColor}
size={borderWidth}
tooltip={false}
/>
) : (
<span style={{ display: 'none' }} />
)}
</Chart>
)}
</div>
) : (
<span style={{ display: 'none' }} />
)}
</Chart>
)}
</div>
);
}
}
</div>
);
};
export default autoHeight()(MiniArea);
import { Chart, Geom, Tooltip } from 'bizcharts';
import React from 'react';
import { Chart, Tooltip, Geom } from 'bizcharts';
import autoHeight from '../autoHeight';
import styles from '../index.less';
export interface IMiniBarProps {
export interface MiniBarProps {
color?: string;
height?: number;
data: Array<{
data: {
x: number | string;
y: number;
}>;
}[];
forceFit?: boolean;
style?: React.CSSProperties;
}
class MiniBar extends React.Component<IMiniBarProps> {
render() {
const { height = 0, forceFit = true, color = '#1890FF', data = [] } = this.props;
const scale = {
x: {
type: 'cat',
},
y: {
min: 0,
},
};
const padding: [number, number, number, number] = [36, 5, 30, 5];
const tooltip: [string, (...args: any[]) => { name?: string; value: string }] = [
'x*y',
(x: string, y: string) => ({
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>
const MiniBar: React.FC<MiniBarProps> = props => {
const { height = 0, forceFit = true, color = '#1890FF', data = [] } = props;
const scale = {
x: {
type: 'cat',
},
y: {
min: 0,
},
};
const padding: [number, number, number, number] = [36, 5, 30, 5];
const tooltip: [string, (...args: any[]) => { name?: string; value: string }] = [
'x*y',
(x: string, y: string) => ({
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 autoHeight()(MiniBar);
......@@ -2,7 +2,7 @@ import React from 'react';
import { Tooltip } from 'antd';
import styles from './index.less';
export interface IMiniProgressProps {
export interface MiniProgressProps {
target: number;
targetLabel?: string;
color?: string;
......@@ -11,33 +11,31 @@ export interface IMiniProgressProps {
style?: React.CSSProperties;
}
const MiniProgress: React.SFC<IMiniProgressProps> = ({
const MiniProgress: React.SFC<MiniProgressProps> = ({
targetLabel,
target,
color = 'rgb(19, 194, 194)',
strokeWidth,
percent,
}) => {
return (
<div className={styles.miniProgress}>
<Tooltip title={targetLabel}>
<div className={styles.target} style={{ left: target ? `${target}%` : undefined }}>
<span style={{ backgroundColor: color || undefined }} />
<span style={{ backgroundColor: color || undefined }} />
</div>
</Tooltip>
<div className={styles.progressWrap}>
<div
className={styles.progress}
style={{
backgroundColor: color || undefined,
width: percent ? `${percent}%` : undefined,
height: strokeWidth || undefined,
}}
/>
}) => (
<div className={styles.miniProgress}>
<Tooltip title={targetLabel}>
<div className={styles.target} style={{ left: target ? `${target}%` : undefined }}>
<span style={{ backgroundColor: color || undefined }} />
<span style={{ backgroundColor: color || undefined }} />
</div>
</Tooltip>
<div className={styles.progressWrap}>
<div
className={styles.progress}
style={{
backgroundColor: color || undefined,
width: percent ? `${percent}%` : undefined,
height: strokeWidth || undefined,
}}
/>
</div>
);
};
</div>
);
export default MiniProgress;
import { Chart, Coord, Geom, Tooltip } from 'bizcharts';
import React, { Component } from 'react';
import { Chart, Tooltip, Geom, Coord } from 'bizcharts';
import { DataView } from '@antv/data-set';
import Debounce from 'lodash.debounce';
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 classNames from 'classnames';
import autoHeight from '../autoHeight';
import styles from './index.less';
export interface IPieProps {
export interface PieProps {
animate?: boolean;
color?: string;
colors?: string[];
......@@ -19,10 +19,10 @@ export interface IPieProps {
hasLegend?: boolean;
padding?: [number, number, number, number];
percent?: number;
data?: Array<{
data?: {
x: string | string;
y: number;
}>;
}[];
inner?: number;
lineWidth?: number;
forceFit?: boolean;
......@@ -34,19 +34,46 @@ export interface IPieProps {
valueFormat?: (value: string) => string | React.ReactNode;
subTitle?: React.ReactNode;
}
interface IPieState {
legendData: Array<{ checked: boolean; x: string; color: string; percent: number; y: string }>;
interface PieState {
legendData: { checked: boolean; x: string; color: string; percent: number; y: string }[];
legendBlock: boolean;
}
class Pie extends Component<IPieProps, IPieState> {
state: IPieState = {
class Pie extends Component<PieProps, PieState> {
state: PieState = {
legendData: [],
legendBlock: false,
};
requestRef: number | undefined;
root!: HTMLDivElement;
chart: G2.Chart | undefined;
requestRef: number | undefined = undefined;
root: HTMLDivElement | undefined = undefined;
chart: G2.Chart | undefined = undefined;
// for window resize auto responsive legend
resize = Debounce(() => {
const { hasLegend } = this.props;
const { legendBlock } = this.state;
if (!hasLegend || !this.root) {
window.removeEventListener('resize', this.resize);
return;
}
if (
this.root &&
this.root.parentNode &&
(this.root.parentNode as HTMLElement).clientWidth <= 380
) {
if (!legendBlock) {
this.setState({
legendBlock: true,
});
}
} else if (legendBlock) {
this.setState({
legendBlock: false,
});
}
}, 400);
componentDidMount() {
window.addEventListener(
......@@ -58,7 +85,7 @@ class Pie extends Component<IPieProps, IPieState> {
);
}
componentDidUpdate(preProps: IPieProps) {
componentDidUpdate(preProps: PieProps) {
const { data } = this.props;
if (data !== preProps.data) {
// because of charts data create when rendered
......@@ -90,7 +117,7 @@ class Pie extends Component<IPieProps, IPieState> {
if (!this.chart) return;
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return;
const items = geom.get('dataArray') || []; // 获取图形对应的
const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item: { color: any; _origin: any }[]) => {
/* eslint no-underscore-dangle:0 */
......@@ -104,6 +131,7 @@ class Pie extends Component<IPieProps, IPieState> {
legendData,
});
};
handleRoot = (n: HTMLDivElement) => {
this.root = n;
};
......@@ -118,7 +146,7 @@ class Pie extends Component<IPieProps, IPieState> {
const filteredLegendData = legendData.filter(l => l.checked).map(l => l.x);
if (this.chart) {
this.chart.filter('x', val => filteredLegendData.indexOf(val + '') > -1);
this.chart.filter('x', val => filteredLegendData.indexOf(`${val}`) > -1);
}
this.setState({
......@@ -126,33 +154,6 @@ class Pie extends Component<IPieProps, IPieState> {
});
};
// 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 &&
this.root.parentNode &&
(this.root.parentNode as HTMLElement).clientWidth <= 380
) {
if (!legendBlock) {
this.setState({
legendBlock: true,
});
}
} else if (legendBlock) {
this.setState({
legendBlock: false,
});
}
}
render() {
const {
valueFormat,
......@@ -216,11 +217,11 @@ class Pie extends Component<IPieProps, IPieState> {
data = [
{
x: '占比',
y: parseFloat(percent + ''),
y: parseFloat(`${percent}`),
},
{
x: '反比',
y: 100 - parseFloat(percent + ''),
y: 100 - parseFloat(`${percent}`),
},
];
}
......
import { Chart, Coord, Geom, Shape, Tooltip } from 'bizcharts';
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 Debounce from 'lodash.debounce';
import classNames from 'classnames';
import autoHeight from '../autoHeight';
import styles from './index.less';
......@@ -12,33 +12,36 @@ import styles from './index.less';
const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
export interface ITagCloudProps {
data: Array<{
export interface TagCloudProps {
data: {
name: string;
value: number;
}>;
}[];
height?: number;
className?: string;
style?: React.CSSProperties;
}
interface ITagCloudState {
interface TagCloudState {
dv: any;
height?: number;
width: number;
}
class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
class TagCloud extends Component<TagCloudProps, TagCloudState> {
state = {
dv: null,
height: 0,
width: 0,
};
isUnmount!: boolean;
requestRef!: number;
root: HTMLDivElement | undefined;
imageMask: HTMLImageElement | undefined;
isUnmount: boolean = false;
requestRef: number = 0;
root: HTMLDivElement | undefined = undefined;
imageMask: HTMLImageElement | undefined = undefined;
componentDidMount() {
requestAnimationFrame(() => {
......@@ -48,22 +51,25 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
window.addEventListener('resize', this.resize, { passive: true });
}
componentDidUpdate(preProps?: ITagCloudProps) {
componentDidUpdate(preProps?: TagCloudProps) {
const { data } = this.props;
if (preProps && 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(this.props);
});
};
saveRootRef = (node: HTMLDivElement) => {
this.root = node;
};
......@@ -77,7 +83,8 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
origin?: any;
color?: any;
}) {
return Object.assign({}, cfg.style, {
return {
...cfg.style,
fillOpacity: cfg.opacity,
fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate,
......@@ -86,7 +93,7 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
fontFamily: cfg.origin._origin.font,
fill: cfg.color,
textBaseline: 'Alphabetic',
});
};
}
(Shape as any).registerShape('point', 'cloud', {
......@@ -96,18 +103,17 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
) {
const attrs = getTextAttrs(cfg);
return container.addShape('text', {
attrs: Object.assign(attrs, {
attrs: {
...attrs,
x: cfg.x,
y: cfg.y,
}),
},
});
},
});
};
@Bind()
@Debounce(500)
renderChart(nextProps: ITagCloudProps) {
renderChart = Debounce((nextProps: TagCloudProps) => {
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const { data, height } = nextProps || this.props;
......@@ -134,8 +140,8 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
return 0;
},
fontSize(d: { value: number }) {
// eslint-disable-next-line
return Math.pow((d.value - min) / (max - min), 2) * (17.5 - 5) + 5;
const size = ((d.value - min) / (max - min)) ** 2;
return size * (17.5 - 5) + 5;
},
});
......@@ -159,7 +165,7 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
} else {
onload();
}
}
}, 500);
render() {
const { className, height } = this.props;
......
import React from 'react';
import { Chart, Tooltip, Geom, Legend, Axis } from 'bizcharts';
import { Axis, Chart, Geom, Legend, Tooltip } from 'bizcharts';
import DataSet from '@antv/data-set';
import React from 'react';
import Slider from 'bizcharts-plugin-slider';
import autoHeight from '../autoHeight';
import styles from './index.less';
export interface ITimelineChartProps {
data: Array<{
export interface TimelineChartProps {
data: {
x: number;
y1: number;
y2: number;
}>;
}[];
title?: string;
titleMap: { y1: string; y2: string };
padding?: [number, number, number, number];
......@@ -19,115 +20,113 @@ export interface ITimelineChartProps {
borderWidth?: number;
}
class TimelineChart extends React.Component<ITimelineChartProps> {
render() {
const {
title,
height = 400,
padding = [60, 20, 40, 40] as [number, number, number, number],
titleMap = {
y1: 'y1',
y2: 'y2',
},
borderWidth = 2,
data: sourceData,
} = this.props;
const TimelineChart: React.FC<TimelineChartProps> = props => {
const {
title,
height = 400,
padding = [60, 20, 40, 40] as [number, number, number, number],
titleMap = {
y1: 'y1',
y2: 'y2',
},
borderWidth = 2,
data: sourceData,
} = props;
const data = Array.isArray(sourceData) ? sourceData : [{ x: 0, y1: 0, y2: 0 }];
const data = Array.isArray(sourceData) ? sourceData : [{ x: 0, y1: 0, y2: 0 }];
data.sort((a, b) => a.x - b.x);
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,
);
}
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 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: { x: string }) => {
const date = obj.x;
return date <= ds.state.end && date >= ds.state.start;
},
})
.transform({
type: 'map',
callback(row: { y1: string; y2: string }) {
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 dv = ds.createView();
dv.source(data)
.transform({
type: 'filter',
callback: (obj: { x: string }) => {
const date = obj.x;
return date <= ds.state.end && date >= ds.state.start;
},
})
.transform({
type: 'map',
callback(row: { y1: string; y2: string }) {
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 timeScale = {
type: 'time',
tickInterval: 60 * 60 * 1000,
mask: 'HH:mm',
range: [0, 1],
};
const cols = {
x: timeScale,
value: {
max,
min: 0,
},
};
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 }: { startValue: string; endValue: string }) => {
ds.setState('start', startValue);
ds.setState('end', endValue);
}}
/>
);
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 }: { startValue: string; endValue: string }) => {
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>
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 autoHeight()(TimelineChart);
import React, { Component } from 'react';
import autoHeight from '../autoHeight';
import styles from './index.less';
......@@ -6,7 +7,7 @@ import styles from './index.less';
/* eslint no-mixed-operators: 0 */
// riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90
export interface IWaterWaveProps {
export interface WaterWaveProps {
title: React.ReactNode;
color?: string;
height?: number;
......@@ -14,13 +15,16 @@ export interface IWaterWaveProps {
style?: React.CSSProperties;
}
class WaterWave extends Component<IWaterWaveProps> {
class WaterWave extends Component<WaterWaveProps> {
state = {
radio: 1,
};
timer: number = 0;
root: HTMLDivElement | undefined | null;
node: HTMLCanvasElement | undefined | null;
root: HTMLDivElement | undefined | null = null;
node: HTMLCanvasElement | undefined | null = null;
componentDidMount() {
this.renderChart();
......@@ -34,7 +38,7 @@ class WaterWave extends Component<IWaterWaveProps> {
);
}
componentDidUpdate(props: IWaterWaveProps) {
componentDidUpdate(props: WaterWaveProps) {
const { percent } = this.props;
if (props.percent !== percent) {
// 不加这个会造成绘制缓慢
......@@ -59,6 +63,7 @@ class WaterWave extends Component<IWaterWaveProps> {
});
}
};
renderChart(type?: string) {
const { percent, color = '#1890FF' } = this.props;
const data = percent / 100;
......@@ -201,6 +206,7 @@ class WaterWave extends Component<IWaterWaveProps> {
}
render();
}
render() {
const { radio } = this.state;
const { percent, title, height = 1 } = this.props;
......
......@@ -6,15 +6,16 @@ export type IReactComponent<P = any> =
| React.ClassicComponentClass<P>;
function computeHeight(node: HTMLDivElement) {
node.style.height = '100%';
const totalHeight = parseInt(getComputedStyle(node).height + '', 10);
const { style } = node;
style.height = '100%';
const totalHeight = parseInt(`${getComputedStyle(node).height}`, 10);
const padding =
parseInt(getComputedStyle(node).paddingTop + '', 10) +
parseInt(getComputedStyle(node).paddingBottom + '', 10);
parseInt(`${getComputedStyle(node).paddingTop}`, 10) +
parseInt(`${getComputedStyle(node).paddingBottom}`, 10);
return totalHeight - padding;
}
function getAutoHeight(n: HTMLDivElement) {
function getAutoHeight(n: HTMLDivElement | undefined) {
if (!n) {
return 0;
}
......@@ -30,24 +31,25 @@ function getAutoHeight(n: HTMLDivElement) {
return height;
}
interface IAutoHeightProps {
interface AutoHeightProps {
height?: number;
}
function autoHeight() {
return function<P extends IAutoHeightProps>(
return <P extends AutoHeightProps>(
WrappedComponent: React.ComponentClass<P> | React.SFC<P>,
): React.ComponentClass<P> {
class AutoHeightComponent extends React.Component<P & IAutoHeightProps> {
): React.ComponentClass<P> => {
class AutoHeightComponent extends React.Component<P & AutoHeightProps> {
state = {
computedHeight: 0,
};
root!: HTMLDivElement;
root: HTMLDivElement | undefined = undefined;
componentDidMount() {
const { height } = this.props;
if (!height) {
let h = getAutoHeight(this.root);
// eslint-disable-next-line
this.setState({ computedHeight: h });
if (h < 1) {
h = getAutoHeight(this.root);
......@@ -55,9 +57,11 @@ function autoHeight() {
}
}
}
handleRoot = (node: HTMLDivElement) => {
this.root = node;
};
render() {
const { height } = this.props;
const { computedHeight } = this.state;
......
import numeral from 'numeral';
import Bar from './Bar';
import ChartCard from './ChartCard';
import Field from './Field';
import Bar from './Bar';
import Pie from './Pie';
import Gauge from './Gauge';
import MiniArea from './MiniArea';
import MiniBar from './MiniBar';
import MiniProgress from './MiniProgress';
import WaterWave from './WaterWave';
import Pie from './Pie';
import TagCloud from './TagCloud';
import TimelineChart from './TimelineChart';
import WaterWave from './WaterWave';
const yuan = (val: number | string) => ${numeral(val).format('0,0')}`;
......
import React from 'react';
import { Row, Col, Icon, Tooltip } from 'antd';
import { Col, Icon, Row, Tooltip } from 'antd';
import { FormattedMessage } from 'umi-plugin-react/locale';
import Charts from './Charts';
import React from 'react';
import numeral from 'numeral';
import styles from '../style.less';
import Yuan from '../utils/Yuan';
import { ChartCard, MiniArea, MiniBar, MiniProgress, Field } from './Charts';
import { VisitDataType } from '../data.d';
import Trend from './Trend';
import { IVisitData } from '../data.d';
const { ChartCard, MiniArea, MiniBar, MiniProgress, Field } = Charts;
import Yuan from '../utils/Yuan';
import styles from '../style.less';
const topColResponsiveProps = {
xs: 24,
......@@ -18,148 +18,143 @@ const topColResponsiveProps = {
style: { marginBottom: 24 },
};
const IntroduceRow = ({ loading, visitData }: { loading: boolean; visitData: IVisitData[] }) => {
return (
<Row gutter={24}>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
title={
<FormattedMessage id="BLOCK_NAME.analysis.total-sales" defaultMessage="Total Sales" />
}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
loading={loading}
total={() => <Yuan>126560</Yuan>}
footer={
<Field
label={
<FormattedMessage id="BLOCK_NAME.analysis.day-sales" defaultMessage="Daily Sales" />
}
value={`¥${numeral(12423).format('0,0')}`}
/>
}
contentHeight={46}
>
<Trend flag="up" style={{ marginRight: 16 }}>
<FormattedMessage id="BLOCK_NAME.analysis.week" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
<FormattedMessage id="BLOCK_NAME.analysis.day" defaultMessage="Daily Changes" />
<span className={styles.trendText}>11%</span>
</Trend>
</ChartCard>
</Col>
const IntroduceRow = ({ loading, visitData }: { loading: boolean; visitData: VisitDataType[] }) => (
<Row gutter={24}>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
title={
<FormattedMessage id="BLOCK_NAME.analysis.total-sales" defaultMessage="Total Sales" />
}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
loading={loading}
total={() => <Yuan>126560</Yuan>}
footer={
<Field
label={
<FormattedMessage id="BLOCK_NAME.analysis.day-sales" defaultMessage="Daily Sales" />
}
value={`¥${numeral(12423).format('0,0')}`}
/>
}
contentHeight={46}
>
<Trend flag="up" style={{ marginRight: 16 }}>
<FormattedMessage id="BLOCK_NAME.analysis.week" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
<FormattedMessage id="BLOCK_NAME.analysis.day" defaultMessage="Daily Changes" />
<span className={styles.trendText}>11%</span>
</Trend>
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
title={<FormattedMessage id="BLOCK_NAME.analysis.visits" defaultMessage="Visits" />}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
total={numeral(8846).format('0,0')}
footer={
<Field
label={
<FormattedMessage
id="BLOCK_NAME.analysis.day-visits"
defaultMessage="Daily Visits"
/>
}
value={numeral(1234).format('0,0')}
/>
}
contentHeight={46}
>
<MiniArea color="#975FE4" data={visitData} />
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
title={<FormattedMessage id="BLOCK_NAME.analysis.payments" defaultMessage="Payments" />}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
total={numeral(6560).format('0,0')}
footer={
<Field
label={
<FormattedMessage
id="BLOCK_NAME.analysis.conversion-rate"
defaultMessage="Conversion Rate"
/>
}
value="60%"
/>
}
contentHeight={46}
>
<MiniBar data={visitData} />
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
loading={loading}
bordered={false}
title={
<FormattedMessage
id="BLOCK_NAME.analysis.operational-effect"
defaultMessage="Operational Effect"
/>
}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
total="78%"
footer={
<div style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}>
<Trend flag="up" style={{ marginRight: 16 }}>
<FormattedMessage id="BLOCK_NAME.analysis.week" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
<FormattedMessage id="BLOCK_NAME.analysis.day" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>11%</span>
</Trend>
</div>
}
contentHeight={46}
>
<MiniProgress percent={78} strokeWidth={8} target={80} color="#13C2C2" />
</ChartCard>
</Col>
</Row>
);
};
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
title={<FormattedMessage id="BLOCK_NAME.analysis.visits" defaultMessage="Visits" />}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
total={numeral(8846).format('0,0')}
footer={
<Field
label={
<FormattedMessage id="BLOCK_NAME.analysis.day-visits" defaultMessage="Daily Visits" />
}
value={numeral(1234).format('0,0')}
/>
}
contentHeight={46}
>
<MiniArea color="#975FE4" data={visitData} />
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
title={<FormattedMessage id="BLOCK_NAME.analysis.payments" defaultMessage="Payments" />}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
total={numeral(6560).format('0,0')}
footer={
<Field
label={
<FormattedMessage
id="BLOCK_NAME.analysis.conversion-rate"
defaultMessage="Conversion Rate"
/>
}
value="60%"
/>
}
contentHeight={46}
>
<MiniBar data={visitData} />
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
loading={loading}
bordered={false}
title={
<FormattedMessage
id="BLOCK_NAME.analysis.operational-effect"
defaultMessage="Operational Effect"
/>
}
action={
<Tooltip
title={
<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />
}
>
<Icon type="info-circle-o" />
</Tooltip>
}
total="78%"
footer={
<div style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}>
<Trend flag="up" style={{ marginRight: 16 }}>
<FormattedMessage id="BLOCK_NAME.analysis.week" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
<FormattedMessage id="BLOCK_NAME.analysis.day" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>11%</span>
</Trend>
</div>
}
contentHeight={46}
>
<MiniProgress percent={78} strokeWidth={8} target={80} color="#13C2C2" />
</ChartCard>
</Col>
</Row>
);
export default IntroduceRow;
import React from 'react';
import { Icon } from 'antd';
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
export interface NumberInfoProps {
title?: React.ReactNode | string;
subTitle?: React.ReactNode | string;
......
import { Card, Col, Row, Tabs } from 'antd';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import React from 'react';
import { Card, Tabs, Row, Col } from 'antd';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import Charts from './Charts';
import styles from '../style.less';
import { OfflineChartData, OfflineDataType } from '../data.d';
import { TimelineChart, Pie } from './Charts';
import NumberInfo from './NumberInfo';
import { IOfflineData, IOfflineChartData } from '../data';
const { TimelineChart, Pie } = Charts;
import styles from '../style.less';
const CustomTab = ({
data,
currentTabKey: currentKey,
}: {
data: IOfflineData;
data: OfflineDataType;
currentTabKey: string;
}) => {
return (
<Row gutter={8} style={{ width: 138, margin: '8px 0' }}>
<Col span={12}>
<NumberInfo
title={data.name}
subTitle={
<FormattedMessage
id="BLOCK_NAME.analysis.conversion-rate"
defaultMessage="Conversion Rate"
/>
}
gap={2}
total={`${data.cvr * 100}%`}
theme={currentKey !== data.name ? 'light' : undefined}
/>
</Col>
<Col span={12} style={{ paddingTop: 36 }}>
<Pie
animate={false}
inner={0.55}
tooltip={false}
margin={[0, 0, 0, 0]}
percent={data.cvr * 100}
height={64}
/>
</Col>
</Row>
);
};
}) => (
<Row gutter={8} style={{ width: 138, margin: '8px 0' }}>
<Col span={12}>
<NumberInfo
title={data.name}
subTitle={
<FormattedMessage
id="BLOCK_NAME.analysis.conversion-rate"
defaultMessage="Conversion Rate"
/>
}
gap={2}
total={`${data.cvr * 100}%`}
theme={currentKey !== data.name ? 'light' : undefined}
/>
</Col>
<Col span={12} style={{ paddingTop: 36 }}>
<Pie
animate={false}
inner={0.55}
tooltip={false}
margin={[0, 0, 0, 0]}
percent={data.cvr * 100}
height={64}
/>
</Col>
</Row>
);
const { TabPane } = Tabs;
......@@ -55,8 +53,8 @@ const OfflineData = ({
}: {
activeKey: string;
loading: boolean;
offlineData: IOfflineData[];
offlineChartData: IOfflineChartData[];
offlineData: OfflineDataType[];
offlineChartData: OfflineChartData[];
handleTabChange: (activeKey: string) => void;
}) => (
<Card loading={loading} className={styles.offlineCard} bordered={false} style={{ marginTop: 32 }}>
......
import React from 'react';
import { Card, Radio } from 'antd';
import Charts from './Charts';
import { FormattedMessage } from 'umi-plugin-react/locale';
import styles from '../style.less';
import Yuan from '../utils/Yuan';
import { RadioChangeEvent } from 'antd/es/radio';
import { ISalesData } from '../data';
const { Pie } = Charts;
import React from 'react';
import { VisitDataType } from '../data.d';
import { Pie } from './Charts';
import Yuan from '../utils/Yuan';
import styles from '../style.less';
const ProportionSales = ({
dropdownGroup,
......@@ -19,61 +18,59 @@ const ProportionSales = ({
loading: boolean;
dropdownGroup: React.ReactNode;
salesType: 'all' | 'online' | 'stores';
salesPieData: ISalesData[];
salesPieData: VisitDataType[];
handleChangeSalesType?: (e: RadioChangeEvent) => void;
}) => {
return (
<Card
loading={loading}
className={styles.salesCard}
bordered={false}
title={
<FormattedMessage
id="BLOCK_NAME.analysis.the-proportion-of-sales"
defaultMessage="The Proportion of Sales"
/>
}
bodyStyle={{ padding: 24 }}
extra={
<div className={styles.salesCardExtra}>
{dropdownGroup}
<div className={styles.salesTypeRadio}>
<Radio.Group value={salesType} onChange={handleChangeSalesType}>
<Radio.Button value="all">
<FormattedMessage id="BLOCK_NAME.channel.all" defaultMessage="ALL" />
</Radio.Button>
<Radio.Button value="online">
<FormattedMessage id="BLOCK_NAME.channel.online" defaultMessage="Online" />
</Radio.Button>
<Radio.Button value="stores">
<FormattedMessage id="BLOCK_NAME.channel.stores" defaultMessage="Stores" />
</Radio.Button>
</Radio.Group>
</div>
}) => (
<Card
loading={loading}
className={styles.salesCard}
bordered={false}
title={
<FormattedMessage
id="BLOCK_NAME.analysis.the-proportion-of-sales"
defaultMessage="The Proportion of Sales"
/>
}
bodyStyle={{ padding: 24 }}
extra={
<div className={styles.salesCardExtra}>
{dropdownGroup}
<div className={styles.salesTypeRadio}>
<Radio.Group value={salesType} onChange={handleChangeSalesType}>
<Radio.Button value="all">
<FormattedMessage id="BLOCK_NAME.channel.all" defaultMessage="ALL" />
</Radio.Button>
<Radio.Button value="online">
<FormattedMessage id="BLOCK_NAME.channel.online" defaultMessage="Online" />
</Radio.Button>
<Radio.Button value="stores">
<FormattedMessage id="BLOCK_NAME.channel.stores" defaultMessage="Stores" />
</Radio.Button>
</Radio.Group>
</div>
}
style={{ marginTop: 24 }}
>
<div
style={{
minHeight: 380,
}}
>
<h4 style={{ marginTop: 8, marginBottom: 32 }}>
<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />
</h4>
<Pie
hasLegend
subTitle={<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />}
total={() => <Yuan>{salesPieData.reduce((pre, now) => now.y + pre, 0)}</Yuan>}
data={salesPieData}
valueFormat={value => <Yuan>{value}</Yuan>}
height={248}
lineWidth={4}
/>
</div>
</Card>
);
};
}
style={{ marginTop: 24 }}
>
<div
style={{
minHeight: 380,
}}
>
<h4 style={{ marginTop: 8, marginBottom: 32 }}>
<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />
</h4>
<Pie
hasLegend
subTitle={<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />}
total={() => <Yuan>{salesPieData.reduce((pre, now) => now.y + pre, 0)}</Yuan>}
data={salesPieData}
valueFormat={value => <Yuan>{value}</Yuan>}
height={248}
lineWidth={4}
/>
</div>
</Card>
);
export default ProportionSales;
import React from 'react';
import { Row, Col, Card, Tabs, DatePicker } from 'antd';
import { Card, Col, DatePicker, Row, Tabs } from 'antd';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import numeral from 'numeral';
import Charts from './Charts';
import { RangePickerValue } from 'antd/es/date-picker/interface';
import { ISalesData } from '../data';
import React from 'react';
import numeral from 'numeral';
import { VisitDataType } from '../data.d';
import { Bar } from './Charts';
import styles from '../style.less';
const { Bar } = Charts;
const { RangePicker } = DatePicker;
const { TabPane } = Tabs;
......@@ -30,7 +29,7 @@ const SalesCard = ({
}: {
rangePickerValue: RangePickerValue;
isActive: (key: 'today' | 'week' | 'month' | 'year') => string;
salesData: ISalesData[];
salesData: VisitDataType[];
loading: boolean;
handleRangePickerChange: (dates: RangePickerValue, dateStrings: [string, string]) => void;
selectDate: (key: 'today' | 'week' | 'month' | 'year') => void;
......
import React from 'react';
import { Row, Col, Table, Tooltip, Card, Icon } from 'antd';
import { Card, Col, Icon, Row, Table, Tooltip } from 'antd';
import { FormattedMessage } from 'umi-plugin-react/locale';
import Charts from './Charts';
import Trend from './Trend';
import NumberInfo from './NumberInfo';
import React from 'react';
import numeral from 'numeral';
import styles from '../style.less';
import { ISearchData, IVisitData2 } from '../data';
import { SearchDataType, VisitDataType } from '../data.d';
const { MiniArea } = Charts;
import { MiniArea } from './Charts';
import NumberInfo from './NumberInfo';
import Trend from './Trend';
import styles from '../style.less';
const columns = [
{
......@@ -51,9 +50,9 @@ const TopSearch = ({
dropdownGroup,
}: {
loading: boolean;
visitData2: IVisitData2[];
visitData2: VisitDataType[];
dropdownGroup: React.ReactNode;
searchData: ISearchData[];
searchData: SearchDataType[];
}) => (
<Card
loading={loading}
......
import React from 'react';
import { Icon } from 'antd';
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
export interface ITrendProps {
export interface TrendProps {
colorful?: boolean;
flag: 'up' | 'down';
style?: React.CSSProperties;
......@@ -11,7 +11,7 @@ export interface ITrendProps {
className?: string;
}
const Trend: React.SFC<ITrendProps> = ({
const Trend: React.SFC<TrendProps> = ({
colorful = true,
reverseColor = false,
flag,
......
export interface IVisitData {
export interface VisitDataType {
x: string;
y: number;
}
export interface IVisitData2 {
x: string;
y: number;
}
export interface ISalesData {
x: string;
y: number;
}
export interface ISearchData {
export interface SearchDataType {
index: number;
keyword: string;
count: number;
......@@ -21,47 +11,32 @@ export interface ISearchData {
status: number;
}
export interface IOfflineData {
export interface OfflineDataType {
name: string;
cvr: number;
}
export interface IOfflineChartData {
export interface OfflineChartData {
x: any;
y1: number;
y2: number;
}
export interface ISalesTypeData {
x: string;
y: number;
}
export interface ISalesTypeDataOnline {
x: string;
y: number;
}
export interface ISalesTypeDataOffline {
x: string;
y: number;
}
export interface IRadarData {
export interface RadarData {
name: string;
label: string;
value: number;
}
export interface IAnalysisData {
visitData: IVisitData[];
visitData2: IVisitData2[];
salesData: ISalesData[];
searchData: ISearchData[];
offlineData: IOfflineData[];
offlineChartData: IOfflineChartData[];
salesTypeData: ISalesTypeData[];
salesTypeDataOnline: ISalesTypeDataOnline[];
salesTypeDataOffline: ISalesTypeDataOffline[];
radarData: IRadarData[];
export interface AnalysisData {
visitData: VisitDataType[];
visitData2: VisitDataType[];
salesData: VisitDataType[];
searchData: SearchDataType[];
offlineData: OfflineDataType[];
offlineChartData: OfflineChartData[];
salesTypeData: VisitDataType[];
salesTypeDataOnline: VisitDataType[];
salesTypeDataOffline: VisitDataType[];
radarData: RadarData[];
}
import { Col, Dropdown, Icon, Menu, Row } from 'antd';
import React, { Component, Suspense } from 'react';
import { connect } from 'dva';
import { Row, Col, Icon, Menu, Dropdown } from 'antd';
import { Dispatch } from 'redux';
import { GridContent } from '@ant-design/pro-layout';
import { RadioChangeEvent } from 'antd/es/radio';
import { RangePickerValue } from 'antd/es/date-picker/interface';
import { connect } from 'dva';
import PageLoading from './components/PageLoading';
import { getTimeDistance } from './utils/utils';
import { AnalysisData } from './data.d';
import styles from './style.less';
import PageLoading from './components/PageLoading';
import { Dispatch } from 'redux';
import { IAnalysisData } from './data.d';
import { RadioChangeEvent } from 'antd/es/radio';
import { GridContent } from '@ant-design/pro-layout';
const IntroduceRow = React.lazy(() => import('./components/IntroduceRow'));
const SalesCard = React.lazy(() => import('./components/SalesCard'));
......@@ -17,7 +18,7 @@ const ProportionSales = React.lazy(() => import('./components/ProportionSales'))
const OfflineData = React.lazy(() => import('./components/OfflineData'));
interface BLOCK_NAME_CAMEL_CASEProps {
BLOCK_NAME_CAMEL_CASE: IAnalysisData;
BLOCK_NAME_CAMEL_CASE: AnalysisData;
dispatch: Dispatch<any>;
loading: boolean;
}
......@@ -51,8 +52,11 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component<
currentTabKey: '',
rangePickerValue: getTimeDistance('year'),
};
reqRef!: number;
timeoutId!: number;
reqRef: number = 0;
timeoutId: number = 0;
componentDidMount() {
const { dispatch } = this.props;
this.reqRef = requestAnimationFrame(() => {
......
import { fakeChartData } from './service';
import { IAnalysisData } from './data';
import { Reducer } from 'redux';
import { AnyAction, Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { AnyAction } from 'redux';
import { AnalysisData } from './data.d';
import { fakeChartData } from './service';
export type Effect = (
action: AnyAction,
effects: EffectsCommandMap & { select: <T>(func: (state: IAnalysisData) => T) => T },
effects: EffectsCommandMap & { select: <T>(func: (state: AnalysisData) => T) => T },
) => void;
export interface ModelType {
namespace: string;
state: IAnalysisData;
state: AnalysisData;
effects: {
fetch: Effect;
fetchSalesData: Effect;
};
reducers: {
save: Reducer<IAnalysisData>;
clear: Reducer<IAnalysisData>;
save: Reducer<AnalysisData>;
clear: Reducer<AnalysisData>;
};
}
......
......@@ -6,7 +6,8 @@ import { yuan } from '../components/Charts';
export default class Yuan extends React.Component<{
children: React.ReactText;
}> {
main: HTMLSpanElement | undefined | null;
main: HTMLSpanElement | undefined | null = null;
componentDidMount() {
this.renderToHtml();
}
......@@ -14,6 +15,7 @@ export default class Yuan extends React.Component<{
componentDidUpdate() {
this.renderToHtml();
}
renderToHtml = () => {
const { children } = this.props;
if (this.main) {
......
import moment from 'moment';
import { RangePickerValue } from 'antd/es/date-picker/interface';
import moment from 'moment';
export function fixedZero(val: number) {
return val * 1 < 10 ? `0${val}` : val;
......
......@@ -13,21 +13,25 @@
"dependencies": {
"@ant-design/pro-layout": "^4.5.2",
"@antv/data-set": "^0.10.2",
"@types/lodash.debounce": "^4.0.6",
"antd": "^3.16.3",
"bizcharts": "^3.5.3-beta.0",
"bizcharts-plugin-slider": "^2.1.1-beta.1",
"classnames": "^2.2.6",
"dva": "^2.4.0",
"lodash.debounce": "^4.0.8",
"numeral": "^2.0.6",
"react": "^16.8.6",
"react-fittext": "^1.0.0",
"umi-request": "^1.0.0"
},
"devDependencies": {
"@types/numeral": "^0.0.25",
"mockjs": "^1.0.1-beta3",
"redux": "^4.0.1",
"umi": "^2.6.9",
"umi-plugin-block-dev": "^1.0.0",
"umi-plugin-react": "^1.7.2"
"umi-plugin-react": "^1.7.2",
"umi-request": "^1.0.0",
"mockjs": "^1.0.1-beta3"
},
"devDependencies": {
"@types/numeral": "^0.0.25"
},
"license": "ISC",
"blockConfig": {
......
import React, { Component } from 'react';
import Charts from '../Charts';
import { Statistic } from 'antd';
import { MiniArea } from '../Charts';
import styles from './index.less';
const { MiniArea } = Charts;
function fixedZero(val: number) {
return val * 1 < 10 ? `0${val}` : val;
}
......@@ -24,12 +23,15 @@ export default class ActiveChart extends Component {
state = {
activeData: getActiveData(),
};
timer: number | undefined;
requestRef: number | undefined;
timer: number | undefined = undefined;
requestRef: number | undefined = undefined;
componentDidMount() {
this.loopData();
}
componentWillUnmount() {
clearTimeout(this.timer);
if (this.requestRef) {
......
import { Axis, Chart, Coord, Geom, Guide, Shape } from 'bizcharts';
import React from 'react';
import { Chart, Geom, Axis, Coord, Guide, Shape } from 'bizcharts';
import autoHeight from '../autoHeight';
const { Arc, Html, Line } = Guide;
export interface IGaugeProps {
export interface GaugeProps {
title: React.ReactNode;
color?: string;
height?: number;
......@@ -30,148 +31,149 @@ const defaultFormatter = (val: string): string => {
}
};
Shape.registerShape!('point', 'pointer', {
drawShape(cfg: any, group: any) {
let point = cfg.points[0];
point = (this as any).parsePoint(point);
const center = (this as any).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',
},
});
},
});
if (Shape.registerShape) {
Shape.registerShape('point', 'pointer', {
drawShape(cfg: any, group: any) {
let point = cfg.points[0];
point = (this as any).parsePoint(point);
const center = (this as any).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',
},
});
},
});
}
const Gauge: React.FC<GaugeProps> = props => {
const {
title,
height = 1,
percent,
forceFit = true,
formatter = defaultFormatter,
color = '#2F9CFF',
bgColor = '#F0F2F5',
} = props;
const cols = {
value: {
type: 'linear',
min: 0,
max: 10,
tickCount: 6,
nice: true,
},
};
const data = [{ value: percent / 10 }];
class Gauge extends React.Component<IGaugeProps> {
render() {
const {
title,
height = 1,
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 renderHtml = () => `
const renderHtml = () => `
<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).toFixed(2)}%
</p>
</div>`;
const data = [{ value: percent / 10 }];
const textStyle: {
fontSize: number;
fill: string;
textAlign: 'center';
} = {
fontSize: 12,
fill: 'rgba(0, 0, 0, 0.65)',
textAlign: 'center',
};
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={undefined} />
<Axis
line={undefined}
tickLine={undefined}
subTickLine={undefined}
name="value"
zIndex={2}
label={{
offset: -12,
formatter,
textStyle: textStyle,
const textStyle: {
fontSize: number;
fill: string;
textAlign: 'center';
} = {
fontSize: 12,
fill: 'rgba(0, 0, 0, 0.65)',
textAlign: 'center',
};
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={undefined} />
<Axis
line={undefined}
tickLine={undefined}
subTickLine={undefined}
name="value"
zIndex={2}
label={{
offset: -12,
formatter,
textStyle,
}}
/>
<Guide>
<Line
start={[3, 0.905]}
end={[3, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 2,
}}
/>
<Guide>
<Line
start={[3, 0.905]}
end={[3, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 2,
}}
/>
<Line
start={[5, 0.905]}
end={[5, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
<Line
start={[7, 0.905]}
end={[7, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
<Arc
start={[0, 0.965]}
end={[10, 0.965]}
style={{
stroke: bgColor,
lineWidth: 10,
}}
/>
<Arc
start={[0, 0.965]}
end={[data[0].value, 0.965]}
style={{
stroke: color,
lineWidth: 10,
}}
/>
<Html position={['50%', '95%']} html={renderHtml()} />
</Guide>
<Geom
line={false}
type="point"
position="value*1"
shape="pointer"
color={color}
active={false}
<Line
start={[5, 0.905]}
end={[5, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
</Chart>
);
}
}
<Line
start={[7, 0.905]}
end={[7, 0.85]}
lineStyle={{
stroke: color,
lineDash: undefined,
lineWidth: 3,
}}
/>
<Arc
start={[0, 0.965]}
end={[10, 0.965]}
style={{
stroke: bgColor,
lineWidth: 10,
}}
/>
<Arc
start={[0, 0.965]}
end={[data[0].value, 0.965]}
style={{
stroke: color,
lineWidth: 10,
}}
/>
<Html position={['50%', '95%']} html={renderHtml()} />
</Guide>
<Geom
line={false}
type="point"
position="value*1"
shape="pointer"
color={color}
active={false}
/>
</Chart>
);
};
export default autoHeight()(Gauge);
import { Axis, AxisProps, Chart, Geom, Tooltip } from 'bizcharts';
import React from 'react';
import { Chart, Axis, Tooltip, Geom } from 'bizcharts';
import autoHeight from '../autoHeight';
import styles from '../index.less';
export interface IAxis {
title: any;
line: any;
gridAlign: any;
labels: any;
tickLine: any;
grid: any;
label: any;
}
export interface IMiniAreaProps {
export interface MiniAreaProps {
color?: string;
height?: number;
borderColor?: string;
line?: boolean;
animate?: boolean;
xAxis?: IAxis;
xAxis?: AxisProps;
forceFit?: boolean;
scale?: { x?: any; y?: any };
yAxis?: Partial<IAxis>;
scale?: {
x?: {
tickCount: number;
};
y?: {
tickCount: number;
};
};
yAxis?: Partial<AxisProps>;
borderWidth?: number;
data: Array<{
data: {
x: number | string;
y: number;
}>;
}[];
}
class MiniArea extends React.Component<IMiniAreaProps> {
render() {
const {
height = 1,
data = [],
forceFit = true,
color = 'rgba(24, 144, 255, 0.2)',
borderColor = '#1089ff',
scale = { x: {}, y: {} },
borderWidth = 2,
line,
xAxis,
yAxis,
animate = true,
} = this.props;
const MiniArea: React.FC<MiniAreaProps> = props => {
const {
height = 1,
data = [],
forceFit = true,
color = 'rgba(24, 144, 255, 0.2)',
borderColor = '#1089ff',
scale = { x: {}, y: {} },
borderWidth = 2,
line,
xAxis,
yAxis,
animate = true,
} = props;
const padding: [number, number, number, number] = [36, 5, 30, 5];
const padding: [number, number, number, number] = [36, 5, 30, 5];
const scaleProps = {
x: {
type: 'cat',
range: [0, 1],
...scale!.x,
},
y: {
min: 0,
...scale!.y,
},
};
const scaleProps = {
x: {
type: 'cat',
range: [0, 1],
...scale.x,
},
y: {
min: 0,
...scale.y,
},
};
const tooltip: [string, (...args: any[]) => { name?: string; value: string }] = [
'x*y',
(x: string, y: string) => ({
name: x,
value: y,
}),
];
const tooltip: [string, (...args: any[]) => { name?: string; value: string }] = [
'x*y',
(x: string, y: string) => ({
name: x,
value: y,
}),
];
const chartHeight = height + 54;
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} />
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={null}
line={null}
tickLine={null}
grid={null}
{...xAxis}
/>
<Axis
key="axis-y"
name="y"
label={null}
line={null}
tickLine={null}
grid={null}
{...yAxis}
/>
<Tooltip showTitle={false} crosshairs={false} />
<Geom
type="area"
position="x*y"
color={color}
tooltip={tooltip}
shape="smooth"
style={{
fillOpacity: 1,
}}
/>
{line ? (
<Geom
type="area"
type="line"
position="x*y"
color={color}
tooltip={tooltip}
shape="smooth"
style={{
fillOpacity: 1,
}}
color={borderColor}
size={borderWidth}
tooltip={false}
/>
{line ? (
<Geom
type="line"
position="x*y"
shape="smooth"
color={borderColor}
size={borderWidth}
tooltip={false}
/>
) : (
<span style={{ display: 'none' }} />
)}
</Chart>
)}
</div>
) : (
<span style={{ display: 'none' }} />
)}
</Chart>
)}
</div>
);
}
}
</div>
);
};
export default autoHeight()(MiniArea);
import { Chart, Coord, Geom, Tooltip } from 'bizcharts';
import React, { Component } from 'react';
import { Chart, Tooltip, Geom, Coord } from 'bizcharts';
import { DataView } from '@antv/data-set';
import Debounce from 'lodash.debounce';
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 classNames from 'classnames';
import autoHeight from '../autoHeight';
import styles from './index.less';
export interface IPieProps {
export interface PieProps {
animate?: boolean;
color?: string;
colors?: string[];
......@@ -19,10 +19,10 @@ export interface IPieProps {
hasLegend?: boolean;
padding?: [number, number, number, number];
percent?: number;
data?: Array<{
data?: {
x: string | string;
y: number;
}>;
}[];
inner?: number;
lineWidth?: number;
forceFit?: boolean;
......@@ -34,19 +34,46 @@ export interface IPieProps {
valueFormat?: (value: string) => string | React.ReactNode;
subTitle?: React.ReactNode;
}
interface IPieState {
legendData: Array<{ checked: boolean; x: string; color: string; percent: number; y: string }>;
interface PieState {
legendData: { checked: boolean; x: string; color: string; percent: number; y: string }[];
legendBlock: boolean;
}
class Pie extends Component<IPieProps, IPieState> {
state: IPieState = {
class Pie extends Component<PieProps, PieState> {
state: PieState = {
legendData: [],
legendBlock: false,
};
requestRef: number | undefined;
root!: HTMLDivElement;
chart: G2.Chart | undefined;
chart: G2.Chart | undefined = undefined;
root: HTMLDivElement | undefined = undefined;
requestRef: number | undefined = 0;
// for window resize auto responsive legend
resize = Debounce(() => {
const { hasLegend } = this.props;
const { legendBlock } = this.state;
if (!hasLegend || !this.root) {
window.removeEventListener('resize', this.resize);
return;
}
if (
this.root &&
this.root.parentNode &&
(this.root.parentNode as HTMLElement).clientWidth <= 380
) {
if (!legendBlock) {
this.setState({
legendBlock: true,
});
}
} else if (legendBlock) {
this.setState({
legendBlock: false,
});
}
}, 300);
componentDidMount() {
window.addEventListener(
......@@ -58,7 +85,7 @@ class Pie extends Component<IPieProps, IPieState> {
);
}
componentDidUpdate(preProps: IPieProps) {
componentDidUpdate(preProps: PieProps) {
const { data } = this.props;
if (data !== preProps.data) {
// because of charts data create when rendered
......@@ -90,7 +117,8 @@ class Pie extends Component<IPieProps, IPieState> {
if (!this.chart) return;
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return;
const items = geom.get('dataArray') || []; // 获取图形对应的
// g2 的类型有问题
const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item: { color: any; _origin: any }[]) => {
/* eslint no-underscore-dangle:0 */
......@@ -104,11 +132,12 @@ class Pie extends Component<IPieProps, IPieState> {
legendData,
});
};
handleRoot = (n: HTMLDivElement) => {
this.root = n;
};
handleLegendClick = (item: any, i: string | number) => {
handleLegendClick = (item: { checked: boolean }, i: string | number) => {
const newItem = item;
newItem.checked = !newItem.checked;
......@@ -118,7 +147,7 @@ class Pie extends Component<IPieProps, IPieState> {
const filteredLegendData = legendData.filter(l => l.checked).map(l => l.x);
if (this.chart) {
this.chart.filter('x', val => filteredLegendData.indexOf(val + '') > -1);
this.chart.filter('x', val => filteredLegendData.indexOf(`${val}`) > -1);
}
this.setState({
......@@ -126,33 +155,6 @@ class Pie extends Component<IPieProps, IPieState> {
});
};
// 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 &&
this.root.parentNode &&
(this.root.parentNode as HTMLElement).clientWidth <= 380
) {
if (!legendBlock) {
this.setState({
legendBlock: true,
});
}
} else if (legendBlock) {
this.setState({
legendBlock: false,
});
}
}
render() {
const {
valueFormat,
......@@ -216,11 +218,11 @@ class Pie extends Component<IPieProps, IPieState> {
data = [
{
x: '占比',
y: parseFloat(percent + ''),
y: parseFloat(`${percent}`),
},
{
x: '反比',
y: 100 - parseFloat(percent + ''),
y: 100 - parseFloat(`${percent}`),
},
];
}
......
import { Chart, Coord, Geom, Shape, Tooltip } from 'bizcharts';
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 Debounce from 'lodash.debounce';
import classNames from 'classnames';
import autoHeight from '../autoHeight';
import styles from './index.less';
......@@ -12,33 +12,36 @@ import styles from './index.less';
const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
export interface ITagCloudProps {
data: Array<{
export interface TagCloudProps {
data: {
name: string;
value: string;
}>;
}[];
height?: number;
className?: string;
style?: React.CSSProperties;
}
interface ITagCloudState {
interface TagCloudState {
dv: any;
height?: number;
width: number;
}
class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
class TagCloud extends Component<TagCloudProps, TagCloudState> {
state = {
dv: null,
height: 0,
width: 0,
};
isUnmount!: boolean;
requestRef!: number;
root: HTMLDivElement | undefined;
imageMask: HTMLImageElement | undefined;
requestRef: number = 0;
isUnmount: boolean = false;
root: HTMLDivElement | undefined = undefined;
imageMask: HTMLImageElement | undefined = undefined;
componentDidMount() {
requestAnimationFrame(() => {
......@@ -48,22 +51,25 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
window.addEventListener('resize', this.resize, { passive: true });
}
componentDidUpdate(preProps?: ITagCloudProps) {
componentDidUpdate(preProps?: TagCloudProps) {
const { data } = this.props;
if (preProps && 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(this.props);
});
};
saveRootRef = (node: HTMLDivElement) => {
this.root = node;
};
......@@ -77,7 +83,8 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
origin?: any;
color?: any;
}) {
return Object.assign({}, cfg.style, {
return {
...cfg.style,
fillOpacity: cfg.opacity,
fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate,
......@@ -86,7 +93,7 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
fontFamily: cfg.origin._origin.font,
fill: cfg.color,
textBaseline: 'Alphabetic',
});
};
}
(Shape as any).registerShape('point', 'cloud', {
......@@ -96,18 +103,17 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
) {
const attrs = getTextAttrs(cfg);
return container.addShape('text', {
attrs: Object.assign(attrs, {
attrs: {
...attrs,
x: cfg.x,
y: cfg.y,
}),
},
});
},
});
};
@Bind()
@Debounce(500)
renderChart(nextProps: ITagCloudProps) {
renderChart = Debounce((nextProps: TagCloudProps) => {
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const { data, height } = nextProps || this.props;
if (data.length < 1 || !this.root) {
......@@ -133,8 +139,8 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
return 0;
},
fontSize(d: { value: number }) {
// eslint-disable-next-line
return Math.pow((d.value - min) / (max - min), 2) * (17.5 - 5) + 5;
const size = ((d.value - min) / (max - min)) ** 2;
return size * (17.5 - 5) + 5;
},
});
......@@ -158,7 +164,7 @@ class TagCloud extends Component<ITagCloudProps, ITagCloudState> {
} else {
onload();
}
}
}, 200);
render() {
const { className, height } = this.props;
......
import React, { Component } from 'react';
import autoHeight from '../autoHeight';
import styles from './index.less';
......@@ -6,7 +7,7 @@ import styles from './index.less';
/* eslint no-mixed-operators: 0 */
// riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90
export interface IWaterWaveProps {
export interface WaterWaveProps {
title: React.ReactNode;
color?: string;
height?: number;
......@@ -14,13 +15,16 @@ export interface IWaterWaveProps {
style?: React.CSSProperties;
}
class WaterWave extends Component<IWaterWaveProps> {
class WaterWave extends Component<WaterWaveProps> {
state = {
radio: 1,
};
timer: number = 0;
root: HTMLDivElement | undefined | null;
node: HTMLCanvasElement | undefined | null;
root: HTMLDivElement | undefined | null = null;
node: HTMLCanvasElement | undefined | null = null;
componentDidMount() {
this.renderChart();
......@@ -34,7 +38,7 @@ class WaterWave extends Component<IWaterWaveProps> {
);
}
componentDidUpdate(props: IWaterWaveProps) {
componentDidUpdate(props: WaterWaveProps) {
const { percent } = this.props;
if (props.percent !== percent) {
// 不加这个会造成绘制缓慢
......@@ -59,6 +63,7 @@ class WaterWave extends Component<IWaterWaveProps> {
});
}
};
renderChart(type?: string) {
const { percent, color = '#1890FF' } = this.props;
const data = percent / 100;
......@@ -201,6 +206,7 @@ class WaterWave extends Component<IWaterWaveProps> {
}
render();
}
render() {
const { radio } = this.state;
const { percent, title, height = 1 } = this.props;
......
......@@ -6,11 +6,12 @@ export type IReactComponent<P = any> =
| React.ClassicComponentClass<P>;
function computeHeight(node: HTMLDivElement) {
node.style.height = '100%';
const totalHeight = parseInt(getComputedStyle(node).height + '', 10);
const { style } = node;
style.height = '100%';
const totalHeight = parseInt(`${getComputedStyle(node).height}`, 10);
const padding =
parseInt(getComputedStyle(node).paddingTop + '', 10) +
parseInt(getComputedStyle(node).paddingBottom + '', 10);
parseInt(`${getComputedStyle(node).paddingTop}`, 10) +
parseInt(`${getComputedStyle(node).paddingBottom}`, 10);
return totalHeight - padding;
}
......@@ -30,22 +31,24 @@ function getAutoHeight(n: HTMLDivElement) {
return height;
}
interface IAutoHeightProps {
interface AutoHeightProps {
height?: number;
}
function autoHeight() {
return function<P extends IAutoHeightProps>(
return <P extends AutoHeightProps>(
WrappedComponent: React.ComponentClass<P> | React.SFC<P>,
): React.ComponentClass<P> {
class AutoHeightComponent extends React.Component<P & IAutoHeightProps> {
): React.ComponentClass<P> => {
class AutoHeightComponent extends React.Component<P & AutoHeightProps> {
state = {
computedHeight: 0,
};
root!: HTMLDivElement;
root: HTMLDivElement | null = null;
componentDidMount() {
const { height } = this.props;
if (!height) {
if (!height && this.root) {
let h = getAutoHeight(this.root);
this.setState({ computedHeight: h });
if (h < 1) {
......@@ -54,9 +57,11 @@ function autoHeight() {
}
}
}
handleRoot = (node: HTMLDivElement) => {
this.root = node;
};
render() {
const { height } = this.props;
const { computedHeight } = this.state;
......
import Pie from './Pie';
import Gauge from './Gauge';
import WaterWave from './WaterWave';
import TagCloud from './TagCloud';
import MiniArea from './MiniArea';
import Pie from './Pie';
import TagCloud from './TagCloud';
import WaterWave from './WaterWave';
const Charts = {
Pie,
WaterWave,
......
export interface ITag {
export interface TagType {
name: string;
value: string;
type: string;
......
import { Card, Col, Row, Statistic, Tooltip } from 'antd';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import React, { Component } from 'react';
import { Dispatch } from 'redux';
import { GridContent } from '@ant-design/pro-layout';
import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Row, Col, Card, Statistic, Tooltip } from 'antd';
import numeral from 'numeral';
import { Dispatch } from 'redux';
import { IStateType } from './model';
import { StateType } from './model';
import { Pie, WaterWave, Gauge, TagCloud } from './components/Charts';
import ActiveChart from './components/ActiveChart';
import styles from './style.less';
import Charts from './components/Charts';
import { GridContent } from '@ant-design/pro-layout';
const { Countdown } = Statistic;
const { Pie, WaterWave, Gauge, TagCloud } = Charts;
const targetTime = new Date().getTime() + 3900000;
interface PAGE_NAME_UPPER_CAMEL_CASEProps {
BLOCK_NAME_CAMEL_CASE: IStateType;
BLOCK_NAME_CAMEL_CASE: StateType;
dispatch: Dispatch<any>;
loading: boolean;
}
......@@ -27,7 +26,7 @@ interface PAGE_NAME_UPPER_CAMEL_CASEProps {
BLOCK_NAME_CAMEL_CASE,
loading,
}: {
BLOCK_NAME_CAMEL_CASE: IStateType;
BLOCK_NAME_CAMEL_CASE: StateType;
loading: {
models: { [key: string]: boolean };
};
......
import { queryTags } from './service';
import { ITag } from './data';
import { Reducer } from 'redux';
import { AnyAction, Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { AnyAction } from 'redux';
import { TagType } from './data.d';
import { queryTags } from './service';
export interface IStateType {
tags: ITag[];
export interface StateType {
tags: TagType[];
}
export type Effect = (
action: AnyAction,
effects: EffectsCommandMap & { select: <T>(func: (state: IStateType) => T) => T },
effects: EffectsCommandMap & { select: <T>(func: (state: StateType) => T) => T },
) => void;
export interface ModelType {
namespace: string;
state: IStateType;
state: StateType;
effects: {
fetchTags: Effect;
};
reducers: {
saveTags: Reducer<IStateType>;
saveTags: Reducer<StateType>;
};
}
......
......@@ -11,13 +11,15 @@
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/workplace"
},
"dependencies": {
"@ant-design/pro-layout": "^4.5.5",
"antd": "^3.16.3",
"bizcharts": "^3.5.3-beta.0",
"dva": "^2.4.0",
"moment": "^2.22.2",
"prop-types": "^15.5.10",
"react": "^16.6.3",
"umi-request": "^1.0.0",
"bizcharts": "^3.5.3-beta.0"
"redux": "^4.0.1",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.6.9",
......
import React, { PureComponent, createElement } from 'react';
import { Button } from 'antd';
import styles from './index.less';
......
......@@ -6,15 +6,16 @@ export type IReactComponent<P = any> =
| React.ClassicComponentClass<P>;
function computeHeight(node: HTMLDivElement) {
node.style.height = '100%';
const totalHeight = parseInt(getComputedStyle(node).height + '', 10);
const { style } = node;
style.height = '100%';
const totalHeight = parseInt(`${getComputedStyle(node).height}`, 10);
const padding =
parseInt(getComputedStyle(node).paddingTop + '', 10) +
parseInt(getComputedStyle(node).paddingBottom + '', 10);
parseInt(`${getComputedStyle(node).paddingTop}`, 10) +
parseInt(`${getComputedStyle(node).paddingBottom}`, 10);
return totalHeight - padding;
}
function getAutoHeight(n: HTMLDivElement) {
function getAutoHeight(n: HTMLDivElement | undefined) {
if (!n) {
return 0;
}
......@@ -30,24 +31,25 @@ function getAutoHeight(n: HTMLDivElement) {
return height;
}
interface IAutoHeightProps {
interface AutoHeightProps {
height?: number;
}
function autoHeight() {
return function<P extends IAutoHeightProps>(
return <P extends AutoHeightProps>(
WrappedComponent: React.ComponentClass<P> | React.SFC<P>,
): React.ComponentClass<P> {
class AutoHeightComponent extends React.Component<P & IAutoHeightProps> {
): React.ComponentClass<P> => {
class AutoHeightComponent extends React.Component<P & AutoHeightProps> {
state = {
computedHeight: 0,
};
root!: HTMLDivElement;
root: HTMLDivElement | undefined = undefined;
componentDidMount() {
const { height } = this.props;
if (!height) {
let h = getAutoHeight(this.root);
// eslint-disable-next-line
this.setState({ computedHeight: h });
if (h < 1) {
h = getAutoHeight(this.root);
......@@ -55,9 +57,11 @@ function autoHeight() {
}
}
}
handleRoot = (node: HTMLDivElement) => {
this.root = node;
};
render() {
const { height } = this.props;
const { computedHeight } = this.state;
......
import { Axis, Chart, Coord, Geom, Tooltip } from 'bizcharts';
import { Col, Row } from 'antd';
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';
export interface IRadarProps {
export interface RadarProps {
title?: React.ReactNode;
height?: number;
padding?: [number, number, number, number];
hasLegend?: boolean;
data: Array<{
data: {
name: string;
label: string;
value: string | number;
}>;
}[];
colors?: string[];
animate?: boolean;
forceFit?: boolean;
tickCount?: number;
style?: React.CSSProperties;
}
interface IRadarState {
legendData: Array<{
interface RadarState {
legendData: {
checked: boolean;
name: string;
color: string;
percent: number;
value: string;
}>;
}[];
}
/* eslint react/no-danger:0 */
class Radar extends Component<IRadarProps, IRadarState> {
state: IRadarState = {
class Radar extends Component<RadarProps, RadarState> {
state: RadarState = {
legendData: [],
};
chart: G2.Chart | undefined;
node: HTMLDivElement | undefined;
chart: G2.Chart | undefined = undefined;
node: HTMLDivElement | undefined = undefined;
componentDidMount() {
this.getLegendData();
}
componentDidUpdate(preProps: IRadarProps) {
componentDidUpdate(preProps: RadarProps) {
const { data } = this.props;
if (data !== preProps.data) {
this.getLegendData();
......@@ -57,10 +60,10 @@ class Radar extends Component<IRadarProps, IRadarState> {
if (!this.chart) return;
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return;
const items = geom.get('dataArray') || []; // 获取图形对应的
const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item: { color: any; _origin: any }[]) => {
// eslint-disable-next-line
// eslint-disable-next-line no-underscore-dangle
const origins = item.map(t => t._origin);
const result = {
name: origins[0].name,
......@@ -97,7 +100,7 @@ class Radar extends Component<IRadarProps, IRadarState> {
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.filter('name', val => filteredLegendData.indexOf(`${val}`) > -1);
this.chart.repaint();
}
......
export interface ITag {
export interface TagType {
key: string;
label: string;
}
export interface IProvince {
export interface ProvinceType {
label: string;
key: string;
}
export interface ICity {
export interface CityType {
label: string;
key: string;
}
export interface IGeographic {
province: IProvince;
city: ICity;
export interface GeographicType {
province: ProvinceType;
city: CityType;
}
export interface INotice {
export interface NoticeType {
id: string;
title: string;
logo: string;
......@@ -29,30 +29,30 @@ export interface INotice {
memberLink: string;
}
export interface ICurrentUser {
export interface CurrentUser {
name: string;
avatar: string;
userid: string;
notice: INotice[];
notice: NoticeType[];
email: string;
signature: string;
title: string;
group: string;
tags: ITag[];
tags: TagType[];
notifyCount: number;
unreadCount: number;
country: string;
geographic: IGeographic;
geographic: GeographicType;
address: string;
phone: string;
}
export interface IMember {
export interface Member {
avatar: string;
name: string;
id: string;
}
export interface IActivities {
export interface ActivitiesType {
id: string;
updatedAt: string;
user: {
......@@ -71,7 +71,7 @@ export interface IActivities {
template: string;
}
export interface IRadarData {
export interface RadarDataType {
label: string;
name: string;
value: number;
......
import { Avatar, Card, Col, List, Row } from 'antd';
import React, { PureComponent } from 'react';
import moment from 'moment';
import { connect } from 'dva';
import Link from 'umi/link';
import { Row, Col, Card, List, Avatar } from 'antd';
import { Dispatch } from 'redux';
import EditableLinkGroup from './components/EditableLinkGroup';
import { Dispatch } from 'redux';
import Link from 'umi/link';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect } from 'dva';
import moment from 'moment';
import Radar from './components/Radar';
import { ModalState } from './model';
import { ICurrentUser, IActivities, IRadarData, INotice } from './data';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import EditableLinkGroup from './components/EditableLinkGroup';
import styles from './style.less';
import { ActivitiesType, CurrentUser, NoticeType, RadarDataType } from './data.d';
const links = [
{
......@@ -41,10 +40,10 @@ const links = [
];
interface BLOCK_NAME_CAMEL_CASEProps {
currentUser: ICurrentUser;
projectNotice: INotice[];
activities: IActivities[];
radarData: IRadarData[];
currentUser: CurrentUser;
projectNotice: NoticeType[];
activities: ActivitiesType[];
radarData: RadarDataType[];
dispatch: Dispatch<any>;
currentUserLoading: boolean;
projectLoading: boolean;
......@@ -83,7 +82,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<BLOCK_NAME_CAMEL_CASEProp
});
}
renderActivities(item: IActivities) {
renderActivities = (item: ActivitiesType) => {
const events = item.template.split(/@\{([^{}]*)\}/gi).map(key => {
if (item[key]) {
return (
......@@ -113,7 +112,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<BLOCK_NAME_CAMEL_CASEProp
/>
</List.Item>
);
}
};
render() {
const {
......@@ -207,11 +206,9 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<BLOCK_NAME_CAMEL_CASEProp
title="动态"
loading={activitiesLoading}
>
<List<IActivities>
<List<ActivitiesType>
loading={activitiesLoading}
renderItem={item => {
return this.renderActivities(item);
}}
renderItem={item => this.renderActivities(item)}
dataSource={activities}
className={styles.activitiesList}
size="large"
......
import { AnyAction, Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { Reducer, AnyAction } from 'redux';
import { queryCurrent, queryProjectNotice, queryActivities, fakeChartData } from './service';
import { CurrentUser, Notice, Activeties, RadarData } from './data';
import { ActivitiesType, CurrentUser, NoticeType, RadarDataType } from './data.d';
import { fakeChartData, queryActivities, queryCurrent, queryProjectNotice } from './service';
export interface ModalState {
currentUser: Partial<CurrentUser>;
projectNotice: Notice[];
activities: Activeties[];
radarData: RadarData[];
projectNotice: NoticeType[];
activities: ActivitiesType[];
radarData: RadarDataType[];
}
export type Effect = (
......
......@@ -7,6 +7,7 @@ flow
```sh
umi block add ant-design-pro/flow
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
......
......@@ -12,6 +12,7 @@
"dev": "umi dev"
},
"dependencies": {
"@ant-design/pro-layout": "^4.5.5",
"@antv/data-set": "^0.10.2",
"antd": "^3.16.3",
"bizcharts": "^3.5.3-beta.0",
......@@ -22,13 +23,13 @@
"numeral": "^2.0.6",
"react": "^16.6.3",
"react-fittext": "^1.0.0",
"umi": "^2.6.9",
"umi-plugin-react": "^1.7.2",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.6.9",
"umi-plugin-block-dev": "^1.1.0",
"umi-plugin-react": "^1.7.2",
"@types/numeral": "^0.0.25"
"@types/numeral": "^0.0.25",
"umi-plugin-block-dev": "^1.1.0"
},
"blockConfig": {
"specVersion": "0.1"
......
import { CanvasMenu, ContextMenu, EdgeMenu, GroupMenu, MultiMenu, NodeMenu } from 'gg-editor';
import React from 'react';
import { NodeMenu, EdgeMenu, GroupMenu, MultiMenu, CanvasMenu, ContextMenu } from 'gg-editor';
import MenuItem from './MenuItem';
import styles from './index.less';
const FlowContextMenu = () => {
return (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
</NodeMenu>
<EdgeMenu>
<MenuItem command="delete" />
</EdgeMenu>
<GroupMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
<MenuItem command="unGroup" icon="ungroup" text="Ungroup" />
</GroupMenu>
<MultiMenu>
<MenuItem command="copy" />
<MenuItem command="paste" />
<MenuItem command="addGroup" icon="group" text="Add Group" />
<MenuItem command="delete" />
</MultiMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
<MenuItem command="pasteHere" icon="paste" text="Paste Here" />
</CanvasMenu>
</ContextMenu>
);
};
const FlowContextMenu = () => (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
</NodeMenu>
<EdgeMenu>
<MenuItem command="delete" />
</EdgeMenu>
<GroupMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
<MenuItem command="unGroup" icon="ungroup" text="Ungroup" />
</GroupMenu>
<MultiMenu>
<MenuItem command="copy" />
<MenuItem command="paste" />
<MenuItem command="addGroup" icon="group" text="Add Group" />
<MenuItem command="delete" />
</MultiMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
<MenuItem command="pasteHere" icon="paste" text="Paste Here" />
</CanvasMenu>
</ContextMenu>
);
export default FlowContextMenu;
import React from 'react';
import { Command } from 'gg-editor';
import React from 'react';
import IconFont from '../../common/IconFont';
import styles from './index.less';
const upperFirst = (str: string) => {
return str.toLowerCase().replace(/( |^)[a-z]/g, (l: string) => l.toUpperCase());
};
const upperFirst = (str: string) =>
str.toLowerCase().replace(/( |^)[a-z]/g, (l: string) => l.toUpperCase());
interface MenuItemProps {
command: string;
......
import { CanvasMenu, ContextMenu, NodeMenu } from 'gg-editor';
import React from 'react';
import { NodeMenu, CanvasMenu, ContextMenu } from 'gg-editor';
import MenuItem from './MenuItem';
import styles from './index.less';
const MindContextMenu = () => {
return (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="append" text="Topic" />
<MenuItem command="appendChild" icon="append-child" text="Subtopic" />
<MenuItem command="collapse" text="Fold" />
<MenuItem command="expand" text="Unfold" />
<MenuItem command="delete" />
</NodeMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
</CanvasMenu>
</ContextMenu>
);
};
const MindContextMenu = () => (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="append" text="Topic" />
<MenuItem command="appendChild" icon="append-child" text="Subtopic" />
<MenuItem command="collapse" text="Fold" />
<MenuItem command="expand" text="Unfold" />
<MenuItem command="delete" />
</NodeMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
</CanvasMenu>
</ContextMenu>
);
export default MindContextMenu;
import FlowContextMenu from './FlowContextMenu';
import MindContextMenu from './MindContextMenu';
import KoniContextMenu from './KoniContextMenu';
import MindContextMenu from './MindContextMenu';
export { FlowContextMenu, MindContextMenu, KoniContextMenu };
import React, { Fragment } from 'react';
import { Card, Form, Input, Select } from 'antd';
import { withPropsAPI } from 'gg-editor';
import React, { Fragment } from 'react';
import { FormComponentProps } from 'antd/es/form';
import { withPropsAPI } from 'gg-editor';
const upperFirst = (str: string) => {
return str.toLowerCase().replace(/( |^)[a-z]/g, (l: string) => l.toUpperCase());
};
const upperFirst = (str: string) =>
str.toLowerCase().replace(/( |^)[a-z]/g, (l: string) => l.toUpperCase());
const { Item } = Form;
const { Option } = Select;
......@@ -60,15 +60,13 @@ class DetailForm extends React.Component<DetailFormProps> {
}, 0);
};
renderEdgeShapeSelect = () => {
return (
<Select onChange={this.handleSubmit}>
<Option value="flow-smooth">Smooth</Option>
<Option value="flow-polyline">Polyline</Option>
<Option value="flow-polyline-round">Polyline Round</Option>
</Select>
);
};
renderEdgeShapeSelect = () => (
<Select onChange={this.handleSubmit}>
<Option value="flow-smooth">Smooth</Option>
<Option value="flow-polyline">Polyline</Option>
<Option value="flow-polyline-round">Polyline Round</Option>
</Select>
);
renderNodeDetail = () => {
const { form } = this.props;
......
import React from 'react';
import { CanvasPanel, DetailPanel, EdgePanel, GroupPanel, MultiPanel, NodePanel } from 'gg-editor';
import { Card } from 'antd';
import { NodePanel, EdgePanel, GroupPanel, MultiPanel, CanvasPanel, DetailPanel } from 'gg-editor';
import React from 'react';
import DetailForm from './DetailForm';
import styles from './index.less';
const FlowDetailPanel = () => {
return (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<EdgePanel>
<DetailForm type="edge" />
</EdgePanel>
<GroupPanel>
<DetailForm type="group" />
</GroupPanel>
<MultiPanel>
<Card type="inner" size="small" title="Multi Select" bordered={false} />
</MultiPanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
};
const FlowDetailPanel = () => (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<EdgePanel>
<DetailForm type="edge" />
</EdgePanel>
<GroupPanel>
<DetailForm type="group" />
</GroupPanel>
<MultiPanel>
<Card type="inner" size="small" title="Multi Select" bordered={false} />
</MultiPanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
export default FlowDetailPanel;
import React from 'react';
import { CanvasPanel, DetailPanel, NodePanel } from 'gg-editor';
import { Card } from 'antd';
import { NodePanel, CanvasPanel, DetailPanel } from 'gg-editor';
import React from 'react';
import DetailForm from './DetailForm';
import styles from './index.less';
const MindDetailPanel = () => {
return (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
};
const MindDetailPanel = () => (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
export default MindDetailPanel;
import FlowDetailPanel from './FlowDetailPanel';
import MindDetailPanel from './MindDetailPanel';
import KoniDetailPanel from './KoniDetailPanel';
import MindDetailPanel from './MindDetailPanel';
export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel };
import React from 'react';
import { Item, ItemPanel } from 'gg-editor';
import { Card } from 'antd';
import { ItemPanel, Item } from 'gg-editor';
import React from 'react';
import styles from './index.less';
const FlowItemPanel = () => {
return (
<ItemPanel className={styles.itemPanel}>
<Card bordered={false}>
<Item
type="node"
size="72*72"
shape="flow-circle"
model={{
color: '#FA8C16',
label: 'Start',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxjaXJjbGUgaWQ9ImIiIGN4PSIzNiIgY3k9IjM2IiByPSIzNiIvPjxmaWx0ZXIgeD0iLTkuNyUiIHk9Ii02LjklIiB3aWR0aD0iMTE5LjQlIiBoZWlnaHQ9IjExOS40JSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iYSI+PGZlT2Zmc2V0IGR5PSIyIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiLz48ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMDQgMCIgaW49InNoYWRvd0JsdXJPdXRlcjEiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0IDIpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGwtb3BhY2l0eT0iLjkyIiBmaWxsPSIjRkZGMkU4IiB4bGluazpocmVmPSIjYiIvPjxjaXJjbGUgc3Ryb2tlPSIjRkZDMDY5IiBjeD0iMzYiIGN5PSIzNiIgcj0iMzUuNSIvPjwvZz48dGV4dCBmb250LWZhbWlseT0iUGluZ0ZhbmdTQy1SZWd1bGFyLCBQaW5nRmFuZyBTQyIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQgMikiPjx0c3BhbiB4PSIyMyIgeT0iNDEiPlN0YXJ0PC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
<Item
type="node"
size="80*48"
shape="flow-rect"
model={{
color: '#1890FF',
label: 'Normal',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iNTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxyZWN0IGlkPSJiIiB4PSIwIiB5PSIwIiB3aWR0aD0iODAiIGhlaWdodD0iNDgiIHJ4PSI0Ii8+PGZpbHRlciB4PSItOC44JSIgeT0iLTEwLjQlIiB3aWR0aD0iMTE3LjUlIiBoZWlnaHQ9IjEyOS4yJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iYSI+PGZlT2Zmc2V0IGR5PSIyIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiLz48ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMDQgMCIgaW49InNoYWRvd0JsdXJPdXRlcjEiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0IDIpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGwtb3BhY2l0eT0iLjkyIiBmaWxsPSIjRTZGN0ZGIiB4bGluazpocmVmPSIjYiIvPjxyZWN0IHN0cm9rZT0iIzE4OTBGRiIgeD0iLjUiIHk9Ii41IiB3aWR0aD0iNzkiIGhlaWdodD0iNDciIHJ4PSI0Ii8+PC9nPjx0ZXh0IGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLVJlZ3VsYXIsIFBpbmdGYW5nIFNDIiBmb250LXNpemU9IjEyIiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii42NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNCAyKSI+PHRzcGFuIHg9IjIxIiB5PSIyOSI+Tm9ybWFsPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
<Item
type="node"
size="80*72"
shape="flow-rhombus"
model={{
color: '#13C2C2',
label: 'Decision',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODYiIGhlaWdodD0iNzgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik00Mi42NyAxLjY3bDM0Ljk2NSAzMS4zNTJhNCA0IDAgMCAxIDAgNS45NTZMNDIuNjcgNzAuMzNhNCA0IDAgMCAxLTUuMzQgMEwyLjM2NSAzOC45NzhhNCA0IDAgMCAxIDAtNS45NTZMMzcuMzMgMS42N2E0IDQgMCAwIDEgNS4zNCAweiIgaWQ9ImIiLz48ZmlsdGVyIHg9Ii04LjglIiB5PSItNi45JSIgd2lkdGg9IjExNy41JSIgaGVpZ2h0PSIxMTkuNCUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImEiPjxmZU9mZnNldCBkeT0iMiIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd09mZnNldE91dGVyMSIvPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSIvPjxmZUNvbXBvc2l0ZSBpbj0ic2hhZG93Qmx1ck91dGVyMSIgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9Im91dCIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA0IDAiIGluPSJzaGFkb3dCbHVyT3V0ZXIxIi8+PC9maWx0ZXI+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMyAxKSI+PHVzZSBmaWxsPSIjMDAwIiBmaWx0ZXI9InVybCgjYSkiIHhsaW5rOmhyZWY9IiNiIi8+PHVzZSBmaWxsLW9wYWNpdHk9Ii45MiIgZmlsbD0iI0U2RkZGQiIgeGxpbms6aHJlZj0iI2IiLz48cGF0aCBzdHJva2U9IiM1Q0RCRDMiIGQ9Ik00Mi4zMzcgMi4wNDJhMy41IDMuNSAwIDAgMC00LjY3NCAwTDIuNjk4IDMzLjM5NGEzLjUgMy41IDAgMCAwIDAgNS4yMTJsMzQuOTY1IDMxLjM1MmEzLjUgMy41IDAgMCAwIDQuNjc0IDBsMzQuOTY1LTMxLjM1MmEzLjUgMy41IDAgMCAwIDAtNS4yMTJMNDIuMzM3IDIuMDQyeiIvPjwvZz48dGV4dCBmb250LWZhbWlseT0iUGluZ0ZhbmdTQy1SZWd1bGFyLCBQaW5nRmFuZyBTQyIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMgMSkiPjx0c3BhbiB4PSIxOCIgeT0iNDIiPkRlY2lzaW9uPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
<Item
type="node"
size="80*48"
shape="flow-capsule"
model={{
color: '#722ED1',
label: 'Model',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iNTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxyZWN0IGlkPSJiIiB4PSIwIiB5PSIwIiB3aWR0aD0iODAiIGhlaWdodD0iNDgiIHJ4PSIyNCIvPjxmaWx0ZXIgeD0iLTguOCUiIHk9Ii0xMC40JSIgd2lkdGg9IjExNy41JSIgaGVpZ2h0PSIxMjkuMiUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImEiPjxmZU9mZnNldCBkeT0iMiIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd09mZnNldE91dGVyMSIvPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSIvPjxmZUNvbXBvc2l0ZSBpbj0ic2hhZG93Qmx1ck91dGVyMSIgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9Im91dCIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA0IDAiIGluPSJzaGFkb3dCbHVyT3V0ZXIxIi8+PC9maWx0ZXI+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNCAyKSI+PHVzZSBmaWxsPSIjMDAwIiBmaWx0ZXI9InVybCgjYSkiIHhsaW5rOmhyZWY9IiNiIi8+PHVzZSBmaWxsLW9wYWNpdHk9Ii45MiIgZmlsbD0iI0Y5RjBGRiIgeGxpbms6aHJlZj0iI2IiLz48cmVjdCBzdHJva2U9IiNCMzdGRUIiIHg9Ii41IiB5PSIuNSIgd2lkdGg9Ijc5IiBoZWlnaHQ9IjQ3IiByeD0iMjMuNSIvPjwvZz48dGV4dCBmb250LWZhbWlseT0iUGluZ0ZhbmdTQy1SZWd1bGFyLCBQaW5nRmFuZyBTQyIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQgMikiPjx0c3BhbiB4PSIyNCIgeT0iMjkiPk1vZGVsPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
</Card>
</ItemPanel>
);
};
const FlowItemPanel = () => (
<ItemPanel className={styles.itemPanel}>
<Card bordered={false}>
<Item
type="node"
size="72*72"
shape="flow-circle"
model={{
color: '#FA8C16',
label: 'Start',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxjaXJjbGUgaWQ9ImIiIGN4PSIzNiIgY3k9IjM2IiByPSIzNiIvPjxmaWx0ZXIgeD0iLTkuNyUiIHk9Ii02LjklIiB3aWR0aD0iMTE5LjQlIiBoZWlnaHQ9IjExOS40JSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iYSI+PGZlT2Zmc2V0IGR5PSIyIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiLz48ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMDQgMCIgaW49InNoYWRvd0JsdXJPdXRlcjEiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0IDIpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGwtb3BhY2l0eT0iLjkyIiBmaWxsPSIjRkZGMkU4IiB4bGluazpocmVmPSIjYiIvPjxjaXJjbGUgc3Ryb2tlPSIjRkZDMDY5IiBjeD0iMzYiIGN5PSIzNiIgcj0iMzUuNSIvPjwvZz48dGV4dCBmb250LWZhbWlseT0iUGluZ0ZhbmdTQy1SZWd1bGFyLCBQaW5nRmFuZyBTQyIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQgMikiPjx0c3BhbiB4PSIyMyIgeT0iNDEiPlN0YXJ0PC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
<Item
type="node"
size="80*48"
shape="flow-rect"
model={{
color: '#1890FF',
label: 'Normal',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iNTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxyZWN0IGlkPSJiIiB4PSIwIiB5PSIwIiB3aWR0aD0iODAiIGhlaWdodD0iNDgiIHJ4PSI0Ii8+PGZpbHRlciB4PSItOC44JSIgeT0iLTEwLjQlIiB3aWR0aD0iMTE3LjUlIiBoZWlnaHQ9IjEyOS4yJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iYSI+PGZlT2Zmc2V0IGR5PSIyIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiLz48ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMDQgMCIgaW49InNoYWRvd0JsdXJPdXRlcjEiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0IDIpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGwtb3BhY2l0eT0iLjkyIiBmaWxsPSIjRTZGN0ZGIiB4bGluazpocmVmPSIjYiIvPjxyZWN0IHN0cm9rZT0iIzE4OTBGRiIgeD0iLjUiIHk9Ii41IiB3aWR0aD0iNzkiIGhlaWdodD0iNDciIHJ4PSI0Ii8+PC9nPjx0ZXh0IGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLVJlZ3VsYXIsIFBpbmdGYW5nIFNDIiBmb250LXNpemU9IjEyIiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii42NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNCAyKSI+PHRzcGFuIHg9IjIxIiB5PSIyOSI+Tm9ybWFsPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
<Item
type="node"
size="80*72"
shape="flow-rhombus"
model={{
color: '#13C2C2',
label: 'Decision',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODYiIGhlaWdodD0iNzgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik00Mi42NyAxLjY3bDM0Ljk2NSAzMS4zNTJhNCA0IDAgMCAxIDAgNS45NTZMNDIuNjcgNzAuMzNhNCA0IDAgMCAxLTUuMzQgMEwyLjM2NSAzOC45NzhhNCA0IDAgMCAxIDAtNS45NTZMMzcuMzMgMS42N2E0IDQgMCAwIDEgNS4zNCAweiIgaWQ9ImIiLz48ZmlsdGVyIHg9Ii04LjglIiB5PSItNi45JSIgd2lkdGg9IjExNy41JSIgaGVpZ2h0PSIxMTkuNCUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImEiPjxmZU9mZnNldCBkeT0iMiIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd09mZnNldE91dGVyMSIvPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSIvPjxmZUNvbXBvc2l0ZSBpbj0ic2hhZG93Qmx1ck91dGVyMSIgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9Im91dCIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA0IDAiIGluPSJzaGFkb3dCbHVyT3V0ZXIxIi8+PC9maWx0ZXI+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMyAxKSI+PHVzZSBmaWxsPSIjMDAwIiBmaWx0ZXI9InVybCgjYSkiIHhsaW5rOmhyZWY9IiNiIi8+PHVzZSBmaWxsLW9wYWNpdHk9Ii45MiIgZmlsbD0iI0U2RkZGQiIgeGxpbms6aHJlZj0iI2IiLz48cGF0aCBzdHJva2U9IiM1Q0RCRDMiIGQ9Ik00Mi4zMzcgMi4wNDJhMy41IDMuNSAwIDAgMC00LjY3NCAwTDIuNjk4IDMzLjM5NGEzLjUgMy41IDAgMCAwIDAgNS4yMTJsMzQuOTY1IDMxLjM1MmEzLjUgMy41IDAgMCAwIDQuNjc0IDBsMzQuOTY1LTMxLjM1MmEzLjUgMy41IDAgMCAwIDAtNS4yMTJMNDIuMzM3IDIuMDQyeiIvPjwvZz48dGV4dCBmb250LWZhbWlseT0iUGluZ0ZhbmdTQy1SZWd1bGFyLCBQaW5nRmFuZyBTQyIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMgMSkiPjx0c3BhbiB4PSIxOCIgeT0iNDIiPkRlY2lzaW9uPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
<Item
type="node"
size="80*48"
shape="flow-capsule"
model={{
color: '#722ED1',
label: 'Model',
}}
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iNTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxyZWN0IGlkPSJiIiB4PSIwIiB5PSIwIiB3aWR0aD0iODAiIGhlaWdodD0iNDgiIHJ4PSIyNCIvPjxmaWx0ZXIgeD0iLTguOCUiIHk9Ii0xMC40JSIgd2lkdGg9IjExNy41JSIgaGVpZ2h0PSIxMjkuMiUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImEiPjxmZU9mZnNldCBkeT0iMiIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd09mZnNldE91dGVyMSIvPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSIvPjxmZUNvbXBvc2l0ZSBpbj0ic2hhZG93Qmx1ck91dGVyMSIgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9Im91dCIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA0IDAiIGluPSJzaGFkb3dCbHVyT3V0ZXIxIi8+PC9maWx0ZXI+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNCAyKSI+PHVzZSBmaWxsPSIjMDAwIiBmaWx0ZXI9InVybCgjYSkiIHhsaW5rOmhyZWY9IiNiIi8+PHVzZSBmaWxsLW9wYWNpdHk9Ii45MiIgZmlsbD0iI0Y5RjBGRiIgeGxpbms6aHJlZj0iI2IiLz48cmVjdCBzdHJva2U9IiNCMzdGRUIiIHg9Ii41IiB5PSIuNSIgd2lkdGg9Ijc5IiBoZWlnaHQ9IjQ3IiByeD0iMjMuNSIvPjwvZz48dGV4dCBmb250LWZhbWlseT0iUGluZ0ZhbmdTQy1SZWd1bGFyLCBQaW5nRmFuZyBTQyIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQgMikiPjx0c3BhbiB4PSIyNCIgeT0iMjkiPk1vZGVsPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg=="
/>
</Card>
</ItemPanel>
);
export default FlowItemPanel;
import React from 'react';
import { Card } from 'antd';
import { Minimap } from 'gg-editor';
import React from 'react';
const EditorMinimap = () => {
return (
<Card type="inner" size="small" title="Minimap" bordered={false}>
<Minimap height={200} />
</Card>
);
};
const EditorMinimap = () => (
<Card type="inner" size="small" title="Minimap" bordered={false}>
<Minimap height={200} />
</Card>
);
export default EditorMinimap;
import React from 'react';
import { Divider } from 'antd';
import React from 'react';
import { Toolbar } from 'gg-editor';
import ToolbarButton from './ToolbarButton';
import styles from './index.less';
const FlowToolbar = () => {
return (
<Toolbar className={styles.toolbar}>
<ToolbarButton command="undo" />
<ToolbarButton command="redo" />
<Divider type="vertical" />
<ToolbarButton command="copy" />
<ToolbarButton command="paste" />
<ToolbarButton command="delete" />
<Divider type="vertical" />
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" />
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" />
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" />
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" />
<Divider type="vertical" />
<ToolbarButton command="toBack" icon="to-back" text="To Back" />
<ToolbarButton command="toFront" icon="to-front" text="To Front" />
<Divider type="vertical" />
<ToolbarButton command="multiSelect" icon="multi-select" text="Multi Select" />
<ToolbarButton command="addGroup" icon="group" text="Add Group" />
<ToolbarButton command="unGroup" icon="ungroup" text="Ungroup" />
</Toolbar>
);
};
const FlowToolbar = () => (
<Toolbar className={styles.toolbar}>
<ToolbarButton command="undo" />
<ToolbarButton command="redo" />
<Divider type="vertical" />
<ToolbarButton command="copy" />
<ToolbarButton command="paste" />
<ToolbarButton command="delete" />
<Divider type="vertical" />
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" />
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" />
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" />
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" />
<Divider type="vertical" />
<ToolbarButton command="toBack" icon="to-back" text="To Back" />
<ToolbarButton command="toFront" icon="to-front" text="To Front" />
<Divider type="vertical" />
<ToolbarButton command="multiSelect" icon="multi-select" text="Multi Select" />
<ToolbarButton command="addGroup" icon="group" text="Add Group" />
<ToolbarButton command="unGroup" icon="ungroup" text="Ungroup" />
</Toolbar>
);
export default FlowToolbar;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment