You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
3.8 KiB
192 lines
3.8 KiB
import { CSSProperties } from 'react'; |
|
import * as echarts from 'echarts'; |
|
import { PieSeriesOption } from 'echarts'; |
|
import { useRef, useEffect } from 'react'; |
|
import { isEmpty, sumBy, head, merge, gt, gte, lte, ceil } from 'lodash'; |
|
import { CHARTS_COLORS, LOADING_COLOR } from './type'; |
|
import { useSize } from 'ahooks'; |
|
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 |
|
type ECOption = echarts.ComposeOption<PieSeriesOption>; |
|
|
|
export interface PieProps { |
|
color?: Array<string>; |
|
series: Array<any>; |
|
loading?: boolean; |
|
smooth?: boolean; |
|
style?: CSSProperties; |
|
options?: ECOption; |
|
onDownCallBack?: any; |
|
loadingType?: any; |
|
} |
|
|
|
function getOptions(width: number) { |
|
if (gt(width, 1920)) { |
|
return { |
|
legend: { |
|
textStyle: { |
|
width: 140, |
|
fontSize: 14, |
|
overflow: 'truncate', |
|
}, |
|
}, |
|
radius: ['60%', '80%'], |
|
center: ['40%', '50%'], |
|
title: { |
|
left: '40%', |
|
}, |
|
}; |
|
} else if (gte(width, 1680)) { |
|
return { |
|
legend: { |
|
textStyle: { |
|
width: 120, |
|
fontSize: 12, |
|
overflow: 'truncate', |
|
}, |
|
}, |
|
radius: ['60%', '80%'], |
|
center: ['35%', '50%'], |
|
title: { |
|
left: '35%', |
|
}, |
|
}; |
|
} else if (gte(width, 1440)) { |
|
return { |
|
legend: { |
|
textStyle: { |
|
width: 100, |
|
fontSize: 10, |
|
overflow: 'truncate', |
|
}, |
|
}, |
|
radius: ['50%', '70%'], |
|
center: ['35%', '50%'], |
|
title: { |
|
left: '35%', |
|
}, |
|
}; |
|
} else if (lte(width, 1440)) { |
|
return { |
|
legend: { |
|
textStyle: { |
|
width: 80, |
|
fontSize: 8, |
|
overflow: 'truncate', |
|
}, |
|
}, |
|
radius: ['40%', '60%'], |
|
center: ['30%', '50%'], |
|
title: { |
|
left: '30%', |
|
}, |
|
}; |
|
} else { |
|
return { |
|
legend: { |
|
textStyle: { |
|
width: 100, |
|
fontSize: 10, |
|
overflow: 'truncate', |
|
}, |
|
}, |
|
radius: ['50%', '70%'], |
|
center: ['35%', '50%'], |
|
title: { |
|
left: '35%', |
|
}, |
|
}; |
|
} |
|
} |
|
|
|
export function ChartsPie(props: PieProps) { |
|
const { |
|
color = CHARTS_COLORS, |
|
series, |
|
loading, |
|
style, |
|
options, |
|
onDownCallBack, |
|
loadingType = { color: LOADING_COLOR }, |
|
} = props; |
|
|
|
const contentEle = useRef<any>(); |
|
|
|
const chartRef = useRef<any>(); |
|
|
|
const size = useSize(document.querySelector('body')); |
|
// @ts-ignore |
|
const sizeOptions = getOptions(size?.width); |
|
// @ts-ignore |
|
const legendShow = options?.legend?.show; |
|
|
|
const defaultOptions: ECOption = merge( |
|
{ |
|
color: color, |
|
tooltip: { |
|
trigger: 'item', |
|
formatter: '{b}<br />数量: {c}<br />占比: {d}%', |
|
}, |
|
label: { |
|
formatter(params: { [key: string]: any }): any { |
|
return `${params?.name}: ${params?.percent ?? 0}%`; |
|
}, |
|
}, |
|
legend: { |
|
show: false, |
|
itemWidth: 10, |
|
itemHeight: 10, |
|
}, |
|
title: { |
|
text: ceil(sumBy(head(series)?.data, 'value'), 2) || 0, |
|
top: '47%', |
|
textAlign: 'center', |
|
left: '50%', |
|
textStyle: { |
|
color: '#333', |
|
fontSize: 22, |
|
fontWeight: '400', |
|
}, |
|
}, |
|
}, |
|
options, |
|
legendShow ? sizeOptions : {}, |
|
); |
|
const responsive = useSize(contentEle.current); |
|
|
|
useEffect(() => { |
|
chartRef.current?.resize(); |
|
}, [JSON.stringify(responsive)]); |
|
|
|
useEffect(() => { |
|
if (!chartRef.current) { |
|
chartRef.current = echarts.init(contentEle.current); |
|
} |
|
if (loading) { |
|
chartRef.current.showLoading(Object.assign({ text: '' }, loadingType)); |
|
return; |
|
} else { |
|
chartRef.current.hideLoading(); |
|
} |
|
|
|
if (!isEmpty(series)) { |
|
chartRef.current.setOption({ |
|
...defaultOptions, |
|
series: series.map((item) => ({ |
|
radius: sizeOptions.radius, |
|
center: legendShow ? sizeOptions.center : ['50%', '50%'], |
|
...item, |
|
type: 'pie', |
|
})), |
|
}); |
|
if (onDownCallBack) { |
|
chartRef.current.on('click', function (params: any) { |
|
onDownCallBack(chartRef.current, params, options); |
|
}); |
|
} |
|
} |
|
}, [series, loading]); |
|
|
|
return ( |
|
<div ref={contentEle} style={{ width: '100%', height: '100%', ...style }} /> |
|
); |
|
}
|
|
|