// src/components/ChatBot/MarkdownWithCharts.js // 支持 ECharts 图表的 Markdown 渲染组件 import React from 'react'; import { Box, Alert, AlertIcon, Text, VStack, Code } from '@chakra-ui/react'; import ReactMarkdown from 'react-markdown'; import { EChartsRenderer } from './EChartsRenderer'; import { logger } from '@utils/logger'; /** * 解析 Markdown 内容,提取 ECharts 代码块 * @param {string} markdown - Markdown 文本 * @returns {Array} - 包含文本和图表的数组 */ const parseMarkdownWithCharts = (markdown) => { if (!markdown) return []; const parts = []; const echartsRegex = /```echarts\s*\n([\s\S]*?)```/g; let lastIndex = 0; let match; while ((match = echartsRegex.exec(markdown)) !== null) { // 添加代码块前的文本 if (match.index > lastIndex) { const textBefore = markdown.substring(lastIndex, match.index).trim(); if (textBefore) { parts.push({ type: 'text', content: textBefore }); } } // 添加 ECharts 配置 const chartConfig = match[1].trim(); parts.push({ type: 'chart', content: chartConfig }); lastIndex = match.index + match[0].length; } // 添加剩余文本 if (lastIndex < markdown.length) { const textAfter = markdown.substring(lastIndex).trim(); if (textAfter) { parts.push({ type: 'text', content: textAfter }); } } // 如果没有找到图表,返回整个 markdown 作为文本 if (parts.length === 0) { parts.push({ type: 'text', content: markdown }); } return parts; }; /** * 支持 ECharts 图表的 Markdown 渲染组件 * @param {string} content - Markdown 文本 */ export const MarkdownWithCharts = ({ content }) => { const parts = parseMarkdownWithCharts(content); return ( {parts.map((part, index) => { if (part.type === 'text') { // 渲染普通 Markdown return ( ( {children} ), h1: ({ children }) => ( {children} ), h2: ({ children }) => ( {children} ), h3: ({ children }) => ( {children} ), ul: ({ children }) => ( {children} ), ol: ({ children }) => ( {children} ), li: ({ children }) => ( {children} ), code: ({ inline, children }) => inline ? ( {children} ) : ( {children} ), blockquote: ({ children }) => ( {children} ), }} > {part.content} ); } else if (part.type === 'chart') { // 渲染 ECharts 图表 try { const chartOption = JSON.parse(part.content); return ( ); } catch (error) { logger.error('解析 ECharts 配置失败', error, part.content); return ( 图表配置解析失败 {part.content.substring(0, 200)} {part.content.length > 200 ? '...' : ''} ); } } return null; })} ); };