/** 轻量级 ECharts 封装 — 原生 echarts/core + ResizeObserver 响应式缩放 * 支持 onEvents(常规事件)与 onZrClick(ZRender 底层点击,扩大热区)。 */ import { useEffect, useRef } from "react"; import * as echarts from "echarts/core"; import { LineChart, BarChart, CustomChart } from "echarts/charts"; import { GridComponent, TooltipComponent, LegendComponent, DataZoomComponent, } from "echarts/components"; import { CanvasRenderer } from "echarts/renderers"; import type { EChartsCoreOption } from "echarts/core"; // 按需注册(新增图表/组件时在此追加,避免全量打包) echarts.use([ LineChart, BarChart, CustomChart, GridComponent, TooltipComponent, LegendComponent, DataZoomComponent, CanvasRenderer, ]); type EChartsInstance = ReturnType; interface BaseEChartProps { option: EChartsCoreOption; height?: number | string; className?: string; onEvents?: Record void>; onZrClick?: (chart: EChartsInstance, event: any) => void; } export default function BaseEChart({ option, height = 380, className, onEvents, onZrClick }: BaseEChartProps) { const containerRef = useRef(null); const chartRef = useRef(null); const eventsRef = useRef(onEvents); eventsRef.current = onEvents; const onZrClickRef = useRef(onZrClick); onZrClickRef.current = onZrClick; // 初始化(仅一次):init → setOption → 注册事件 → ResizeObserver useEffect(() => { const el = containerRef.current; if (!el) return; const chart = echarts.init(el); chartRef.current = chart; chart.setOption(option); // 常规 ECharts 事件(读 eventsRef,保证始终用最新 handler) Object.keys(eventsRef.current ?? {}).forEach((event) => { chart.on(event, (params: any) => { eventsRef.current?.[event]?.(params); }); }); // ZRender 底层点击(点击列阴影/背景也能触发,热区更大) chart.getZr().on("click", (e: any) => { onZrClickRef.current?.(chart, e); }); const observer = new ResizeObserver(() => chart.resize()); observer.observe(el); return () => { observer.disconnect(); chart.dispose(); chartRef.current = null; }; // 仅挂载时执行一次;option 更新交给下面的 effect // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // option 更新:整表替换,避免 merge 残留旧 series useEffect(() => { chartRef.current?.setOption(option, { notMerge: true }); }, [option]); return
; }