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 |  628 +++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 387 insertions(+), 241 deletions(-)

diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx
index 5530946..7c8d06c 100644
--- a/src/app/chat/page.tsx
+++ b/src/app/chat/page.tsx
@@ -43,232 +43,274 @@
 // 娣诲姞鐢ㄤ簬瑙f瀽鍜屾覆鏌� ECharts 鐨勭粍浠�
 function EchartsRenderer({ code }: { code: string }) {
   const [error, setError] = useState<string | null>(null);
-  const [isLoaded, setIsLoaded] = useState(false);
+  const [isLoading, setIsLoading] = useState(true);
+  const [isModalOpen, setIsModalOpen] = useState(false);
   const chartContainerRef = useRef<HTMLDivElement>(null);
-  const codeRef = useRef<string>(code);
-  const renderAttemptedRef = useRef(false);
+  const modalChartRef = useRef<HTMLDivElement>(null);
+  const hasRenderedRef = useRef<boolean>(false);
+  const chartInstanceRef = useRef<any>(null);
   
-  // 鏇存柊浠g爜寮曠敤
+  // 鍒濆鍖朌OM瀹瑰櫒
   useEffect(() => {
-    codeRef.current = code;
-  }, [code]);
-
-  // 寤惰繜鍒濆鍖栫洿鍒版秷鎭ǔ瀹�
-  useEffect(() => {
-    // 涓虹‘淇濇祦寮忓唴瀹圭ǔ瀹氾紝璁剧疆寤惰繜
-    const timer = setTimeout(() => {
-      setIsLoaded(true);
-    }, 500);
-    
-    return () => clearTimeout(timer);
-  }, []);
-
-  // 涓昏娓叉煋閫昏緫 - 浠呭湪缁勪欢鏍囪涓哄凡鍔犺浇鍚庢墽琛�
-  useEffect(() => {
-    // 濡傛灉缁勪欢鏈爣璁颁负宸插姞杞芥垨宸茬粡灏濊瘯杩囨覆鏌擄紝鍒欓��鍑�
-    if (!isLoaded || renderAttemptedRef.current) return;
-    
-    // 纭繚鍙湪瀹㈡埛绔墽琛�
-    if (typeof window === 'undefined' || !chartContainerRef.current) return;
-    
-    // 鏍囪涓哄凡灏濊瘯娓叉煋锛岄槻姝㈤噸澶嶆覆鏌�
-    renderAttemptedRef.current = true;
-    
-    // 闃叉浠g爜鎵ц涓殑鍙橀噺鍐茬獊
-    const safeCode = codeRef.current.replace(/window\.option/g, '_uniqueOptionVar');
-    
-    // 绛夊緟DOM鏇存柊瀹屾垚
-    setTimeout(() => {
-      let chartInstance: any = null;
+    // 纭繚瀹瑰櫒鍑嗗濂戒簡
+    if (chartContainerRef.current) {
+      // 璁剧疆涓�涓垵濮嬮珮搴﹀拰瀹藉害锛岄伩鍏�"invalid dom"閿欒
+      const container = chartContainerRef.current;
+      container.style.width = '100%';
+      container.style.height = '400px';
       
-      const initChart = async () => {
+      // 娣诲姞涓存椂鍐呭锛岀‘淇滵OM娓叉煋瀹屾垚
+      container.innerHTML = '<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;"><span>姝e湪鍑嗗鍥捐〃...</span></div>';
+    }
+  }, []);
+  
+  // 涓�娆℃�у姞杞藉浘琛紝涓嶈繘琛屽姩鎬佹洿鏂�
+  useEffect(() => {
+    // 濡傛灉宸茬粡娓叉煋杩囷紝璺宠繃
+    if (hasRenderedRef.current) {
+      return;
+    }
+    
+    setIsLoading(true);
+    
+    // 纭繚DOM鍏冪礌宸茬粡瀹屽叏鎸傝浇鍜屾覆鏌�
+    const timer = setTimeout(() => {
+      // 纭繚DOM鍏冪礌浠嶇劧瀛樺湪
+      if (!chartContainerRef.current) {
+        console.error('鍥捐〃瀹瑰櫒涓嶅瓨鍦紝璺宠繃鍒濆鍖�');
+        setIsLoading(false);
+        return;
+      }
+      
+      // 娓呴櫎涓存椂鍐呭
+      chartContainerRef.current.innerHTML = '';
+      
+      // 鏍囪涓哄凡娓叉煋
+      hasRenderedRef.current = true;
+      
+      // 鍒濆鍖栧浘琛�
+      const initializeChart = async () => {
         try {
-          // 鍔ㄦ�佸鍏charts
           const echarts = await import('echarts');
           
-          if (!chartContainerRef.current) {
-            console.warn('鍥捐〃瀹瑰櫒宸蹭笉瀛樺湪');
-            return;
-          }
-          
           // 鍒濆鍖栧浘琛�
-          chartInstance = echarts.init(chartContainerRef.current);
+          const chartInstance = echarts.init(chartContainerRef.current);
+          chartInstanceRef.current = chartInstance;
           
-          // 浣跨敤Function鏋勯�犲櫒锛屼絾娣诲姞棰濆鐨勪繚鎶�
+          // 灏濊瘯瑙f瀽鍜岃缃浘琛ㄩ�夐」
           try {
-            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 {
-              console.warn('鏈幏鍙栧埌鏈夋晥鐨勫浘琛ㄩ厤缃紝浣跨敤澶囩敤閰嶇疆');
-              useFallbackOption(chartInstance);
+              throw new Error("鏃犳硶鑾峰彇鍥捐〃閰嶇疆");
             }
           } catch (e) {
-            console.error('鎵ц鍥捐〃浠g爜閿欒:', e);
-            setError(e instanceof Error ? e.message : '鍥捐〃浠g爜鎵ц閿欒');
-            useFallbackOption(chartInstance);
-          }
-          
-          // 娣诲姞鍝嶅簲寮忚皟鏁�
-          const handleResize = () => {
-            chartInstance && chartInstance.resize();
-          };
-          
-          window.addEventListener('resize', handleResize);
-          
-          // 纭繚缁勪欢鍗歌浇鏃舵竻鐞嗚祫婧�
-          return () => {
-            window.removeEventListener('resize', handleResize);
-            if (chartInstance) {
-              try {
-                chartInstance.dispose();
-              } catch (e) {
-                console.warn('鍥捐〃瀹炰緥閿�姣佸け璐�', e);
-              }
-            }
-          };
-        } catch (e) {
-          console.error('鍥捐〃鍒濆鍖栧け璐�:', e);
-          setError('鍔犺浇鍥捐〃搴撳け璐�');
-          return () => {};
-        }
-      };
-      
-      // 澶囩敤閰嶇疆鍑芥暟
-      const useFallbackOption = (instance: any) => {
-        try {
-          // 绠�鍗曠殑澶囩敤閰嶇疆
-          const fallbackOption = {
-            title: {
-              text: '鏁版嵁鍙鍖栧浘琛�',
-              subtext: '(鍘熷浠g爜瑙f瀽澶辫触锛屾樉绀哄鐢ㄥ浘琛�)'
-            },
-            tooltip: {
-              trigger: 'axis'
-            },
-            legend: {
-              data: ['鏁版嵁']
-            },
-            xAxis: {
-              type: 'category',
-              data: ['椤圭洰1', '椤圭洰2', '椤圭洰3', '椤圭洰4', '椤圭洰5']
-            },
-            yAxis: {
-              type: 'value'
-            },
-            series: [{
-              name: '鏁版嵁',
-              type: 'bar',
-              data: [5, 20, 36, 10, 10]
-            }]
-          };
-          
-          // 妫�娴嬫槸鍚︽槸鐑姏鍥句唬鐮�
-          if (safeCode.includes('heatmap') || 
-              safeCode.includes('visualMap') || 
-              safeCode.includes('椋庨櫓')) {
-            (fallbackOption.series as any) = [{
-              type: 'heatmap',
-              name: '椋庨櫓鍊�',  // 娣诲姞缂哄け鐨刵ame灞炴��
-              data: [
-                [0, 0, 5], [0, 1, 7], [0, 2, 3],
-                [1, 0, 7], [1, 1, 8], [1, 2, 6],
-                [2, 0, 9], [2, 1, 10], [2, 2, 8]
-              ] as any[]
-            }];
+            console.error('鍥捐〃浠g爜鎵ц閿欒:', e);
+            setError('鍥捐〃閰嶇疆閿欒');
             
-            fallbackOption.title.text = 'VIP瀹㈡埛璁㈠崟浜や粯椋庨櫓鐑姏鍥�';
-            fallbackOption.title.subtext = '鏁版嵁瑙f瀽澶辫触锛屾樉绀虹ず渚嬬儹鍔涘浘';
-            
-            // 娣诲姞鐑姏鍥炬墍闇�鐨勫叾浠栭厤缃�
-            Object.assign(fallbackOption, {
-              tooltip: {
-                position: 'top'
-              },
-              xAxis: {
-                type: 'category',
-                data: ['浣�', '涓�', '楂�'],
-                name: '寤惰繜椋庨櫓'
-              },
-              yAxis: {
-                type: 'category',
-                data: ['浣�', '涓�', '楂�'],
-                name: '杩濈害鎴愭湰'
-              },
-              visualMap: {
-                min: 1,
-                max: 10,
-                calculable: true,
-                orient: 'horizontal',
-                left: 'center',
-                bottom: '15%'
-              }
+            // 璁剧疆涓�涓粯璁ゅ浘琛ㄤ互鏄剧ず閿欒
+            chartInstance.setOption({
+              title: { text: '鍥捐〃閰嶇疆閿欒' },
+              xAxis: { type: 'category', data: ['閿欒'] },
+              yAxis: { type: 'value' },
+              series: [{ data: [0], type: 'bar' }]
             });
           }
           
-          // 璁剧疆澶囩敤閰嶇疆
-          instance.setOption(fallbackOption);
+          // 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉鍣�
+          const handleResize = () => chartInstance.resize();
+          window.addEventListener('resize', handleResize);
+          
+          // 娓呯悊鍑芥暟
+          return () => {
+            window.removeEventListener('resize', handleResize);
+            chartInstance.dispose();
+          };
         } catch (e) {
-          console.error('搴旂敤澶囩敤閰嶇疆澶辫触:', e);
+          console.error('ECharts鍔犺浇澶辫触:', e);
+          setError('鍥捐〃搴撳姞杞藉け璐�');
+        } finally {
+          setIsLoading(false);
         }
       };
       
-      // 鍒濆鍖栧浘琛ㄥ苟鑾峰彇娓呯悊鍑芥暟
-      const cleanupPromise = initChart();
-      
-      // 瀹屽叏閲嶅啓娓呯悊鍑芥暟閫昏緫锛岄伩鍏嶄娇鐢≒romise鐨勪笉纭畾杩斿洖绫诲瀷
-      // 鍒濆鍖栧浘琛�
-      initChart().then(cleanupFn => {
-        // 瀛樺偍娓呯悊鍑芥暟渚涗互鍚庝娇鐢�
-        if (typeof cleanupFn === 'function') {
-          // 浣跨敤ref瀛樺偍娓呯悊鍑芥暟
-          const currentCleanup = cleanupFn;
-          // 缁勪欢鍗歌浇鏃舵墽琛�
-          return () => currentCleanup();
-        }
-      }).catch(e => {
-        console.error('鍒濆鍖栧浘琛ㄥけ璐�:', e);
-      });
-      
-      // 杩斿洖涓�涓┖鐨勬竻鐞嗗嚱鏁帮紝閬垮厤绫诲瀷閿欒
-      return () => {};
-    }, 100);
-  }, [isLoaded]);
+      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">
+      {isLoading && (
+        <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>
+          </div>
+        </div>
+      )}
+      
       {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}
-          <button 
-            className="ml-2 text-red-700 hover:text-red-900" 
-            onClick={() => setError(null)}
-          >
-            脳
-          </button>
         </div>
       )}
-      <div 
-        ref={chartContainerRef} 
-        className="w-full bg-white border border-gray-200 rounded-lg overflow-hidden" 
-        style={{ height: '400px' }}
+      
+      {/* 鍥捐〃瀹瑰櫒 */}
+      <div
+        ref={chartContainerRef}
+        className="w-full bg-white border border-gray-200 rounded-lg overflow-hidden chart-container"
+        style={{ 
+          height: '300px',
+          minHeight: '300px',
+          visibility: 'visible',
+          position: 'relative'
+        }}
+        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>
   );
 }
@@ -322,6 +364,84 @@
   );
 }
 
