Browse Source

优化一些小问题

pull/11096/head
期贤 2 years ago
parent
commit
690e210221
  1. 56
      package.json
  2. 14209
      pnpm-lock.yaml
  3. 3
      src/components/RightContent/AvatarDropdown.tsx
  4. 122
      src/pages/dashboard/analysis/components/Charts/Bar/index.tsx
  5. 179
      src/pages/dashboard/analysis/components/Charts/Gauge/index.tsx
  6. 141
      src/pages/dashboard/analysis/components/Charts/MiniArea/index.tsx
  7. 59
      src/pages/dashboard/analysis/components/Charts/MiniBar/index.tsx
  8. 305
      src/pages/dashboard/analysis/components/Charts/Pie/index.tsx
  9. 209
      src/pages/dashboard/analysis/components/Charts/TagCloud/index.tsx
  10. 149
      src/pages/dashboard/analysis/components/Charts/TimelineChart/index.tsx
  11. 8
      src/pages/dashboard/analysis/components/Trend/index.style.ts
  12. 180
      src/pages/dashboard/monitor/components/Charts/Gauge/index.tsx
  13. 141
      src/pages/dashboard/monitor/components/Charts/MiniArea/index.tsx
  14. 311
      src/pages/dashboard/monitor/components/Charts/Pie/index.tsx
  15. 209
      src/pages/dashboard/monitor/components/Charts/TagCloud/index.tsx
  16. 174
      src/pages/dashboard/workplace/components/Radar/index.tsx
  17. 20
      src/pages/list/search/applications/components/TagSelect/index.tsx
  18. 22
      src/pages/list/search/articles/components/TagSelect/index.tsx
  19. 22
      src/pages/list/search/projects/components/TagSelect/index.tsx
  20. 2
      src/typings.d.ts

56
package.json

