import React, { useEffect, useRef, useState } from 'react' import * as echarts from 'echarts' import { EChartsOption, init, dispose, registerMap } from 'echarts' import { Empty } from 'antd' import { china } from '@/utils/dictionary' /**通用直接传入原始配置EChartsOption*/ function Echarts(props: { children: EChartsOption | null, style?: { width: number | string, height: number | string } }) { const { style } = props const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() /**创建myChart实例 */ useEffect(() => { if (ref?.current && props?.children) { let myChart = init(ref.current) setMyChart(myChart) myChart.setOption(props.children); myChart.resize() } }, [ref?.current, props?.children]) /**卸载myChart */ useEffect(() => { return () => { if (myChart?.id) { dispose(myChart.id) } } }, [myChart?.id]) return <> { props.children ?
:
} } /** * Map粉丝统计 * @param data name省名称 vlaue值 * @param style width ,height */ function Map(props: { data: { name: string, value: number }[], style?: { width: number | string, height: number | string } }) { const { data, style } = props const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() /**创建myChart实例 */ useEffect(() => { if (ref?.current && data.length > 0) { let myChart = init(ref.current)//初始化chart registerMap('China', require('../../public/map.json'))//获取地图数据 let num: number[] = []//获取所有值方便取最大最小值 let newData: any = china//处理匹配接口返回数据和地图数据的映射避免名字不同 data.forEach((item: { name: string, value: number }) => { newData.forEach((data: { name: string, value: number }) => { if (item.name && data.name.indexOf(item.name) !== -1) { data.value = item.value num.push(item.value) } }) }) setMyChart(myChart)//存放Chart实例以备卸载 myChart.setOption({//设置Chart配置 /**悬浮提示 */ tooltip: { trigger: 'item', formatter: '{b}:{c}' }, /**右侧操作工具 */ toolbox: { show: true, orient: 'vertical', right: 50, top: 'center', feature: { dataView: { readOnly: false, title: '查看详情' }, restore: { title: '刷新' }, saveAsImage: { title: '保存为图片' } } }, /**左侧操作工具 */ visualMap: { min: 0, max: Math.max(...num), text: ['High', 'Low'], realtime: false, calculable: true, top: 'middle', inRange: { color: ['lightskyblue', 'yellow', 'orangered'] } }, /**地图参数设置 */ series: [ { name: '粉丝统计', type: 'map', map: 'China', // 自定义扩展图表类型 zoom: 1.2, scaleLimit: { min: 1, max: 10 }, roam: true, nameProperty: 'name', data: newData, } ], }); } }, [ref?.current, data]) /**卸载myChart */ useEffect(() => { if (data.length === 0) { if (myChart?.id) { dispose(myChart.id) } } return () => { if (myChart?.id) { dispose(myChart.id) } } }, [myChart?.id, data]) return <> { data.length > 0 &&
} { data.length === 0 &&
} } /** * 饼图总关注 * @param data name 名称 value 值 * @param style width,height * @param name tooltip上的名字即饼图的名字 * @param centerName 饼图中心统计的名称 */ function PieFocus(props: { /** * @param data name 名称 value 值 */ data: { name: string, value: number }[], /** * @param name tooltip上的名字即饼图的名字 */ name: string, /** * @param centerName 饼图中心统计的名称 */ centerName: string, /** * @param style width,height */ style?: { width: number | string, height: number | string }, }) { const { data, style, name, centerName } = props const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() /**创建myChart实例 */ useEffect(() => { if (ref?.current && data.length > 0) { let myChart = init(ref.current) setMyChart(myChart) myChart.setOption({ /**鼠标悬浮 */ tooltip: { // trigger: 'item', // formatter: '{a}
{b}: {c} ({d}%)' }, /**图列 */ legend: { left: 'center', bottom: 0, data: data?.map((item: { name: string }) => item.name) }, /**工具保存为图片 */ toolbox: { right: 50, feature: { saveAsImage: { title: '保存为图片' } } }, /**饼图参数 */ series: [ { name, type: 'pie', radius: ['50%', '70%'], // avoidLabelOverlap: false, label: { show: true, // position: 'center', // fontSize: '30', // fontWeight: 'bold', formatter: `{b}:{c} ({d}%)`, }, data }, { name, type: 'pie', radius: ['50%', '70%'], label: { show: true, position: 'center', fontSize: '30', fontWeight: 'bold', formatter: `${data.length > 1 ? data?.reduce((a: any, b: { value: number }) => { return (a?.value || a) + (b?.value || 0) }) : data[0].value}\n${centerName}`, }, data } ] }); } }, [ref?.current, data]) /**卸载myChart */ useEffect(() => { if (data.length === 0) { if (myChart?.id) { dispose(myChart?.id) } } return () => { if (myChart?.id) { dispose(myChart?.id) } } }, [myChart?.id, data]) return <> { data.length > 0 &&
} { data.length === 0 &&
} } /** * Line 折线图 * @param data legendName 图列名称 y:x(key为Y轴的展示的名称,value对应X轴的值。例:{legendName:'取消关注','2020-12-12':10,'2020-12-13':11,'2020-12-14':33}[]) * @param style width,height */ function Line(props: { data?: { legendName: string, [key: string]: any }[], style?: React.CSSProperties, areaStyle?: boolean,//线变块颜色 color?: string | string[],//线颜色 markPoint?: any,//浮点 fontColor?: string,//文字颜色 series?: boolean,//堆叠 title?: string,//标题 smooth?: boolean,//true 圆弧线 false 直角 }) { const { data, style, areaStyle = false, color, markPoint, fontColor, series, title, smooth = false } = props const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() let textStyle = fontColor ? { textStyle: { color: fontColor } } : {} /**创建myChart实例 */ useEffect(() => { if (ref?.current && (data as any[]).length > 0) { let Xdata: any = [] let Ydatas: any[] = [] data?.forEach((item, index) => { Ydatas.push([]) Object.keys(item).forEach((key: string) => { if (index === 0 && key !== 'legendName') { Xdata.push(key) } if (key !== 'legendName') { Ydatas[index].push(item[key]) } }) }) let myChart = init(ref.current) setMyChart(myChart) myChart.setOption({ /**鼠标悬浮 */ tooltip: { trigger: 'axis' }, /**图列 */ legend: { data: (data as { legendName: string, [key: string]: any }[]).map((item: { legendName: string }) => item.legendName), bottom: 0, left: 'center', ...textStyle }, xAxis: { data: Xdata, boundaryGap: false,//贴边 }, yAxis: [ { type: 'value', }, ], grid: { left: 100, right: 50, }, ...textStyle, /**工具保存为图片 */ toolbox: { right: 50, feature: { saveAsImage: { title: '保存为图片' } } }, title: { text: title || '', textAlign: 'center', right: 'center' }, /**线图参数 */ series: (data as { legendName: string, [key: string]: any }[]).map((item: { legendName: string }, index: number) => { return { name: item.legendName,//名称 type: 'line',//图形 data: Ydatas[index],//数据 areaStyle: areaStyle ? {} : undefined,//是否开启块级颜色 color: Array.isArray(color) ? color[index] : color,//颜色 markPoint: {//最大值最小值 data: markPoint ? markPoint : [] }, emphasis: series ? { focus: 'series' } : {}, //堆叠 smooth } }) }); } }, [ref?.current, data]) /**卸载myChart */ useEffect(() => { if ((data as any[])?.length === 0) { if (myChart?.id) { dispose(myChart?.id) } } return () => { if (myChart?.id) { dispose(myChart.id) } } }, [myChart?.id, data]) return <> { (data as any[])?.length > 0 &&
} { (data as any[])?.length === 0 &&
} } /** * Bar 柱状图 * @param data legendName 图列名称 y:x(key为Y轴的展示的名称,value对应X轴的值。例:{legendName:'取消关注','2020-12-12':10,'2020-12-13':11,'2020-12-14':33}[]) * @param style width,height */ function Bar(props: { data?: { legendName: string, [key: string]: any }[], style?: React.CSSProperties, fontColor?: string,//文字颜色 title?: string,//标题 }) { const { data, style, fontColor, title } = props const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() let textStyle = fontColor ? { textStyle: { color: fontColor } } : {} /**创建myChart实例 */ useEffect(() => { if (ref?.current && (data as any[]).length > 0) { let Xdata: any = [] // name let Ydatas: any[] = [] // value data?.forEach((item) => { Xdata.push(item.name) Ydatas.push(item.value) }) let sum = eval(Ydatas.join("+")) Ydatas = Ydatas.map((item: number) => { return (item / sum * 100).toFixed(0) }) let myChart = init(ref.current) setMyChart(myChart) myChart.setOption({ /**鼠标悬浮 */ // tooltip: { // trigger: 'axis', // }, xAxis: { max: 'dataMax' }, yAxis: { type: 'category', data: Xdata, inverse: true }, grid: { left: 100, right: 50, }, ...textStyle, title: { text: title || '', textAlign: 'center', right: 'center' }, /**线图参数 */ series: [ { type: 'bar',//图形 realtimeSort: true, data: Ydatas,//数据 name: 'X', label: { show: true, position: 'right', valueAnimation: true, } } ] }); } }, [ref?.current, data]) /**卸载myChart */ useEffect(() => { if ((data as any[])?.length === 0) { if (myChart?.id) { dispose(myChart?.id) } } return () => { if (myChart?.id) { dispose(myChart.id) } } }, [myChart?.id, data]) return <> { (data as any[])?.length > 0 &&
} { (data as any[])?.length === 0 &&
} } /** * Bar 柱状图 * @param data legendName 图列名称 y:x(key为Y轴的展示的名称,value对应X轴的值。例:{legendName:'取消关注','2020-12-12':10,'2020-12-13':11,'2020-12-14':33}[]) * @param style width,height */ // [ // { name: '4341556373', value: 300 }, { name: '4341523456', value: 240 }, // ] function BarMonitor(props: { data?: { legendName: string, [key: string]: any }[], style?: React.CSSProperties, fontColor?: string,//文字颜色 title?: string,//标题 isGraphic?: boolean, // 是否渐变 xName?: string, yName?: string, planID?: string, onChange?: (id: string, accountId: number[]) => void, }) { const { data, style, fontColor, title, xName, yName, planID, onChange } = props const colors = ['#f8128d', '#fa40a3', '#f56db5', '#f58bc4', '#f7a1cf', '#f5b7d8', '#facee5', '#f8c8e2', '#f8d6e8', '#fae8f1'] const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() let textStyle = fontColor ? { textStyle: { color: fontColor } } : {} /**创建myChart实例 */ useEffect(() => { if (ref?.current && (data as any[]).length > 0) { let Xdata: any = [] // name let Ydatas: any[] = [] // value data?.forEach((item) => { Xdata.push(item.adName) Ydatas.push(item.value) }) let dataIndex = data?.findIndex((item: any) => item?.name == planID) Ydatas = Ydatas.map((item: number, index: number) => { if (dataIndex !== -1 && dataIndex === index) { return { value: item, itemStyle: { color: '#37A2FF', shadowColor: 'rgba(0, 0, 0, 0.8)', shadowBlur: 10, }, } } else { return { value: item, itemStyle: { color: colors[index], } } } }) let myChart = init(ref.current) setMyChart(myChart) myChart?.setOption({ /**鼠标悬浮 */ // tooltip: { // trigger: 'axis', // }, xAxis: { name: xName || '', max: 'dataMax', nameTextStyle: { // y轴名字文字样式设置 verticalAlign: "bottom", lineHeight: 5, fontWeight: 'bold', }, axisLine: { // 坐标轴轴线相关设置 show: true // 是否显示坐标轴轴线 }, }, yAxis: { name: yName || '', type: 'category', data: Xdata, inverse: true, nameLocation: 'start', // y轴名字显示位置 nameTextStyle: { // y轴名字文字样式设置 verticalAlign: "bottom", lineHeight: 0, fontWeight: 'bold', color: '#000', padding: [0, 80, 0, 0] }, nameGap: 20, triggerEvent: true, axisLabel: {//名称 margin: 10, width: 80, overflow: 'truncate', ellipsis: '...', color: (value: any, index: any) => { if (index === dataIndex) { return '#f8128d' } return '#3946c3' }, }, }, grid: { left: 100, right: 70 }, ...textStyle, title: { text: title || '起量广告排行榜', textStyle: { color: '#3946c3', fontSize: 16 }, left: 'center', bottom: 0, }, tooltip: { show: true, // formatter:'{b0}: {c0}', formatter: function (a: any) { return `${a.marker} ${a.name} : ${a.value}` } }, series: [ { type: 'bar',//图形 realtimeSort: true, data: Ydatas,//数据 name: 'X', showBackground: true, backgroundStyle: { color: 'transparent' }, label: { show: true, valueAnimation: true, position: 'right', fontSize: 14, fontWeight: 900 } } ] }); myChart.on('click', (params: any) => { let v = params.dataIndex let d: any = (data as any[])[v] onChange && onChange(d.name, [d.accountId]) }); } }, [ref?.current, data, planID]) /**卸载myChart */ useEffect(() => { if ((data as any[])?.length === 0) { if (myChart?.id) { dispose(myChart?.id) } } return () => { if (myChart?.id) { dispose(myChart.id) } } }, [myChart?.id, data]) return <> { (data as any[])?.length > 0 &&
} { (data as any[])?.length === 0 &&
} } /** * Line 折线图 * @param data legendName 图列名称 y:x(key为Y轴的展示的名称,value对应X轴的值。例:{legendName:'取消关注','2020-12-12':10,'2020-12-13':11,'2020-12-14':33}[]) * @param style width,height */ function LineMonitor(props: { data?: { legendName: string, [key: string]: any }[], style?: React.CSSProperties, areaStyle?: boolean,//线变块颜色 color?: string | string[],//线颜色 markPoint?: any,//浮点 fontColor?: string,//文字颜色 series?: boolean,//堆叠 title?: string,//标题 smooth?: boolean,//true 圆弧线 false 直角 dataZoomInside?: boolean, // 控制是否开启滚轮缩放 dataZoomSlider?: boolean, // 控制是否开启组件缩放 }) { const { data, style, areaStyle = false, color, markPoint, fontColor, series, title, smooth = false, dataZoomInside, dataZoomSlider } = props const ref: { current: HTMLDivElement | null } = useRef(null) const [myChart, setMyChart] = useState() let textStyle = fontColor ? { textStyle: { color: fontColor } } : {} /**创建myChart实例 */ useEffect(() => { if (ref?.current && (data as any[]).length > 0) { let Xdata: any = [] let Ydatas: any[] = [] data?.forEach((item, index) => { Ydatas.push([]) Object.keys(item).forEach((key: string) => { if (index === 0 && key !== 'legendName') { Xdata.push(key) } if (key !== 'legendName') { Ydatas[index].push(item[key]) } }) }) let myChart = init(ref.current) // 设置是否可缩放 let dataZoom = [] if (dataZoomInside) { dataZoom.push({ type: 'inside' }) } if (dataZoomSlider) { dataZoom.push({ type: 'slider', width: 200, height: 20, top: 0, left: 140, disabled: dataZoomSlider }) } setMyChart(myChart) myChart.setOption({ /**鼠标悬浮 */ tooltip: { trigger: 'axis' }, /**图列 */ legend: { data: (data as { legendName: string, [key: string]: any }[]).map((item: { legendName: string }) => item.legendName), bottom: 0, left: 'center', show: data && data?.length > 1 ? true : false,//数据只有一条就隐藏 ...textStyle }, xAxis: { data: Xdata, boundaryGap: false,//贴边 }, yAxis: [ { type: 'value', axisLabel: {//名称 fontWeight: 900, fontSize: 15, color: '#333333', textBorderColor: '#dfdfdf', textBorderWidth: 1 }, }, ], dataZoom, grid: { left: 100, right: 50, }, ...textStyle, /**工具保存为图片 */ toolbox: { right: 50, // feature: { // saveAsImage: { // title: '保存为图片' // } // } }, title: { text: title || '', left: 'center', bottom: 0, textStyle: { color: '#3946c3', fontSize: 16 }, }, /**线图参数 */ series: (data as { legendName: string, [key: string]: any }[]).map((item: { legendName: string }, index: number) => { return { name: item.legendName,//名称 type: 'line',//图形 data: Ydatas[index],//数据 areaStyle: areaStyle ? {} : undefined,//是否开启块级颜色 color: Array.isArray(color) ? color[index] : color,//颜色 markPoint: {//最大值最小值 data: markPoint ? markPoint : [] }, emphasis: series ? { focus: 'series' } : {}, //堆叠 smooth } }) }); } }, [ref?.current, data]) /**卸载myChart */ useEffect(() => { if ((data as any[])?.length === 0) { if (myChart?.id) { dispose(myChart?.id) } } return () => { if (myChart?.id) { dispose(myChart.id) } } }, [myChart?.id, data]) return <> { (data as any[])?.length > 0 &&
} { (data as any[])?.length === 0 &&
} } /** * 常用option配置//https://echarts.apache.org/next/zh/option.html#legend * @param legend 图例组件 用于用户操作筛选图表展示的组件 * @param title 图标标题 * @param grid 网格控制 可在一个渲染框架中渲染多个图表 或单个图表的间距宽高等值的设定 * @param xAxis grid 中的 x 轴的设置 * @param yAxis grid 中的 y 轴的设置 * @param tooltip 鼠标悬浮的提示组件 * @param series 系列数据配置 */ function useEcharts() { return { Echarts, Map, PieFocus, Line, Bar, BarMonitor, LineMonitor } } export default useEcharts