+// 鍒涘缓鐙珛鐨勮緭鍏ョ粍浠讹紝閬垮厤鐘舵�佸叡浜鑷寸殑閲嶆覆鏌�
+interface ChatInputProps {
+  onSendMessage: () => void;
+  isStreaming: boolean;
+  isMessageComplete: boolean;
+}
+
+function ChatInput({ onSendMessage, isStreaming, isMessageComplete }: ChatInputProps) {
+  // 鍐呴儴鐘舵�侊紝涓庡閮ㄥ畬鍏ㄩ殧绂�
+  const [inputText, setInputText] = useState('');
+  const inputRef = useRef<HTMLTextAreaElement>(null);
+  
+  // 澶勭悊杈撳叆鍙樺寲
+  const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+    setInputText(e.target.value);
+  };
+  
+  // 澶勭悊鎸夐敭浜嬩欢
+  const handleKeyDown = (e: React.KeyboardEvent) => {
+    if (e.key === 'Enter' && !e.shiftKey) {
+      e.preventDefault();
+      handleSend();
+    }
+  };
+  
+  // 澶勭悊鍙戦��
+  const handleSend = () => {
+    if (isStreaming || !inputText.trim() || !isMessageComplete) return;
+    
+    // 閫氱煡鐖剁粍浠跺彂閫佹秷鎭墠鏇存柊娑堟伅鍐呭
+    (window as any).messageToSend = inputText.trim();
+    
+    // 娓呯┖杈撳叆
+    setInputText('');
+    
+    // 璋冪敤鐖剁粍浠剁殑鍙戦�佹柟娉�
+    onSendMessage();
+  };
+  
+  return (
+    <div className="fixed bottom-0 left-0 right-0 bg-gradient-to-t from-white via-white to-white/95 pt-4 pb-6" style={{ zIndex: 50 }}>
+      <div className="max-w-4xl mx-auto px-6">
+        <div className="relative">
+          <div className="absolute -top-6 left-1/2 -translate-x-1/2 w-48 h-[1px] bg-gradient-to-r from-transparent via-gray-200 to-transparent"></div>
+          
+          <div className="flex gap-4 items-start">
+            <div className="flex-1 relative">
+              <textarea
+                ref={inputRef}
+                value={inputText}
+                onChange={handleInputChange}
+                onKeyDown={handleKeyDown}
+                placeholder="杈撳叆娑堟伅..."
+                disabled={isStreaming}
+                className="w-full resize-none rounded-xl border-0 bg-gray-50 px-4 py-3 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-100/50 focus:bg-white min-h-[48px] max-h-32 shadow-inner transition-all duration-300 ease-in-out hover:bg-gray-100/70 disabled:opacity-50 disabled:cursor-not-allowed"
+                style={{ height: '48px' }}
+              />
+              <div className="absolute right-4 bottom-2 text-xs text-gray-400 bg-gray-50 px-2">
+                鎸塃nter鍙戦�侊紝Shift+Enter鎹㈣
+              </div>
+            </div>
+            <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 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">
+                <path d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" />
+              </svg>
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
 export default function Page() {
   const router = useRouter();
   const [apiKey, setApiKey] = useState<string>('');
@@ -352,6 +472,40 @@
 
   // 鍦ㄧ粍浠堕《閮ㄦ坊鍔犱竴涓紩鐢紝鐢ㄤ簬璺熻釜缁勪欢鏄惁宸插嵏杞�
   const isMountedRef = useRef(true);
+
+  // 鍒嗙娑堟伅杈撳叆鐘舵�侊紝閬垮厤瑙﹀彂涓嶅繀瑕佺殑閲嶆覆鏌�
+  const messageInputRef = useRef<HTMLTextAreaElement>(null);
+  const [localMessage, setLocalMessage] = useState('');
+  
+  const handleMessageChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+    // 鍙洿鏂版湰鍦扮姸鎬侊紝涓嶈Е鍙戝叏灞�閲嶆覆鏌�
+    setLocalMessage(e.target.value);
+  };
+
+  // 浠呭湪鍙戦�佹椂鏇存柊鍏ㄥ眬娑堟伅鐘舵��
+  const handleKeyPress = (e: React.KeyboardEvent) => {
+    if (e.key === 'Enter' && !e.shiftKey) {
+      e.preventDefault();
+      // 鏇存柊鍏ㄥ眬鐘舵�佸苟鍙戦��
+      setMessage(localMessage);
+      setTimeout(() => handleSendMessage(), 10);
+    }
+  };
+
+  // 鐐瑰嚮鍙戦�佹寜閽椂
+  const handleSendButtonClick = () => {
+    // 妫�鏌ユ秷鎭槸鍚︿负绌�
+    if (!localMessage.trim() || !isMessageComplete) return;
+    
+    // 鏇存柊鍏ㄥ眬鐘舵�佸苟鍙戦��
+    setMessage(localMessage);
+    setTimeout(() => handleSendMessage(), 10);
+  };
+
+  // 鍚屾娑堟伅鐘舵�佸埌鏈湴鐘舵��
+  useEffect(() => {
+    setLocalMessage(message);
+  }, [message]);
 
   useEffect(() => {
     // 鑾峰彇瀛樺偍鐨凙PI Key
@@ -427,7 +581,9 @@
   };
 
   const handleSendMessage = async () => {
-    if (!message.trim() || !isMessageComplete) return;
+    // 浠巜indow瀵硅薄鑾峰彇娑堟伅鍐呭
+    const inputMessage = (window as any).messageToSend || '';
+    if (!inputMessage.trim() || !isMessageComplete) return;
     if (!apiKey) {
       showErrorMessage('璇峰厛璁剧疆API Key');
       return;
@@ -439,7 +595,7 @@
     // 鍒涘缓鏂版秷鎭�
     const userMessage: Message = {
       role: 'user',
-      content: message.trim(),
+      content: inputMessage.trim(),
       timestamp: Date.now(),
       id: 'user-' + Date.now().toString()
     };
@@ -466,6 +622,9 @@
 
     setCurrentMessageId(assistantMessage.id);
     setMessage('');
+
+    // 娓呴櫎杈撳叆鍐呭
+    (window as any).messageToSend = '';
 
     let controller: AbortController | null = new AbortController();
     
@@ -768,16 +927,6 @@
     });
   }, []);
 