@ -39,63 +39,53 @@
],
"dependencies": {
"@ant-design/icons": "^4.8.1",
"@ant-design/plots": "^2.0.0-beta.1",
"@ant-design/use-emotion-css": "1.0.4",
"@antv/data-set": "^0.11.8",
"@antv/l7": "^2.18.3",
"@antv/l7-maps": "^2.18.3",
"@ant-design/plots": "^2.1.0",
"@ant-design/pro-components": "^2.6.43",
"@antv/l7": "^2.20.5",
"@antv/l7-maps": "^2.20.5",
"@antv/l7-react": "^2.4.3",
"ahooks": "^2.10.14",
"antd": "^5.9.4",
"@ant-design/pro-components": "^2.6.28",
"@umijs/route-utils": "^2.2.2",
"antd": "^5.12.1",
"antd-style": "^3.5.2",
"classnames": "^2.3.2",
"dayjs": "^1.11.10",
"gg-editor": "^2.0.4",
"lodash": "^4.17.21",
"lodash-decorators": "^6.0.1",
"numeral": "^2.0.6",
"nzh": "^1.0.11",
"omit.js": "^2.0.2",
"querystring": "^0.2.1",
"rc-menu": "^9.12.0",
"rc-util": "^5.37.0",
"rc-util": "^5.38.1",
"react": "^18.2.0",
"react-dev-inspector": "^1.9.0",
"react-dom": "^18.2.0",
"react-fittext": "^1.0.0",
"react-helmet-async": "^1.3.0",
"react-router": "^4.3.1"
"react-fittext": "^1.0.0"
},
"devDependencies": {
"@types/lodash.debounce": "^4.0.7",
"devDependencies": {
"@ant-design/pro-cli": "^2.1.5",
"@testing-library/react": "^13.4.0",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/classnames": "^2.3.1",
"@types/express": "^4.17.18",
"@types/express": "^4.17.21",
"@types/history": "^4.7.11",
"@types/jest": "^29.5.5",
"@types/lodash": "^4.14.199",
"@types/react": "^18.2.25",
"@types/react-dom": "^18.2.10",
"@types/react-helmet": "^6.1.7",
"@types/jest": "^29.5.11",
"@types/lodash": "^4.14.202",
"@types/lodash.debounce": "^4.0.9",
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"@types/react-helmet": "^6.1.11",
"@umijs/fabric": "^2.14.1",
"@umijs/lint": "^4.0.83",
"@umijs/max": "^4.0.83",
"@umijs/lint": "^4.0.89",
"@umijs/max": "^4.0.89",
"cross-env": "^7.0.3",
"eslint": "^8.50.0",
"eslint": "^8.55.0",
"express": "^4.18.2",
"gh-pages": "^3.2.3",
"husky": "^7.0.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"mockjs": "^1.1.0",
"prettier": "^3.0.3",
"prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-packagejson": "^2.4.6",
"prettier": "^3.1.0",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-packagejson": "^2.4.7",
"prettier-plugin-two-style-order": "^1.0.1",
"react-dev-inspector": "^1.9.0",
"swagger-ui-dist": "^4.19.1",
"ts-node": "^10.9.1",
"typescript": "^4.9.5",

14209
pnpm-lock.yaml

File diff suppressed because it is too large

3
src/components/RightContent/AvatarDropdown.tsx

@ -4,7 +4,6 @@ import { history, useModel } from '@umijs/max';
import { Spin } from 'antd';
import { createStyles } from 'antd-style';
import { stringify } from 'querystring';
import type { MenuInfo } from 'rc-menu/lib/interface';
import React, { useCallback } from 'react';
import { flushSync } from 'react-dom';
import HeaderDropdown from '../HeaderDropdown';
@ -63,7 +62,7 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, childre
const { initialState, setInitialState } = useModel('@@initialState');
const onMenuClick = useCallback(
(event: MenuInfo) => {
(event: any) => {
const { key } = event;
if (key === 'logout') {
flushSync(() => {

122
src/pages/dashboard/analysis/components/Charts/Bar/index.tsx

@ -1,122 +0,0 @@
import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import Debounce from 'lodash.debounce';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import useStyles from '../index.style';
export type BarProps = {
title: React.ReactNode;
color?: string;
padding?: [number, number, number, number];
height?: number;
data: {
x: string;
y: number;
}[];
forceFit?: boolean;
autoLabel?: boolean;
style?: React.CSSProperties;
};
const Bar: React.FC<BarProps> = ({
height = 1,
title,
forceFit = true,
data,
color = 'rgba(24, 144, 255, 0.85)',
padding,
autoLabel = true,
}) => {
const { styles } = useStyles();
const [autoHideXLabels, setAutoHideXLabels] = useState(false);
const rootRef = useRef<HTMLDivElement | null>(null);
const nodeRef = useRef<HTMLDivElement | null>(null);
const resize = useCallback(
Debounce(() => {
if (!nodeRef.current || !nodeRef.current.parentNode) {
return;
}
const canvasWidth = nodeRef.current.parentNode?.clientWidth || 0;
if (!autoLabel) {
return;
}
const minWidth = data?.length * 30;
if (canvasWidth <= minWidth) {
if (!autoHideXLabels) {
setAutoHideXLabels(true);
}
} else if (autoHideXLabels) {
setAutoHideXLabels(false);
}
}, 500),
[autoHideXLabels, autoLabel, data?.length],
);
useEffect(() => {
window.addEventListener('resize', resize, {
passive: true,
});
return () => {
window.removeEventListener('resize', resize);
};
}, [resize]);
const handleRoot = useCallback((n: HTMLDivElement) => {
rootRef.current = n;
}, []);
const handleRef = useCallback((n: HTMLDivElement) => {
nodeRef.current = n;
}, []);
const scale = {
x: {
type: 'cat',
},
y: {
min: 0,
},
};
const tooltip = [
'x*y',
(x: string, y: string) => ({
name: x,
value: y,
}),
];
return (
<div className={styles.chart} style={{ height }} ref={handleRoot}>
<div ref={handleRef}>
{title && (
<h4
style={{
marginBottom: 20,
}}
>
{title}
</h4>
)}
<Chart
scale={scale}
height={title ? height - 41 : height}
forceFit={forceFit}
data={data}
padding={padding || 'auto'}
>
<Axis
name="x"
title={false}
label={autoHideXLabels ? undefined : {}}
tickLine={autoHideXLabels ? undefined : {}}
/>
<Axis name="y" min={0} />
<Tooltip showTitle={false} crosshairs={false} />
<Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
</Chart>
</div>
</div>
);
};
export default Bar;

179
src/pages/dashboard/analysis/components/Charts/Gauge/index.tsx

@ -1,179 +0,0 @@
import { Axis, Chart, Coord, Geom, Guide, Shape } from 'bizcharts';
import React from 'react';
import autoHeight from '../autoHeight';
const { Arc, Html, Line } = Guide;
export type GaugeProps = {
title: React.ReactNode;
color?: string;
height?: number;
bgColor?: number;
percent: number;
forceFit?: boolean;
style?: React.CSSProperties;
formatter: (value: string) => string;
};
const defaultFormatter = (val: string): string => {
switch (val) {
case '2':
return '差';
case '4':
return '中';
case '6':
return '良';
case '8':
return '优';
default:
return '';
}
};
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 }];
const renderHtml = () => `
<div style="width: 300px;text-align: center;font-size: 12px!important;">
<div style="font-size: 14px; color: rgba(0,0,0,0.43);margin: 0;">${title}</div>
<div style="font-size: 24px;color: rgba(0,0,0,0.85);margin: 0;">
${(data[0].value * 10).toFixed(2)}%
</div>
</div>`;
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,
}}
/>
<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}
/>
</Chart>
);
};
export default autoHeight()(Gauge);

141
src/pages/dashboard/analysis/components/Charts/MiniArea/index.tsx

@ -1,141 +0,0 @@
import type { AxisProps } from 'bizcharts';
import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import React from 'react';
import autoHeight from '../autoHeight';
import useStyles from '../index.style';
export type MiniAreaProps = {
color?: string;
height?: number;
borderColor?: string;
line?: boolean;
animate?: boolean;
xAxis?: AxisProps;
forceFit?: boolean;
scale?: {
x?: {
tickCount: number;
};
y?: {
tickCount: number;
};
};
yAxis?: Partial<AxisProps>;
borderWidth?: number;
data: {
x: number | string;
y: number;
}[];
};
const MiniArea: React.FC<MiniAreaProps> = (props) => {
const { styles } = useStyles();
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 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 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={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="line"
position="x*y"
shape="smooth"
color={borderColor}
size={borderWidth}
tooltip={false}
/>
) : (
<span
style={{
display: 'none',
}}
/>
)}
</Chart>
)}
</div>
</div>
);
};
export default autoHeight()(MiniArea);

59
src/pages/dashboard/analysis/components/Charts/MiniBar/index.tsx

@ -1,59 +0,0 @@
import { Chart, Geom, Tooltip } from 'bizcharts';
import React from 'react';
import autoHeight from '../autoHeight';
import useStyles from '../index.style';
export type MiniBarProps = {
color?: string;
height?: number;
data: {
x: number | string;
y: number;
}[];
forceFit?: boolean;
style?: React.CSSProperties;
};
const MiniBar: React.FC<MiniBarProps> = (props) => {
const { styles } = useStyles();
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);

305
src/pages/dashboard/analysis/components/Charts/Pie/index.tsx

