Files
vf_react/src/components/ChatBot/EChartsRenderer.js
2025-11-30 15:36:20 +08:00

126 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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"
/>
);
};