126 lines
3.7 KiB
JavaScript
126 lines
3.7 KiB
JavaScript
// src/components/ChatBot/EChartsRenderer.js
|
||
// ECharts 图表渲染组件
|
||
|
||
import React, { useEffect, useRef } from 'react';
|
||
import { Box, useColorModeValue } from '@chakra-ui/react';
|
||
import * as echarts from 'echarts';
|
||
|
||
/**
|
||
* 验证 ECharts 配置是否有效
|
||
*/
|
||
const isValidOption = (option) => {
|
||
if (!option || typeof option !== 'object') return false;
|
||
|
||
// 检查 xAxis 配置
|
||
if (option.xAxis) {
|
||
const xAxis = Array.isArray(option.xAxis) ? option.xAxis[0] : option.xAxis;
|
||
if (xAxis && xAxis.type === 'category' && (!xAxis.data || xAxis.data.length === 0)) {
|
||
// category 类型的 xAxis 必须有数据
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 检查 series 配置
|
||
if (option.series) {
|
||
const series = Array.isArray(option.series) ? option.series : [option.series];
|
||
const hasValidSeries = series.some(s => s && s.data && s.data.length > 0);
|
||
if (!hasValidSeries) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
/**
|
||
* ECharts 图表渲染组件
|
||
* @param {Object} option - ECharts 配置对象
|
||
* @param {number} height - 图表高度(默认 400px)
|
||
* @param {string} variant - 主题变体: 'light' | 'dark' | 'auto' (默认 auto)
|
||
*/
|
||
export const EChartsRenderer = ({ option, height = 400, variant = 'auto' }) => {
|
||
const chartRef = useRef(null);
|
||
const chartInstance = useRef(null);
|
||
|
||
// 系统颜色模式
|
||
const systemBgColor = useColorModeValue('white', 'transparent');
|
||
const systemIsDark = useColorModeValue(false, true);
|
||
|
||
// 根据 variant 决定实际使用的模式
|
||
const isDarkMode = variant === 'dark' ? true : variant === 'light' ? false : systemIsDark;
|
||
const bgColor = variant === 'dark' ? 'transparent' : variant === 'light' ? 'white' : systemBgColor;
|
||
|
||
useEffect(() => {
|
||
if (!chartRef.current || !option) return;
|
||
|
||
// 验证配置是否有效
|
||
if (!isValidOption(option)) {
|
||
console.warn('EChartsRenderer: Invalid or empty chart configuration, skipping render');
|
||
return;
|
||
}
|
||
|
||
// 初始化图表(支持深色模式)
|
||
if (!chartInstance.current) {
|
||
chartInstance.current = echarts.init(chartRef.current, isDarkMode ? 'dark' : null);
|
||
}
|
||
|
||
// 深色模式下的默认文字颜色
|
||
const darkModeTextStyle = isDarkMode
|
||
? {
|
||
textStyle: { color: '#e5e7eb' },
|
||
title: { textStyle: { color: '#f3f4f6' }, ...option?.title },
|
||
legend: { textStyle: { color: '#d1d5db' }, ...option?.legend },
|
||
xAxis: { axisLabel: { color: '#9ca3af' }, axisLine: { lineStyle: { color: '#4b5563' } }, ...option?.xAxis },
|
||
yAxis: { axisLabel: { color: '#9ca3af' }, axisLine: { lineStyle: { color: '#4b5563' } }, splitLine: { lineStyle: { color: '#374151' } }, ...option?.yAxis },
|
||
}
|
||
: {};
|
||
|
||
// 设置默认主题配置
|
||
const defaultOption = {
|
||
backgroundColor: 'transparent',
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
containLabel: true,
|
||
},
|
||
...option,
|
||
...darkModeTextStyle,
|
||
};
|
||
|
||
// 设置图表配置
|
||
chartInstance.current.setOption(defaultOption, true);
|
||
|
||
// 响应式调整大小
|
||
const handleResize = () => {
|
||
chartInstance.current?.resize();
|
||
};
|
||
|
||
window.addEventListener('resize', handleResize);
|
||
|
||
return () => {
|
||
window.removeEventListener('resize', handleResize);
|
||
// chartInstance.current?.dispose(); // 不要销毁,避免重新渲染时闪烁
|
||
};
|
||
}, [option, isDarkMode, variant]);
|
||
|
||
// 组件卸载时销毁图表
|
||
useEffect(() => {
|
||
return () => {
|
||
chartInstance.current?.dispose();
|
||
chartInstance.current = null;
|
||
};
|
||
}, []);
|
||
|
||
return (
|
||
<Box
|
||
ref={chartRef}
|
||
width="100%"
|
||
height={`${height}px`}
|
||
bg={bgColor}
|
||
borderRadius="md"
|
||
boxShadow="sm"
|
||
/>
|
||
);
|
||
};
|