@ -1,305 +0,0 @@
import { DataView } from '@antv/data-set';
import { Divider } from 'antd';
import { Chart, Coord, Geom, Tooltip } from 'bizcharts';
import classNames from 'classnames';
import Debounce from 'lodash.debounce';
import React, { Component } from 'react';
import ReactFitText from 'react-fittext';
import autoHeight from '../autoHeight';
export type PieProps = {
animate?: boolean;
color?: string;
colors?: string[];
selected?: boolean;
height?: number;
margin?: [number, number, number, number];
hasLegend?: boolean;
padding?: [number, number, number, number];
percent?: number;
data?: {
x: string | string;
y: number;
}[];
inner?: number;
lineWidth?: number;
forceFit?: boolean;
style?: React.CSSProperties;
className?: string;
total?: React.ReactNode | number | (() => React.ReactNode | number);
title?: React.ReactNode;
tooltip?: boolean;
valueFormat?: (value: string) => string | React.ReactNode;
subTitle?: React.ReactNode;
};
type PieState = {
legendData: {
checked: boolean;
x: string;
color: string;
percent: number;
y: string;
}[];
legendBlock: boolean;
};
class Pie extends Component<PieProps, PieState> {
state: PieState = {
legendData: [],
legendBlock: false,
};
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(
'resize',
() => {
this.requestRef = requestAnimationFrame(() => this.resize());
},
{
passive: true,
},
);
}
componentDidUpdate(preProps: PieProps) {
const { data } = this.props;
if (data !== preProps.data) {
// because of charts data create when rendered
// so there is a trick for get rendered time
this.getLegendData();
}
}
componentWillUnmount() {
if (this.requestRef) {
window.cancelAnimationFrame(this.requestRef);
}
window.removeEventListener('resize', this.resize);
if (this.resize) {
(this.resize as any).cancel();
}
}
getG2Instance = (chart: G2.Chart) => {
this.chart = chart;
requestAnimationFrame(() => {
this.getLegendData();
this.resize();
});
};
// for custom lengend view
getLegendData = () => {
if (!this.chart) return;
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return;
const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map(
(
item: {
color: any;
_origin: any;
}[],
) => {
/* eslint no-underscore-dangle:0 */
const origin = item[0]._origin;
origin.color = item[0].color;
origin.checked = true;
return origin;
},
);
this.setState({
legendData,
});
};
handleRoot = (n: HTMLDivElement) => {
this.root = n;
};
handleLegendClick = (item: any, i: string | number) => {
const newItem = item;
newItem.checked = !newItem.checked;
const { legendData } = this.state;
const key = i as unknown as number;
legendData[key] = newItem;
const filteredLegendData = legendData.filter((l) => l.checked).map((l) => l.x);
if (this.chart) {
this.chart.filter('x', (val) => filteredLegendData.indexOf(`${val}`) > -1);
}
this.setState({
legendData,
});
};
render() {
const {
valueFormat,
subTitle,
total,
hasLegend = false,
className,
style,
height = 0,
forceFit = true,
percent,
color,
inner = 0.75,
animate = true,
colors,
lineWidth = 1,
} = this.props;
const { legendData, legendBlock } = this.state;
const pieClassName = classNames(styles.pie, className, {
[styles.hasLegend]: !!hasLegend,
[styles.legendBlock]: legendBlock,
});
const {
data: propsData,
selected: propsSelected = true,
tooltip: propsTooltip = true,
} = this.props;
let data = propsData || [];
let selected = propsSelected;
let tooltip = propsTooltip;
const defaultColors = colors;
data = data || [];
selected = selected || true;
tooltip = tooltip || true;
let formatColor;
const scale = {
x: {
type: 'cat',
range: [0, 1],
},
y: {
min: 0,
},
};
if (percent || percent === 0) {
selected = false;
tooltip = false;
formatColor = (value: string) => {
if (value === '占比') {
return color || 'rgba(24, 144, 255, 0.85)';
}
return '#F0F2F5';
};
data = [
{
x: '占比',
y: parseFloat(`${percent}`),
},
{
x: '反比',
y: 100 - parseFloat(`${percent}`),
},
];
}
const tooltipFormat: [
string,
(...args: any[]) => {
name?: string;
value: string;
},
] = [
'x*percent',
(x: string, p: number) => ({
name: x,
value: `${(p * 100).toFixed(2)}%`,
}),
];
const padding = [12, 0, 12, 0] as [number, number, number, number];
const dv = new DataView();
dv.source(data).transform({
type: 'percent',
field: 'y',
dimension: 'x',
as: 'percent',
});
return (
<div ref={this.handleRoot} className={pieClassName} style={style}>
<ReactFitText maxFontSize={25}>
<div className={styles.chart}>
<Chart
scale={scale}
height={height}
forceFit={forceFit}
data={dv}
padding={padding}
animate={animate}
onGetG2Instance={this.getG2Instance}
>
{!!tooltip && <Tooltip showTitle={false} />}
<Coord type="theta" innerRadius={inner} />
<Geom
style={{
lineWidth,
stroke: '#fff',
}}
tooltip={tooltip ? tooltipFormat : undefined}
type="intervalStack"
position="percent"
color={['x', percent || percent === 0 ? formatColor : defaultColors] as any}
selected={selected}
/>
</Chart>
{(subTitle || total) && (
<div className={styles.total}>
{subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
{/* eslint-disable-next-line */}
{total && (
<div className="pie-stat">{typeof total === 'function' ? total() : total}</div>
)}
</div>
)}
</div>
</ReactFitText>
{hasLegend && (
<ul className={styles.legend}>
{legendData.map((item, i) => (
<li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
<span
className={styles.dot}
style={{
backgroundColor: !item.checked ? '#aaa' : item.color,
}}
/>
<span className={styles.legendTitle}>{item.x}</span>
<Divider type="vertical" />
<span className={styles.percent}>
{`${(Number.isNaN(item.percent) ? 0 : item.percent * 100).toFixed(2)}%`}
</span>
<span className={styles.value}>{valueFormat ? valueFormat(item.y) : item.y}</span>
</li>
))}
</ul>
)}
</div>
);
}
}
export default autoHeight()(Pie);

