"use client"; import React, { useMemo } from "react"; import ReactECharts from "echarts-for-react"; import * as echarts from "echarts"; import { Box, Paper, Typography, alpha, useTheme } from "@mui/material"; /* ------------------------------------------------------------------ */ /* Inline chart rendered inside a chat message bubble. */ /* Accepts structured data produced by the AI tool_call. */ /* ------------------------------------------------------------------ */ export interface ChatChartSeries { name: string; data: number[]; type?: "line" | "bar"; } export interface ChatInlineChartProps { title?: string; chart_type?: "line" | "bar" | "pie"; x_data?: string[]; series?: ChatChartSeries[]; y_axis_name?: string; x_axis_name?: string; } const COLORS = [ "#5470c6", "#91cc75", "#fac858", "#ee6666", "#73c0de", "#3ba272", "#fc8452", "#9a60b4", "#ea7ccc", ]; export const ChatInlineChart: React.FC = ({ title, chart_type: chartType = "line", x_data: xData, series = [], y_axis_name: yAxisName, x_axis_name: xAxisName, }) => { const theme = useTheme(); const option = useMemo(() => { if (!series.length) return null; /* ---------- Pie chart ---------- */ if (chartType === "pie") { const pieData = series[0]?.data.map((value, i) => ({ name: xData?.[i] ?? `${i}`, value, })) ?? []; return { tooltip: { trigger: "item" }, legend: { top: "bottom", textStyle: { fontSize: 11 } }, series: [ { type: "pie", radius: ["30%", "60%"], data: pieData, emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: "rgba(0, 0, 0, 0.5)", }, }, label: { fontSize: 11 }, }, ], color: COLORS, }; } /* ---------- Line / Bar chart ---------- */ return { tooltip: { trigger: "axis", confine: true }, legend: { top: "top", textStyle: { fontSize: 11 } }, grid: { left: "5%", right: "5%", bottom: "12%", top: title ? "18%" : "14%", containLabel: true, }, xAxis: { type: "category" as const, boundaryGap: chartType === "bar", data: xData ?? [], axisLabel: { fontSize: 10, rotate: xData && xData.length > 10 ? 30 : 0, }, name: xAxisName, }, yAxis: { type: "value" as const, scale: true, axisLabel: { fontSize: 10 }, name: yAxisName, }, dataZoom: xData && xData.length > 20 ? [{ type: "inside", start: 0, end: 100 }] : undefined, series: series.map((s, i) => { const color = COLORS[i % COLORS.length]; return { name: s.name, type: (s.type ?? chartType) as string, data: s.data, symbol: chartType === "line" ? "none" : undefined, smooth: chartType === "line", itemStyle: { color }, ...(chartType === "line" ? { areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: alpha(color, 0.3) }, { offset: 1, color: alpha(color, 0.05) }, ]), opacity: 0.3, }, } : {}), }; }), color: COLORS, }; }, [chartType, xData, series, title, yAxisName, xAxisName]); if (!option) { return ( 图表数据为空 ); } return ( {title && ( {title} )} ); };