From d2ecbec16ab9d7768b8d07a93d22816507d81833 Mon Sep 17 00:00:00 2001 From: hongjli <3117313295@qq.com> Date: 星期日, 27 四月 2025 20:52:14 +0800 Subject: [PATCH] 渲染echarts图优化 --- src/app/chat/page.tsx | 95 ++++++++++++++++++++++++++++------------------- 1 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index e4d65cd..5530946 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useRef, useCallback } from 'react'; +import { useState, useEffect, useRef, useCallback, useContext, createContext } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import Image from 'next/image'; @@ -10,6 +10,9 @@ import rehypeSanitize from 'rehype-sanitize'; import dynamic from 'next/dynamic'; import { ReactNode } from 'react'; + +// 鍒涘缓涓�涓秷鎭畬鎴愮姸鎬佺殑Context +const MessageCompletionContext = createContext<boolean>(true); // 鍔ㄦ�佸鍏� ECharts锛岀‘淇濆畠鍙湪瀹㈡埛绔覆鏌� const ReactECharts = dynamic(() => import('echarts-for-react'), { ssr: false }); @@ -272,27 +275,41 @@ // 淇敼浠g爜鍧楁覆鏌撶粍浠讹紝娣诲姞鏇村ソ鐨勭被鍨嬫娴� function CodeBlockRenderer({ language, value }: { language: string; value: string }) { - // 鏇寸簿纭湴妫�娴婨Charts浠g爜 - const isEchartsCode = () => { + // 鍒ゆ柇鏄惁鏄疛avaScript浠g爜锛屾娴嬫槸鍚﹀寘鍚浘琛ㄧ浉鍏崇壒寰� + const isEchartsCode = useCallback(() => { if (language !== 'javascript') return false; // 妫�鏌ユ槸鍚﹀寘鍚獷Charts鐗规湁鐨勯厤缃」 - const hasEchartsConfig = - value.includes('option =') || - value.includes('const option') || - value.includes('let option') || - value.includes('series') && - (value.includes('type:') || value.includes('tooltip:') || value.includes('xAxis:')); - - return hasEchartsConfig; - }; + return value.includes('option') && + (value.includes('series') || + value.includes('chart') || + value.includes('echarts') || + value.includes('xAxis') || + value.includes('yAxis')); + }, [language, value]); - // 濡傛灉鏄� ECharts 浠g爜鍒欐覆鏌撳浘琛� - if (isEchartsCode()) { + // 妫�鏌ユ秷鎭槸鍚﹀畬鏁� - 閫氳繃鐖剁粍浠朵紶閫掔殑isMessageComplete鐘舵�� + const isComplete = useContext(MessageCompletionContext); + + // 鍦ㄦ秷鎭湭瀹屾垚鏃舵樉绀哄姞杞藉姩鐢� + if (language === 'javascript' && !isComplete) { + return ( + <div className="w-full bg-gray-50 rounded-md my-4 p-6 text-center"> + <div className="flex flex-col items-center justify-center"> + <div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-gray-400 mb-4"></div> + <p className="text-gray-500">浠g爜鍔犺浇涓�...</p> + <p className="text-xs text-gray-400 mt-2">绛夊緟娑堟伅瀹屾垚鍚庡皢娓叉煋鍥捐〃</p> + </div> + </div> + ); + } + + // 娑堟伅瀹屾垚鍚庯紝濡傛灉鏄浘琛ㄤ唬鐮佸垯娓叉煋涓哄浘琛� + if (isEchartsCode() && isComplete) { return <EchartsRenderer code={value} />; } - // 鍚﹀垯鎸夋櫘閫氫唬鐮佸潡娓叉煋 + // 鏅�欽avaScript浠g爜鎴栧叾浠栬瑷�鐨勪唬鐮佸潡锛岀洿鎺ユ樉绀轰唬鐮� return ( <div className="bg-gray-800 rounded-md my-2 overflow-hidden"> <div className="flex items-center justify-between px-4 py-2 border-b border-gray-700"> @@ -1004,30 +1021,32 @@ <div className="p-3"> <div className="text-gray-800 leading-relaxed"> {mainContent ? ( - <ReactMarkdown - remarkPlugins={[remarkGfm]} - rehypePlugins={[rehypeRaw, rehypeSanitize]} - components={{ - // @ts-ignore - ReactMarkdown 缁勪欢绫诲瀷瀹氫箟鐨勫吋瀹规�ч棶棰� - code: ({ node, inline, className, children, ...props }) => { - const match = /language-(\w+)/.exec(className || ''); - const language = match ? match[1] : ''; - const value = String(children).replace(/\n$/, ''); - - if (!inline && match) { - return <CodeBlockRenderer language={language} value={value} />; + <MessageCompletionContext.Provider value={isMessageComplete && msg.id === currentMessageId}> + <ReactMarkdown + remarkPlugins={[remarkGfm]} + rehypePlugins={[rehypeRaw, rehypeSanitize]} + components={{ + // @ts-ignore - ReactMarkdown 缁勪欢绫诲瀷瀹氫箟鐨勫吋瀹规�ч棶棰� + code: ({ node, inline, className, children, ...props }) => { + const match = /language-(\w+)/.exec(className || ''); + const language = match ? match[1] : ''; + const value = String(children).replace(/\n$/, ''); + + if (!inline && match) { + return <CodeBlockRenderer language={language} value={value} />; + } + + return ( + <code className={className} {...props}> + {children} + </code> + ); } - - return ( - <code className={className} {...props}> - {children} - </code> - ); - } - }} - > - {mainContent} - </ReactMarkdown> + }} + > + {mainContent} + </ReactMarkdown> + </MessageCompletionContext.Provider> ) : ( msg.role === 'assistant' && !isMessageComplete ? '澶勭悊鍥炲涓�...' : '' )} -- Gitblit v1.9.3