209
src/pages/dashboard/analysis/components/Charts/TagCloud/index.tsx

@ -1,209 +0,0 @@
import DataSet from '@antv/data-set';
import { Chart, Coord, Geom, Shape, Tooltip } from 'bizcharts';
import classNames from 'classnames';
import Debounce from 'lodash.debounce';
import React, { Component } from 'react';
import autoHeight from '../autoHeight';
/* eslint no-underscore-dangle: 0 */
/* eslint no-param-reassign: 0 */
const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
export type TagCloudProps = {
data: {
name: string;
value: number;
}[];
height?: number;
className?: string;
style?: React.CSSProperties;
};
type TagCloudState = {
dv: any;
height?: number;
width: number;
};
class TagCloud extends Component<TagCloudProps, TagCloudState> {
state = {
dv: null,
height: 0,
width: 0,
};
isUnmount: boolean = false;
requestRef: number = 0;
root: HTMLDivElement | undefined = undefined;
imageMask: HTMLImageElement | undefined = undefined;
componentDidMount() {
requestAnimationFrame(() => {
this.initTagCloud();
this.renderChart(this.props);
});
window.addEventListener('resize', this.resize, {
passive: true,
});
}
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;
};
initTagCloud = () => {
function getTextAttrs(cfg: {
x?: any;
y?: any;
style?: any;
opacity?: any;
origin?: any;
color?: any;
}) {
return {
...cfg.style,
fillOpacity: cfg.opacity,
fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate,
text: cfg.origin._origin.text,
textAlign: 'center',
fontFamily: cfg.origin._origin.font,
fill: cfg.color,
textBaseline: 'Alphabetic',
};
}
(Shape as any).registerShape('point', 'cloud', {
drawShape(
cfg: {
x: any;
y: any;
},
container: {
addShape: (
arg0: string,
arg1: {
attrs: any;
},
) => void;
},
) {
const attrs = getTextAttrs(cfg);
return container.addShape('text', {
attrs: {
...attrs,
x: cfg.x,
y: cfg.y,
},
});
},
});
};
renderChart = Debounce((nextProps: TagCloudProps) => {
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const { data, height } = nextProps || this.props;
if (data.length < 1 || !this.root) {
return;
}
const h = height;
const w = this.root.offsetWidth;
const onload = () => {
const dv = new DataSet.View().source(data);
const range = dv.range('value');
const [min, max] = range;
dv.transform({
type: 'tag-cloud',
fields: ['name', 'value'],
imageMask: this.imageMask,
font: 'Verdana',
size: [w, h],
// 宽高设置最好根据 imageMask 做调整
padding: 0,
timeInterval: 5000,
// max execute time
rotate() {
return 0;
},
fontSize(d: { value: number }) {
const size = ((d.value - min) / (max - min)) ** 2;
return size * (17.5 - 5) + 5;
},
});
if (this.isUnmount) {
return;
}
this.setState({
dv,
width: w,
height: h,
});
};
if (!this.imageMask) {
this.imageMask = new Image();
this.imageMask.crossOrigin = '';
this.imageMask.src = imgUrl;
this.imageMask.onload = onload;
} else {
onload();
}
}, 500);
render() {
const { className, height } = this.props;
const { dv, width, height: stateHeight } = this.state;
return (
<div
className={classNames(styles.tagCloud, className)}
style={{
width: '100%',
height,
}}
ref={this.saveRootRef}
>
{dv && (
<Chart
width={width}
height={stateHeight}
data={dv}
padding={0}
scale={{
x: {
nice: false,
},
y: {
nice: false,
},
}}
>
<Tooltip showTitle={false} />
<Coord reflect="y" />
<Geom
type="point"
position="x*y"
color="text"
shape="cloud"
tooltip={[
'text*value',
function trans(text, value) {
return {
name: text,
value,
};
},
]}
/>
</Chart>
)}
</div>
);
}
}
export default autoHeight()(TagCloud);

149
src/pages/dashboard/analysis/components/Charts/TimelineChart/index.tsx

@ -1,149 +0,0 @@
import DataSet from '@antv/data-set';
import { Axis, Chart, Geom, Legend, Tooltip } from 'bizcharts';
import Slider from 'bizcharts-plugin-slider';
import React from 'react';
import autoHeight from '../autoHeight';
import useStyles from './index.style';
export type TimelineChartProps = {
data: {
x: number;
y1: number;
y2: number;
}[];
title?: string;
titleMap: {
y1: string;
y2: string;
};
padding?: [number, number, number, number];
height?: number;
style?: React.CSSProperties;
borderWidth?: number;
};
const TimelineChart: React.FC<TimelineChartProps> = (props) => {
const { styles } = useStyles();
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,
},
];
data.sort((a, b) => a.x - b.x);
let max;
if (data[0] && data[0].y1 && data[0].y2) {
max = Math.max(
[...data].sort((a, b) => b.y1 - a.y1)[0].y1,
[...data].sort((a, b) => b.y2 - a.y2)[0].y2,
);
}
const ds = new DataSet({
state: {
start: data[0].x,
end: data[data.length - 1].x,
},
});
const dv = ds.createView();
dv.source(data)
.transform({
type: 'filter',
callback: (obj: { 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 as 'y1'] = row.y1;
newRow[titleMap.y2 as 'y2'] = row.y2;
return newRow;
},
})
.transform({
type: 'fold',
fields: [titleMap.y1, titleMap.y2],
// 展开字段集
key: 'key',
// key字段
value: 'value', // value字段
});
const timeScale = {
type: 'time',
tickInterval: 60 * 60 * 1000,
mask: 'HH:mm',
range: [0, 1],
};
const cols = {
x: timeScale,
value: {
max,
min: 0,
},
};
const SliderGen = () => (
<Slider
padding={[0, padding[1] + 20, 0, padding[3]]}
width="auto"
height={26}
xAxis="x"
yAxis="y1"
scales={{
x: timeScale,
}}
data={data}
start={ds.state.start}
end={ds.state.end}
backgroundChart={{
type: 'line',
}}
onChange={({ startValue, endValue }: { 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>
</div>
</div>
);
};
export default autoHeight()(TimelineChart);

8
src/pages/dashboard/analysis/components/Trend/index.style.ts

@ -22,11 +22,9 @@ const useStyles = createStyles(({ token }) => {
color: token.colorText,
},
},
'reverseColor .up': {
color: token['green-6'],
},
'reverseColor .down': {
color: token['red-6'],
reverseColor: {
up: { color: token['green-6'] },
down: { color: token['red-6'] },
},
};
});

