From b68687c61e43729e236783d8da37a82b13ffc302 Mon Sep 17 00:00:00 2001
From: hongjli <3117313295@qq.com>
Date: 星期一, 28 四月 2025 11:22:26 +0800
Subject: [PATCH] 渲染echarts图优化

---
 src/app/chat/page.tsx |  237 +++++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 173 insertions(+), 64 deletions(-)

diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx
index 8656771..7c8d06c 100644
--- a/src/app/chat/page.tsx
+++ b/src/app/chat/page.tsx
@@ -44,8 +44,11 @@
 function EchartsRenderer({ code }: { code: string }) {
   const [error, setError] = useState<string | null>(null);
   const [isLoading, setIsLoading] = useState(true);
+  const [isModalOpen, setIsModalOpen] = useState(false);
   const chartContainerRef = useRef<HTMLDivElement>(null);
+  const modalChartRef = useRef<HTMLDivElement>(null);
   const hasRenderedRef = useRef<boolean>(false);
+  const chartInstanceRef = useRef<any>(null);
   
   // 鍒濆鍖朌OM瀹瑰櫒
   useEffect(() => {
@@ -85,105 +88,163 @@
       // 鏍囪涓哄凡娓叉煋
       hasRenderedRef.current = true;
       
-      // 绠�鍖栫殑鍥捐〃鍒濆鍖栭�昏緫
-      const loadChart = async () => {
+      // 鍒濆鍖栧浘琛�
+      const initializeChart = async () => {
         try {
-          // 鍔ㄦ�佸鍏charts
           const echarts = await import('echarts');
           
-          // 鍐嶆纭DOM鍏冪礌瀛樺湪骞跺叿鏈夋湁鏁堝昂瀵�
-          if (!chartContainerRef.current || 
-              !chartContainerRef.current.offsetWidth || 
-              !chartContainerRef.current.offsetHeight) {
-            console.error('鍥捐〃瀹瑰櫒灏哄鏃犳晥锛岃烦杩囧垵濮嬪寲');
-            setError('鍥捐〃瀹瑰櫒灏哄鏃犳晥');
-            setIsLoading(false);
-            return;
-          }
+          // 鍒濆鍖栧浘琛�
+          const chartInstance = echarts.init(chartContainerRef.current);
+          chartInstanceRef.current = chartInstance;
           
-          // 鍒濆鍖栧浘琛� - 娣诲姞娓叉煋鍣ㄧ被鍨嬪弬鏁�
-          const chartInstance = echarts.init(
-            chartContainerRef.current, 
-            undefined, 
-            { renderer: 'canvas', devicePixelRatio: window.devicePixelRatio || 1 }
-          );
-          
+          // 灏濊瘯瑙f瀽鍜岃缃浘琛ㄩ�夐」
           try {
-            // 瀹夊叏澶勭悊浠g爜
-            const safeCode = code.replace(/window\.option/g, '_uniqueOptionVar');
-            
-            // 鎵ц浠g爜鑾峰彇閰嶇疆
-            const getFinalOption = new Function(`
+            const safeCode = code.replace(/window\.option/g, 'option');
+            const safeFunc = new Function(`
               "use strict";
               let option;
-              let _uniqueOptionVar;
               try {
                 ${safeCode}
-                return option || _uniqueOptionVar;
+                return option;
               } catch (e) {
-                console.error("ECharts鍐呴儴鎵ц閿欒:", e);
+                console.error("鍥捐〃浠g爜鎵ц閿欒:", e);
                 return null;
               }
             `);
             
-            const chartOption = getFinalOption();
+            const chartOption = safeFunc();
             
-            // 搴旂敤閰嶇疆
             if (chartOption) {
               chartInstance.setOption(chartOption);
+              setError(null);
             } else {
-              // 搴旂敤榛樿閰嶇疆
-              chartInstance.setOption({
-                title: { text: '鍥捐〃鏁版嵁瑙f瀽澶辫触' },
-                xAxis: { type: 'category', data: ['鏃犳暟鎹�'] },
-                yAxis: { type: 'value' },
-                series: [{ data: [0], type: 'bar' }]
-              });
+              throw new Error("鏃犳硶鑾峰彇鍥捐〃閰嶇疆");
             }
-            
-            setIsLoading(false);
           } catch (e) {
             console.error('鍥捐〃浠g爜鎵ц閿欒:', e);
-            setError('鍥捐〃瑙f瀽澶辫触');
-            setIsLoading(false);
+            setError('鍥捐〃閰嶇疆閿欒');
+            
+            // 璁剧疆涓�涓粯璁ゅ浘琛ㄤ互鏄剧ず閿欒
+            chartInstance.setOption({
+              title: { text: '鍥捐〃閰嶇疆閿欒' },
+              xAxis: { type: 'category', data: ['閿欒'] },
+              yAxis: { type: 'value' },
+              series: [{ data: [0], type: 'bar' }]
+            });
           }
           
-          // 绐楀彛澶у皬鏀瑰彉鏃讹紝閲嶈鍥捐〃澶у皬
+          // 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉鍣�
           const handleResize = () => chartInstance.resize();
           window.addEventListener('resize', handleResize);
           
-          // 缁勪欢鍗歌浇鏃舵竻鐞�
+          // 娓呯悊鍑芥暟
           return () => {
             window.removeEventListener('resize', handleResize);
-            try {
-              chartInstance.dispose();
-            } catch (e) {}
+            chartInstance.dispose();
           };
         } catch (e) {
-          console.error('鍥捐〃鍔犺浇澶辫触:', e);
+          console.error('ECharts鍔犺浇澶辫触:', e);
           setError('鍥捐〃搴撳姞杞藉け璐�');
+        } finally {
           setIsLoading(false);
         }
       };
       
-      // 鎵ц鍔犺浇
-      loadChart();
+      initializeChart();
     }, 500); // 澧炲姞寤惰繜锛岀‘淇滵OM瀹屽叏娓叉煋
     
     return () => clearTimeout(timer);
   }, [code]);
   
+  // 褰撳叏灞忕姸鎬佸彉鍖栨椂閲嶆柊璋冩暣鍥捐〃澶у皬
+  useEffect(() => {
+    if (chartInstanceRef.current) {
+      setTimeout(() => {
+        chartInstanceRef.current.resize();
+      }, 300); // 缁橠OM涓�浜涙椂闂存潵鏇存柊
+    }
+  }, [isModalOpen]);
+  
+  // 澶勭悊鍏ㄥ睆鍒囨崲
+  const toggleModal = useCallback(() => {
+    setIsModalOpen(prev => !prev);
+  }, []);
+
+  // 澶勭悊妯℃�佺獥鍙g殑鍥捐〃
+  useEffect(() => {
+    if (!isModalOpen || !modalChartRef.current) return;
+    
+    const initModalChart = async () => {
+      try {
+        const echarts = await import('echarts');
+        
+        // 鍒濆鍖栨ā鎬佺獥鍙d腑鐨勫浘琛�
+        const modalChartInstance = echarts.init(modalChartRef.current);
+        
+        // 濡傛灉涓诲浘琛ㄥ凡缁忓垵濮嬪寲锛屽鐢ㄥ叾閰嶇疆
+        if (chartInstanceRef.current) {
+          const option = chartInstanceRef.current.getOption();
+          modalChartInstance.setOption(option);
+        } else {
+          // 濡傛灉涓诲浘琛ㄦ病鏈夊垵濮嬪寲锛屽皾璇曚粠浠g爜鍒濆鍖�
+          try {
+            const safeCode = code.replace(/window\.option/g, 'option');
+            const safeFunc = new Function(`
+              "use strict";
+              let option;
+              try {
+                ${safeCode}
+                return option;
+              } catch (e) {
+                console.error("妯℃�佸浘琛ㄤ唬鐮佹墽琛岄敊璇�:", e);
+                return null;
+              }
+            `);
+            
+            const chartOption = safeFunc();
+            
+            if (chartOption) {
+              modalChartInstance.setOption(chartOption);
+            } else {
+              throw new Error("鏃犳硶鑾峰彇妯℃�佸浘琛ㄩ厤缃�");
+            }
+          } catch (e) {
+            console.error('妯℃�佸浘琛ㄤ唬鐮佹墽琛岄敊璇�:', e);
+            
+            // 璁剧疆涓�涓粯璁ゅ浘琛�
+            modalChartInstance.setOption({
+              title: { text: '鍥捐〃閰嶇疆閿欒' },
+              xAxis: { type: 'category', data: ['閿欒'] },
+              yAxis: { type: 'value' },
+              series: [{ data: [0], type: 'bar' }]
+            });
+          }
+        }
+        
+        // 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉鍣�
+        const handleResize = () => modalChartInstance.resize();
+        window.addEventListener('resize', handleResize);
+        
+        // 杩斿洖娓呯悊鍑芥暟
+        return () => {
+          window.removeEventListener('resize', handleResize);
+          modalChartInstance.dispose();
+        };
+      } catch (e) {
+        console.error('妯℃�佸浘琛ㄥ垵濮嬪寲澶辫触:', e);
+      }
+    };
+    
+    // 寤惰繜涓�鐐规墽琛岋紝纭繚DOM宸叉洿鏂板畬鎴�
+    const timer = setTimeout(initModalChart, 100);
+    return () => clearTimeout(timer);
+  }, [isModalOpen, code]);
+  
   // 绠�鍖栫殑娓叉煋閫昏緫锛岀‘淇濆鍣ㄦ湁鏄庣‘鐨勫昂瀵�
   return (
-    <div className="relative my-4 echart-container">
-      {error && (
-        <div className="absolute top-0 left-0 right-0 bg-red-50 border border-red-200 text-red-600 px-3 py-2 rounded-md text-xs z-10">
-          {error}
-        </div>
-      )}
-      
+    <div className="relative my-4">
       {isLoading && (
-        <div className="absolute inset-0 flex items-center justify-center bg-white bg-opacity-70 z-10">
+        <div className="absolute inset-0 flex items-center justify-center bg-white/70 z-10">
           <div className="flex flex-col items-center">
             <div className="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-blue-500 mb-3"></div>
             <p className="text-gray-500">鍥捐〃鍔犺浇涓�...</p>
@@ -191,17 +252,65 @@
         </div>
       )}
       
-      <div 
-        ref={chartContainerRef} 
+      {error && (
+        <div className="absolute top-0 left-0 right-0 bg-red-50 border border-red-200 text-red-600 px-3 py-2 rounded-md text-xs z-10">
+          {error}
+        </div>
+      )}
+      
+      {/* 鍥捐〃瀹瑰櫒 */}
+      <div
+        ref={chartContainerRef}
         className="w-full bg-white border border-gray-200 rounded-lg overflow-hidden chart-container"
         style={{ 
-          height: '400px',
-          minHeight: '300px', // 纭繚鏈夋渶灏忛珮搴�
-          visibility: 'visible', // 纭繚瀹瑰櫒鍙
-          position: 'relative' // 鍒涘缓BFC
+          height: '300px',
+          minHeight: '300px',
+          visibility: 'visible',
+          position: 'relative'
         }}
-        data-echarts-container="true" // 娣诲姞鏍囪瘑灞炴��
+        data-echarts-container="true"
       />
+      
+      {/* 鎵撳紑寮圭獥鎸夐挳 */}
+      <button 
+        onClick={() => setIsModalOpen(true)} 
+        className="absolute top-2 right-2 bg-white/90 hover:bg-white p-2 rounded-full shadow-md z-20 transition-all duration-300 hover:scale-110 cursor-pointer"
+        title="鍦ㄥ脊绐椾腑鏌ョ湅"
+      >
+        <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 3h6v6m0-6L14 9M9 21H3v-6m0 6l7-7" />
+        </svg>
+      </button>
+      
+      {/* 寮瑰嚭寮忓璇濇 */}
+      {isModalOpen && (
+        <div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/50 p-4">
+          <div className="bg-white rounded-lg w-[90vw] max-w-6xl h-[80vh] relative flex flex-col">
+            {/* 瀵硅瘽妗嗗ご閮� */}
+            <div className="flex justify-between items-center p-4 border-b">
+              <h3 className="text-xl font-semibold text-gray-800">鏁版嵁鍙鍖栧浘琛�</h3>
+              <button 
+                onClick={() => setIsModalOpen(false)} 
+                className="p-1 rounded-full hover:bg-gray-100 cursor-pointer"
+                aria-label="鍏抽棴"
+              >
+                <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
+                </svg>
+              </button>
+            </div>
+            
+            {/* 瀵硅瘽妗嗕富浣� - 鍥捐〃 */}
+            <div className="flex-1 overflow-hidden p-4">
+              <div 
+                ref={modalChartRef} 
+                className="w-full h-full" 
+                data-echarts-modal
+              ></div>
+            </div>
+          </div>
+        </div>
+      )}
     </div>
   );
 }
@@ -319,7 +428,7 @@
             <button
               onClick={handleSend}
               disabled={isStreaming || !inputText.trim() || !isMessageComplete}
-              className="h-12 px-5 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-medium rounded-xl transition-all duration-300 flex items-center gap-2 shadow-lg shadow-blue-500/20 hover:shadow-blue-500/30 hover:scale-[1.02] active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none disabled:shadow-none"
+              className="h-12 px-5 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-medium rounded-xl transition-all duration-300 flex items-center gap-2 shadow-lg shadow-blue-500/20 hover:shadow-blue-500/30 hover:scale-[1.02] active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none disabled:shadow-none cursor-pointer"
             >
               <span>{isStreaming ? '鍥炲涓�...' : (isMessageComplete ? '鍙戦��' : '澶勭悊涓�...')}</span>
               <svg xmlns="http://www.w3.org/2000/svg" className={`h-4 w-4 transform rotate-45 ${isStreaming ? 'animate-pulse' : ''}`} viewBox="0 0 20 20" fill="currentColor">

--
Gitblit v1.9.3