-  // 鏇存柊鍙戦�佹寜閽殑绂佺敤鐘舵��
-  const isSendDisabled = isStreaming || !message.trim() || !isMessageComplete;
-
-  const handleKeyPress = (e: React.KeyboardEvent) => {
-    if (e.key === 'Enter' && !e.shiftKey) {
-      e.preventDefault();
-      handleSendMessage();
-    }
-  };
-
   useEffect(() => {
     // 椤甸潰鍔犺浇鍚庡欢杩熸樉绀烘秷鎭垪琛紝閬垮厤闂儊
     const timer = setTimeout(() => {
@@ -797,6 +946,37 @@
       .dot-animation span:nth-child(1) { animation: dotBounce 1.4s -0.32s infinite ease-in-out; }
       .dot-animation span:nth-child(2) { animation: dotBounce 1.4s -0.16s infinite ease-in-out; }
       .dot-animation span:nth-child(3) { animation: dotBounce 1.4s 0s infinite ease-in-out; }
+      
+      /* 浼樺寲鍥捐〃瀹瑰櫒鏍峰紡锛屼笉鍐嶄娇鐢╥solation鍜寃ill-change */
+      .echart-wrapper {
+        position: relative;
+        z-index: auto;
+      }
+      
+      /* 浣跨敤鏇存俯鍜岀殑鎬ц兘浼樺寲灞炴�э紝閬垮厤灞傜骇閿欒 */
+      canvas {
+        transition: none !important;  
+      }
+      
+      /* 淇杈撳叆妗嗛伄鎸¢棶棰� */
+      .fixed.bottom-0 {
+        z-index: 10;
+      }
+      
+      /* 闃叉鍥捐〃闂儊浣嗕笉褰卞搷鍏朵粬鍏冪礌 */
+      .echart-wrapper > div {
+        transform: translateZ(0);
+        will-change: transform;
+        transition: none !important;
+      }
+      
+      /* 淇echarts娓叉煋闂 */
+      [data-echarts-container] {
+        visibility: visible !important;
+        opacity: 1 !important;
+        width: 100% !important;
+        contain: none !important;
+      }
     `;
     document.head.appendChild(style);
 
@@ -970,15 +1150,6 @@
                           : 'bg-[#E8F4FF] rounded-xl'
                         } inline-block max-w-[85%] relative overflow-hidden`}>
                         
-                        {/* 璋冭瘯淇℃伅 - 绉婚櫎杩欓儴鍒� */}
-                        {/* 
-                        {process.env.NODE_ENV !== 'production' && (
-                          <div className="bg-gray-100 px-2 py-1 text-xs text-gray-500">
-                            闀垮害: {(msg.content || '').length} | 璁℃暟: {forceUpdateCounter}
-                          </div>
-                        )}
-                        */}
-                        
                         {msg.role === 'assistant' && (
                           <>{(() => {
                             // 瑙f瀽娑堟伅鍐呭锛屾彁鍙栨�濊�冮儴鍒�
@@ -1021,7 +1192,9 @@
                                 <div className="p-3">
                                   <div className="text-gray-800 leading-relaxed">
                                     {mainContent ? (
-                                      <MessageCompletionContext.Provider value={isMessageComplete && msg.id === currentMessageId}>
+                                      <MessageCompletionContext.Provider 
+                                        value={msg.id !== currentMessageId || isMessageComplete}
+                                      >
                                         <ReactMarkdown
                                           remarkPlugins={[remarkGfm]}
                                           rehypePlugins={[rehypeRaw, rehypeSanitize]}
@@ -1054,7 +1227,9 @@
                                 </div>
                                 
                                 {/* 鍔犺浇鎸囩ず鍣� */}
-                                {msg.role === 'assistant' && !isMessageComplete && (
+                                {msg.role === 'assistant' && 
+                                  !isMessageComplete && 
+                                  msg.id === currentMessageId && ( // 鍙湪褰撳墠澶勭悊鐨勬秷鎭樉绀哄姞杞芥寚绀哄櫒
                                   <div className="absolute bottom-1 right-2">
                                     <div className="flex space-x-1">
                                       <div className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse delay-0"></div>
@@ -1089,41 +1264,12 @@
           </div>
         </div>
 
-        {/* 杈撳叆鍖哄煙 */}
-        <div className="fixed bottom-0 left-0 right-0 bg-gradient-to-t from-white via-white to-white/95 pt-4 pb-6">
-          <div className="max-w-4xl mx-auto px-6">
-            <div className="relative">
-              <div className="absolute -top-6 left-1/2 -translate-x-1/2 w-48 h-[1px] bg-gradient-to-r from-transparent via-gray-200 to-transparent"></div>
-              
-              <div className="flex gap-4 items-start">
-                <div className="flex-1 relative">
-                  <textarea
-                    value={message}
-                    onChange={(e) => setMessage(e.target.value)}
-                    onKeyDown={handleKeyPress}
-                    placeholder="杈撳叆娑堟伅..."
-                    disabled={isStreaming}
-                    className="w-full resize-none rounded-xl border-0 bg-gray-50 px-4 py-3 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-100/50 focus:bg-white min-h-[48px] max-h-32 shadow-inner transition-all duration-300 ease-in-out hover:bg-gray-100/70 disabled:opacity-50 disabled:cursor-not-allowed"
-                    style={{ height: '48px' }}
-                  />
-                  <div className="absolute right-4 bottom-2 text-xs text-gray-400 bg-gray-50 px-2">
-                    鎸塃nter鍙戦�侊紝Shift+Enter鎹㈣
-                  </div>
-                </div>
-                <button
-                  onClick={handleSendMessage}
-                  disabled={isSendDisabled}
-                  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"
-                >
-                  <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">
-                    <path d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" />
-                  </svg>
-                </button>
-              </div>
-            </div>
-          </div>
-        </div>
+        {/* 杈撳叆鍖哄煙 - 鎶藉彇涓虹嫭绔嬬粍浠� */}
+        <ChatInput 
+          onSendMessage={handleSendMessage} 
+          isStreaming={isStreaming} 
+          isMessageComplete={isMessageComplete} 
+        />
       </div>
     </div>
   );

--
Gitblit v1.9.3