180
src/pages/dashboard/monitor/components/Charts/Gauge/index.tsx

@ -1,180 +0,0 @@
import { Axis, Chart, Coord, Geom, Guide, Shape } from 'bizcharts';
import React from 'react';
import autoHeight from '../autoHeight';
const { Arc, Html, Line } = Guide;
export type GaugeProps = {
title: React.ReactNode;
color?: string;
height?: number;
bgColor?: number;
percent: number;
forceFit?: boolean;
style?: React.CSSProperties;
formatter?: (value: string) => string;
};
const defaultFormatter = (val: string): string => {
switch (val) {
case '2':
return '差';
case '4':
return '中';
case '6':
return '良';
case '8':
return '优';
default:
return '';
}
};
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 }];
const renderHtml = () => `
<div style="width: 300px;text-align: center;font-size: 12px!important;">
<div style="font-size: 14px; color: rgba(0,0,0,0.43);margin: 0;">${title}</div>
<div style="font-size: 24px;color: rgba(0,0,0,0.85);margin: 0;">
${(data[0].value * 10).toFixed(2)}%
</div>
</div>`;
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,
}}
/>
<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}
/>
</Chart>
);
};
export default autoHeight()(Gauge);

141
src/pages/dashboard/monitor/components/Charts/MiniArea/index.tsx

@ -1,141 +0,0 @@
import type { AxisProps } from 'bizcharts';
import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import React from 'react';
import autoHeight from '../autoHeight';
import useStyles from '../index.style';
export type MiniAreaProps = {
color?: string;
height?: number;
borderColor?: string;
line?: boolean;
animate?: boolean;
xAxis?: AxisProps;
forceFit?: boolean;
scale?: {
x?: {
tickCount: number;
};
y?: {
tickCount: number;
};
};
yAxis?: Partial<AxisProps>;
borderWidth?: number;
data: {
x: number | string;
y: number;
}[];
};
const MiniArea: React.FC<MiniAreaProps> = (props) => {
const { styles } = useStyles();
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 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 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={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="line"
position="x*y"
shape="smooth"
color={borderColor}
size={borderWidth}
tooltip={false}
/>
) : (
<span
style={{
display: 'none',
}}
/>
)}
</Chart>
)}
</div>
</div>
);
};
export default autoHeight()(MiniArea);

311
src/pages/dashboard/monitor/components/Charts/Pie/index.tsx

