/** @format */ import React, { useRef, useLayoutEffect, useEffect, useState } from 'react'; import echarts from 'echarts/lib/echarts'; import zrender from 'zrender'; import 'echarts/lib/chart/line'; // import 'echarts/lib/chart/pie'; // import 'echarts/lib/chart/gauge'; // import 'echarts/lib/chart/bar'; import 'echarts/lib/component/tooltip'; // import 'echarts/lib/component/toolbox'; import 'echarts/lib/component/title'; import 'echarts/lib/component/legend'; import 'echarts/lib/component/grid'; import { Container } from './styled'; import _ from 'lodash'; interface BasicLineChartProps { /** * 容器大小 */ size?: { width: number; height: number }; /** * 配置项 */ option?: Record; /** * 数据源,统一为数组格式 */ data?: Record; theme?: Record; } const BasicLineChart = (props: BasicLineChartProps) => { const { size = { height: 215, width: 372 }, option = {}, data, theme = { lineColor: ['#50E1A8', '#FDA568'], }, data: { xData = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'], yData = [[10, 52, 200, 334, 390, 330, 220]], } = {}, } = props; const wrapperRef = useRef(null); const [echartsInc, setEchartsInc] = useState | null>( null, ); const [isLegendShow, setIsLegendShow] = useState(true); const [seriesNameArray, setSeriesNameArray] = useState([]); function formatSeries( data: Record[] = [], op = {}, key: number, isPile: boolean, ) { let themeColor = theme.lineColor && theme.lineColor[key]; let areaStylexy = isPile ? { x: 0, y: 0, x2: 0, y2: 1, } : { x: 1, y: 1, x2: 0, y2: 0, }; // series 模板 const baseSeries = { name: '直接访问', type: 'line', data: [], itemStyle: { color: new zrender.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: themeColor, }, { offset: 1, color: 'rgb(80, 225, 168, 0.1)', }, ]), borderWidth: 3, opacity: 0, }, smooth: false, markPoint: { data: [ { type: 'max', name: '最大值' }, // { type: 'min', name: '最小值' }, ], symbol: 'pin', //使用此方式隐藏图标 symbolSize: 0.1, symbolOffset: [0, -10], label: { color: themeColor, }, }, label: { show: false, color: '#999', fontWeight: 'normal', align: 'center', fontFamily: 'Microsoft YaHei', fontSize: 12, position: 'top', }, lineStyle: { type: 'solid', width: 1, }, areaStyle: { opacity: 1, color: { type: 'linear', ...areaStylexy, colorStops: [ { offset: 0, color: themeColor, // 0% 处的颜色 }, { offset: 1, color: 'rgba(9,9,9,0)', // 100% 处的颜色 }, ], global: false, // 缺省为 false }, }, }; const latestSeries = _.mergeWith(baseSeries, op); latestSeries.data = data; return latestSeries; } const optionBase = { tooltip: { show: true, trigger: 'axis', triggerOn: 'mousemove', axisPointer: { // 坐标轴指示器,坐标轴触发有效 type: 'line', // 默认为直线,可选为:'line' | 'shadow' }, hideDelay: 100, textStyle: { fontFamily: 'Microsoft Yahei', fontWeight: 'normal', fontSize: 14, color: '#fff', }, }, legend: { show: false, padding: 2, itemGap: 5, itemWidth: 6, icon: 'circle', itemHeight: 6, left: 'center', orient: 'horizontal', textStyle: { fontFamily: 'Microsoft Yahei', fontWeight: 'normal', fontSize: 12, color: '#999', }, }, // legend: [ // { // top: '1%', // right: '1%', // textStyle: { // color: '#3BAEFE', // fontSize: 11, // fontFamily: '微软雅黑', // }, // itemWidth: 27, // itemHeight: 16, // data: [ // { // name: '直接访问', // icon: 'rect', // }, // ], // }, // { // top: "15%", // right: "15%", // textStyle: { // color: "#3BAEFE", // fontSize: 14, // fontFamily: "微软雅黑" // }, // data:["\n","\n","\n","农药占比","LOL占比"] // } // ], grid: { left: 30, right: 40, bottom: 30, top: 30, containLabel: true, }, xAxis: { name: '', type: 'category', show: true, data: xData, axisTick: { alignWithLabel: true, show: false, }, nameTextStyle: {}, axisLabel: { show: true, color: '#999', fontWeight: 'normal', fontFamily: 'Microsoft YaHei', fontSize: 12, rotate: 0, interval: 0, }, axisLine: { show: false, lineStyle: { color: '#333' }, }, splitLine: { show: false, lineStyle: { color: '#999', type: 'dashed' }, }, }, yAxis: { type: 'value', min: 0, max: 'dataMax', axisTick: { show: false, }, axisLabel: { show: true, color: '#999', fontWeight: 'normal', fontFamily: 'Microsoft YaHei', fontSize: 12, interval: 'auto', formatter: '{value}', }, axisLine: { show: false, lineStyle: { color: '#333' }, }, splitLine: { show: true, lineStyle: { color: '#aaa', type: 'dashed' }, }, name: '单位', nameTextStyle: { color: '#fff', }, }, series: [], animation: true, animationEasing: 'cubicOut', animationEasingUpdate: 'cubicOut', animationDuration: 500, animationDurationUpdate: 300, }; useLayoutEffect(() => { if (wrapperRef && wrapperRef.current) { const myChart = echarts.init(wrapperRef.current, null, { devicePixelRatio: 2, }); // legend不以默认方式显示,自定义样式 setEchartsInc(myChart); const latestOption = _.mergeWith(optionBase, option); let isPile = yData.length > 1; latestOption.series = yData.map( (item: Record[], index: number) => formatSeries( item, option.series && option.series[index], index, isPile, ), ); if (latestOption.legend && latestOption.legend.show) { latestOption.legend.show = false; setSeriesNameArray( latestOption.series.map((item: Record) => item.name), ); setIsLegendShow(true); } myChart.setOption(latestOption); } }, []); useEffect(() => { if (!echartsInc) { return; } const latestOption = _.mergeWith(optionBase, option); let isPile = yData.length > 1; latestOption.series = yData.map( (item: Record[], index: number) => formatSeries( item, option.series && option.series[index], index, isPile, ), ); if (latestOption.legend && latestOption.legend.show) { latestOption.legend.show = false; setSeriesNameArray( latestOption.series.map((item: Record) => item.name), ); setIsLegendShow(true); } echartsInc.setOption(latestOption, { notMerge: true }); }, [option, data]); useEffect(() => { if (!echartsInc) { return; } echartsInc.resize(); }, [size]); return (
{isLegendShow ? (
{seriesNameArray && seriesNameArray.length ? seriesNameArray.map((itemName, index) => { const color = theme.lineColor[index % theme.lineColor.length]; return (
{itemName}
); }) : null}
) : null}
); }; export default BasicLineChart;