import { Card } from "antd"; import * as echarts from "echarts/core"; import { GridComponent, TooltipComponent, LegendComponent, TitleComponent, } from "echarts/components"; import { BarChart, PieChart, LineChart, BarSeriesOption, PieSeriesOption, LineSeriesOption, } from "echarts/charts"; import { CanvasRenderer } from "echarts/renderers"; import { useRef, useEffect, useState } from "react"; import { ChartComponentProps, ChartData, ValueItem } from "./getChartData"; echarts.use([ GridComponent, TooltipComponent, LegendComponent, TitleComponent, BarChart, PieChart, LineChart, CanvasRenderer, ]); type EChartsOption = echarts.ComposeOption< BarSeriesOption | PieSeriesOption | LineSeriesOption >; const tabList = [ { key: "bar", label: "柱状图" }, { key: "pie", label: "饼图" }, { key: "line", label: "折线图" }, ]; const getOption = (type: string, data: ChartData): EChartsOption => { const commonOption = { title: { text: `${tabList.find((t) => t.key === type)?.label}` }, tooltip: { trigger: "item" }, }; const getValue = (item: number | ValueItem): number => typeof item === "number" ? item : item.value; const getItemStyle = (item: number | ValueItem) => typeof item === "object" ? item.itemStyle : undefined; switch (type) { case "bar": return { ...commonOption, xAxis: { type: "category", data: data.categories }, yAxis: { type: "value" }, series: [ { data: data.values, type: "bar", itemStyle: { color: (params) => { const dataItem = params.data as ValueItem | number; if (typeof dataItem === "object" && dataItem.itemStyle?.color) { return dataItem.itemStyle.color; } // 返回一个默认颜色,比如 ECharts 默认色系中的颜色 return "#5470c6"; }, }, }, ], }; case "pie": return { ...commonOption, series: [ { type: "pie", data: data.values.map((item, i) => ({ name: data.categories[i], value: getValue(item), itemStyle: getItemStyle(item), })), radius: "50%", }, ], }; case "line": return { ...commonOption, xAxis: { type: "category", data: data.categories }, yAxis: { type: "value" }, series: [ { data: data.values.map(getValue), type: "line", smooth: true, areaStyle: {}, itemStyle: { color: (params) => { const dataItem = params.data as ValueItem | number; if (typeof dataItem === "object" && dataItem.itemStyle?.color) { return dataItem.itemStyle.color; } // 返回一个默认颜色,比如 ECharts 默认色系中的颜色 return "#5470c6"; }, }, }, ], }; default: return commonOption; } }; export function ChartComponent({ data = { categories: [], values: [] }, }: ChartComponentProps) { const [activeTabKey, setActiveTabKey] = useState("bar"); const chartRef = useRef<HTMLDivElement>(null); const chartInstance = useRef<echarts.ECharts | null>(null); useEffect(() => { if (!chartRef.current) return; if ( !data?.categories?.length || !data?.values?.length || data.values.some( (item) => typeof item !== "number" && (typeof (item as ValueItem).value !== "number" || ((item as ValueItem).itemStyle && typeof (item as ValueItem).itemStyle?.color !== "string")), ) ) { console.warn("Invalid chart data"); return; } if (chartInstance.current) { chartInstance.current.dispose(); } chartInstance.current = echarts.init(chartRef.current); chartInstance.current.setOption(getOption(activeTabKey, data)); const resizeHandler = () => chartInstance.current?.resize(); window.addEventListener("resize", resizeHandler); return () => { window.removeEventListener("resize", resizeHandler); chartInstance.current?.dispose(); }; }, [activeTabKey, data]); return ( <Card style={{ width: "auto", minHeight: 400 }} tabList={tabList} activeTabKey={activeTabKey} onTabChange={setActiveTabKey} tabProps={{ size: "middle" }} > <div ref={chartRef} style={{ width: "100%", height: 400, minHeight: 400, alignItems: "center", }} /> </Card> ); }