@ -1,311 +0,0 @@
import { DataView } from '@antv/data-set';
import { Divider } from 'antd';
import { Chart, Coord, Geom, Tooltip } from 'bizcharts';
import classNames from 'classnames';
import Debounce from 'lodash.debounce';
import React, { Component } from 'react';
import ReactFitText from 'react-fittext';
import autoHeight from '../autoHeight';
export type PieProps = {
animate?: boolean;
color?: string;
colors?: string[];
selected?: boolean;
height?: number;
margin?: [number, number, number, number];
hasLegend?: boolean;
padding?: [number, number, number, number];
percent?: number;
data?: {
x: string | string;
y: number;
}[];
inner?: number;
lineWidth?: number;
forceFit?: boolean;
style?: React.CSSProperties;
className?: string;
total?: React.ReactNode | number | (() => React.ReactNode | number);
title?: React.ReactNode;
tooltip?: boolean;
valueFormat?: (value: string) => string | React.ReactNode;
subTitle?: React.ReactNode;
};
type PieState = {
legendData: {
checked: boolean;
x: string;
color: string;
percent: number;
y: string;
}[];
legendBlock: boolean;
};
class Pie extends Component<PieProps, PieState> {
state: PieState = {
legendData: [],
legendBlock: false,
};
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(
'resize',
() => {
this.requestRef = requestAnimationFrame(() => this.resize());
},
{
passive: true,
},
);
}
componentDidUpdate(preProps: PieProps) {
const { data } = this.props;
if (data !== preProps.data) {
// because of charts data create when rendered
// so there is a trick for get rendered time
this.getLegendData();
}
}
componentWillUnmount() {
if (this.requestRef) {
window.cancelAnimationFrame(this.requestRef);
}
window.removeEventListener('resize', this.resize);
if (this.resize) {
(this.resize as any).cancel();
}
}
getG2Instance = (chart: G2.Chart) => {
this.chart = chart;
requestAnimationFrame(() => {
this.getLegendData();
this.resize();
});
};
// for custom lengend view
getLegendData = () => {
if (!this.chart) return;
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return;
// g2 的类型有问题
const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map(
(
item: {
color: any;
_origin: any;
}[],
) => {
/* eslint no-underscore-dangle:0 */
const origin = item[0]._origin;
origin.color = item[0].color;
origin.checked = true;
return origin;
},
);
this.setState({
legendData,
});
};
handleRoot = (n: HTMLDivElement) => {
this.root = n;
};
handleLegendClick = (
item: {
checked: boolean;
},
i: string | number,
) => {
const newItem = {
...item,
};
newItem.checked = !newItem.checked;
const { legendData } = this.state;
legendData[i as unknown as number] = newItem as any;
const filteredLegendData = legendData.filter((l) => l.checked).map((l) => l.x);
if (this.chart) {
this.chart.filter('x', (val) => filteredLegendData.indexOf(`${val}`) > -1);
}
this.setState({
legendData,
});
};
render() {
const {
valueFormat,
subTitle,
total,
hasLegend = false,
className,
style,
height = 0,
forceFit = true,
percent,
color,
inner = 0.75,
animate = true,
colors,
lineWidth = 1,
} = this.props;
const { legendData, legendBlock } = this.state;
const pieClassName = classNames(styles.pie, className, {
[styles.hasLegend]: !!hasLegend,
[styles.legendBlock]: legendBlock,
});
const {
data: propsData,
selected: propsSelected = true,
tooltip: propsTooltip = true,
} = this.props;
let data = propsData || [];
let selected = propsSelected;
let tooltip = propsTooltip;
const defaultColors = colors;
data = data || [];
selected = selected || true;
tooltip = tooltip || true;
let formatColor;
const scale = {
x: {
type: 'cat',
range: [0, 1],
},
y: {
min: 0,
},
};
if (percent || percent === 0) {
selected = false;
tooltip = false;
formatColor = (value: string) => {
if (value === '占比') {
return color || 'rgba(24, 144, 255, 0.85)';
}
return '#F0F2F5';
};
data = [
{
x: '占比',
y: parseFloat(`${percent}`),
},
{
x: '反比',
y: 100 - parseFloat(`${percent}`),
},
];
}
const tooltipFormat: [
string,
(...args: any[]) => {
name?: string;
value: string;
},
] = [
'x*percent',
(x: string, p: number) => ({
name: x,
value: `${(p * 100).toFixed(2)}%`,
}),
];
const padding = [12, 0, 12, 0] as [number, number, number, number];
const dv = new DataView();
dv.source(data).transform({
type: 'percent',
field: 'y',
dimension: 'x',
as: 'percent',
});
return (
<div ref={this.handleRoot} className={pieClassName} style={style}>
<ReactFitText maxFontSize={25}>
<div className={styles.chart}>
<Chart
scale={scale}
height={height}
forceFit={forceFit}
data={dv}
padding={padding}
animate={animate}
onGetG2Instance={this.getG2Instance}
>
{!!tooltip && <Tooltip showTitle={false} />}
<Coord type="theta" innerRadius={inner} />
<Geom
style={{
lineWidth,
stroke: '#fff',
}}
tooltip={tooltip ? tooltipFormat : undefined}
type="intervalStack"
position="percent"
color={['x', percent || percent === 0 ? formatColor : defaultColors] as any}
selected={selected}
/>
</Chart>
{(subTitle || total) && (
<div className={styles.total}>
{subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
{/* eslint-disable-next-line */}
{total && (
<div className="pie-stat">{typeof total === 'function' ? total() : total}</div>
)}
</div>
)}
</div>
</ReactFitText>
{hasLegend && (
<ul className={styles.legend}>
{legendData.map((item, i) => (
<li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
<span
className={styles.dot}
style={{
backgroundColor: !item.checked ? '#aaa' : item.color,
}}
/>
<span className={styles.legendTitle}>{item.x}</span>
<Divider type="vertical" />
<span className={styles.percent}>
{`${(Number.isNaN(item.percent) ? 0 : item.percent * 100).toFixed(2)}%`}
</span>
<span className={styles.value}>{valueFormat ? valueFormat(item.y) : item.y}</span>
</li>
))}
</ul>
)}
</div>
);
}
}
export default autoHeight()(Pie);

209
src/pages/dashboard/monitor/components/Charts/TagCloud/index.tsx

@ -1,209 +0,0 @@
import DataSet from '@antv/data-set';
import { Chart, Coord, Geom, Shape, Tooltip } from 'bizcharts';
import classNames from 'classnames';
import Debounce from 'lodash.debounce';
import React, { Component } from 'react';
import autoHeight from '../autoHeight';
/* eslint no-underscore-dangle: 0 */
/* eslint no-param-reassign: 0 */
const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
export type TagCloudProps = {
data: {
name: string;
value: string;
}[];
height?: number;
className?: string;
style?: React.CSSProperties;
};
type TagCloudState = {
dv: any;
height?: number;
width: number;
};
class TagCloud extends Component<TagCloudProps, TagCloudState> {
state = {
dv: null,
height: 0,
width: 0,
};
requestRef: number = 0;
isUnmount: boolean = false;
root: HTMLDivElement | undefined = undefined;
imageMask: HTMLImageElement | undefined = undefined;
componentDidMount() {
requestAnimationFrame(() => {
this.initTagCloud();
this.renderChart(this.props);
});
window.addEventListener('resize', this.resize, {
passive: true,
});
}
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;
};
initTagCloud = () => {
function getTextAttrs(cfg: {
x?: any;
y?: any;
style?: any;
opacity?: any;
origin?: any;
color?: any;
}) {
return {
...cfg.style,
fillOpacity: cfg.opacity,
fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate,
text: cfg.origin._origin.text,
textAlign: 'center',
fontFamily: cfg.origin._origin.font,
fill: cfg.color,
textBaseline: 'Alphabetic',
};
}
(Shape as any).registerShape('point', 'cloud', {
drawShape(
cfg: {
x: any;
y: any;
},
container: {
addShape: (
arg0: string,
arg1: {
attrs: any;
},
) => void;
},
) {
const attrs = getTextAttrs(cfg);
return container.addShape('text', {
attrs: {
...attrs,
x: cfg.x,
y: cfg.y,
},
});
},
});
};
renderChart = Debounce((nextProps: TagCloudProps) => {
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const { data, height } = nextProps || this.props;
if (data.length < 1 || !this.root) {
return;
}
const h = height;
const w = this.root.offsetWidth;
const onload = () => {
const dv = new DataSet.View().source(data);
const range = dv.range('value');
const [min, max] = range;
dv.transform({
type: 'tag-cloud',
fields: ['name', 'value'],
imageMask: this.imageMask,
font: 'Verdana',
size: [w, h],
// 宽高设置最好根据 imageMask 做调整
padding: 0,
timeInterval: 5000,
// max execute time
rotate() {
return 0;
},
fontSize(d: { value: number }) {
const size = ((d.value - min) / (max - min)) ** 2;
return size * (17.5 - 5) + 5;
},
});
if (this.isUnmount) {
return;
}
this.setState({
dv,
width: w,
height: h,
});
};
if (!this.imageMask) {
this.imageMask = new Image();
this.imageMask.crossOrigin = '';
this.imageMask.src = imgUrl;
this.imageMask.onload = onload;
} else {
onload();
}
}, 200);
render() {
const { className, height } = this.props;
const { dv, width, height: stateHeight } = this.state;
return (
<div
className={classNames(styles.tagCloud, className)}
style={{
width: '100%',
height,
}}
ref={this.saveRootRef}
>
{dv && (
<Chart
width={width}
height={stateHeight}
data={dv}
padding={0}
scale={{
x: {
nice: false,
},
y: {
nice: false,
},
}}
>
<Tooltip showTitle={false} />
<Coord reflect="y" />
<Geom
type="point"
position="x*y"
color="text"
shape="cloud"
tooltip={[
'text*value',
function trans(text, value) {
return {
name: text,
value,
};
},
]}
/>
</Chart>
)}
</div>
);
}
}
export default autoHeight()(TagCloud);

174
src/pages/dashboard/workplace/components/Radar/index.tsx

@ -1,174 +0,0 @@
import { Col, Row } from 'antd';
import { Axis, Chart, Coord, Geom, Tooltip } from 'bizcharts';
import React, { useEffect, useRef, useState } from 'react';
export type RadarProps = {
title?: React.ReactNode;
height?: number;
padding?: [number, number, number, number];
hasLegend?: boolean;
data: {
name: string;
label: string;
value: string | number;
}[];
colors?: string[];
animate?: boolean;
forceFit?: boolean;
tickCount?: number;
style?: React.CSSProperties;
};
type RadarState = {
legendData: {
checked: boolean;
name: string;
color: string;
percent: number;
value: string;
}[];
};
const defaultColors = [
'#1890FF',
'#FACC14',
'#2FC25B',
'#8543E0',
'#F04864',
'#13C2C2',
'#fa8c16',
'#a0d911',
];
const Radar: React.FC<RadarProps> = (props) => {
const {
data = [],
height = 0,
title,
hasLegend = false,
forceFit = true,
tickCount = 5,
padding = [35, 30, 16, 30] as [number, number, number, number],
animate = true,
colors = defaultColors,
} = props;
const chartRef = useRef<G2.Chart>();
const [legendData, setLegendData] = useState<RadarState['legendData']>([]);
const getLegendData = () => {
if (!chartRef.current) return;
const geom = chartRef.current.getAllGeoms()[0];
if (!geom) return;
const items = (geom as any).get('dataArray') || [];
const legendData = items.map((item: any[]) => {
const origins = item.map((t) => t._origin);
const result = {
name: origins[0].name,
color: item[0].color,
checked: true,
value: origins.reduce((p, n) => p + n.value, 0),
};
return result;
});
setLegendData(legendData);
};
useEffect(() => {
getLegendData();
}, [data]);
const handleLegendClick = (item: any, i: string | number) => {
const newItem = { ...item, checked: !item.checked };
const newLegendData = [...legendData];
newLegendData[i as number] = newItem;
const filteredLegendData = newLegendData.filter((l) => l.checked).map((l) => l.name);
if (chartRef.current) {
chartRef.current.filter('name', (val) => filteredLegendData.indexOf(`${val}`) > -1);
chartRef.current.repaint();
}
setLegendData(newLegendData);
};
const scale = {
value: {
min: 0,
tickCount,
},
};
const chartHeight = height - (hasLegend ? 80 : 22);
return (
<div style={{ height }}>
{title && <h4>{title}</h4>}
<Chart
scale={scale}
height={chartHeight}
forceFit={forceFit}
data={data}
padding={padding}
animate={animate}
onGetG2Instance={(chart: G2.Chart) => {
chartRef.current = chart;
}}
>
<Tooltip />
<Coord type="polar" />
<Axis
name="label"
line={undefined}
tickLine={undefined}
grid={{
lineStyle: {
lineDash: undefined,
},
hideFirstLine: false,
}}
/>
<Axis
name="value"
grid={{
type: 'polygon',
lineStyle: {
lineDash: undefined,
},
}}
/>
<Geom type="line" position="label*value" color={['name', colors]} size={1} />
<Geom
type="point"
position="label*value"
color={['name', colors]}
shape="circle"
size={3}
/>
</Chart>
{hasLegend && (
<Row>
{legendData.map((item, i) => (
<Col
span={24 / legendData.length}
key={item.name}
onClick={() => handleLegendClick(item, i)}
>
<div>
<p>
<span
style={{
backgroundColor: !item.checked ? '#aaa' : item.color,
}}
/>
<span>{item.name}</span>
</p>
<h6>{item.value}</h6>
</div>
</Col>
))}
</Row>
)}
</div>
);
};
export default Radar;

20
src/pages/list/search/applications/components/TagSelect/index.tsx

@ -1,9 +1,8 @@
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useBoolean, useControllableValue } from 'ahooks';
import { Tag } from 'antd';
import classNames from 'classnames';
import type { FC } from 'react';
import React from 'react';
import { useMergedState } from 'rc-util';
import React, { FC, useState } from 'react';
import useStyles from './index.style';
const { CheckableTag } = Tag;
export interface TagSelectOptionProps {
@ -24,8 +23,11 @@ const TagSelectOption: React.FC<TagSelectOptionProps> & {
{children}
</CheckableTag>
);
TagSelectOption.isTagSelectOption = true;
type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
export interface TagSelectProps {
onChange?: (value: (string | number)[]) => void;
expandable?: boolean;
@ -47,8 +49,14 @@ const TagSelect: FC<TagSelectProps> & {
} = (props) => {
const { styles } = useStyles();
const { children, hideCheckAll = false, className, style, expandable, actionsText = {} } = props;
const [expand, { toggle }] = useBoolean();
const [value, setValue] = useControllableValue<(string | number)[]>(props);
const [expand, setExpand] = useState<boolean>(false);
const [value, setValue] = useMergedState<(string | number)[]>(props.defaultValue || [], {
value: props.value,
defaultValue: props.defaultValue,
onChange: props.onChange,
});
const isTagSelectOption = (node: TagSelectOptionElement) =>
node &&
node.type &&
@ -106,7 +114,7 @@ const TagSelect: FC<TagSelectProps> & {
<a
className={styles.trigger}
onClick={() => {
toggle();
setExpand(!expand);
}}
>
{expand ? (

22
src/pages/list/search/articles/components/TagSelect/index.tsx

@ -1,17 +1,16 @@
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useBoolean, useControllableValue } from 'ahooks';
import { Tag } from 'antd';
import classNames from 'classnames';
import type { FC } from 'react';
import React from 'react';
import { useMergedState } from 'rc-util';
import React, { FC, useState } from 'react';
import useStyles from './index.style';
const { CheckableTag } = Tag;
export interface TagSelectOptionProps {
value: string | number;
style?: React.CSSProperties;
checked?: boolean;
children?: React.ReactNode;
onChange?: (value: string | number, state: boolean) => void;
children?: React.ReactNode;
}
const TagSelectOption: React.FC<TagSelectOptionProps> & {
isTagSelectOption: boolean;
@ -24,8 +23,11 @@ const TagSelectOption: React.FC<TagSelectOptionProps> & {
{children}
</CheckableTag>
);
TagSelectOption.isTagSelectOption = true;
type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
export interface TagSelectProps {
onChange?: (value: (string | number)[]) => void;
expandable?: boolean;
@ -47,8 +49,14 @@ const TagSelect: FC<TagSelectProps> & {
} = (props) => {
const { styles } = useStyles();
const { children, hideCheckAll = false, className, style, expandable, actionsText = {} } = props;
const [expand, { toggle }] = useBoolean();
const [value, setValue] = useControllableValue<(string | number)[]>(props);
const [expand, setExpand] = useState<boolean>(false);
const [value, setValue] = useMergedState<(string | number)[]>(props.defaultValue || [], {
value: props.value,
defaultValue: props.defaultValue,
onChange: props.onChange,
});
const isTagSelectOption = (node: TagSelectOptionElement) =>
node &&
node.type &&
@ -106,7 +114,7 @@ const TagSelect: FC<TagSelectProps> & {
<a
className={styles.trigger}
onClick={() => {
toggle();
setExpand(!expand);
}}
>
{expand ? (

22
src/pages/list/search/projects/components/TagSelect/index.tsx

@ -1,17 +1,16 @@
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useBoolean, useControllableValue } from 'ahooks';
import { Tag } from 'antd';
import classNames from 'classnames';
import type { FC } from 'react';
import React from 'react';
import { useMergedState } from 'rc-util';
import React, { FC, useState } from 'react';
import useStyles from './index.style';
const { CheckableTag } = Tag;
export interface TagSelectOptionProps {
value: string | number;
children?: React.ReactNode;
style?: React.CSSProperties;
checked?: boolean;
onChange?: (value: string | number, state: boolean) => void;
children?: React.ReactNode;
}
const TagSelectOption: React.FC<TagSelectOptionProps> & {
isTagSelectOption: boolean;
@ -24,8 +23,11 @@ const TagSelectOption: React.FC<TagSelectOptionProps> & {
{children}
</CheckableTag>
);
TagSelectOption.isTagSelectOption = true;
type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
export interface TagSelectProps {
onChange?: (value: (string | number)[]) => void;
expandable?: boolean;
@ -47,8 +49,14 @@ const TagSelect: FC<TagSelectProps> & {
} = (props) => {
const { styles } = useStyles();
const { children, hideCheckAll = false, className, style, expandable, actionsText = {} } = props;
const [expand, { toggle }] = useBoolean();
const [value, setValue] = useControllableValue<(string | number)[]>(props);
const [expand, setExpand] = useState<boolean>(false);
const [value, setValue] = useMergedState<(string | number)[]>(props.defaultValue || [], {
value: props.value,
defaultValue: props.defaultValue,
onChange: props.onChange,
});
const isTagSelectOption = (node: TagSelectOptionElement) =>
node &&
node.type &&
@ -106,7 +114,7 @@ const TagSelect: FC<TagSelectProps> & {
<a
className={styles.trigger}
onClick={() => {
toggle();
setExpand(!expand);
}}
>
{expand ? (

2
src/typings.d.ts

@ -12,9 +12,7 @@ declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';
declare module 'numeral';
declare module '@antv/data-set';
declare module 'mockjs';
declare module 'react-fittext';
declare module 'bizcharts-plugin-slider';
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;

Loading…
Cancel
Save