From 58d288637adc23ef918618c3e4fa351243d1040a Mon Sep 17 00:00:00 2001 From: gaoziman <2942894660@qq.com> Date: Fri, 19 Dec 2025 20:20:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BB=84=E4=BB=B6):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=89=A7=E8=A1=8C=E7=BB=93=E6=9E=9C=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 CodeExecutionResult 组件展示代码执行输出和图形 - 支持 Base64 图片渲染和点击放大查看 - 显示执行引擎(Pyodide/Piston)和执行时间 - 新增 PyodideLoading 组件显示 Python 环境加载进度 - 支持暗色主题 --- .../features/CodeExecutionResult.tsx | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 src/components/features/CodeExecutionResult.tsx diff --git a/src/components/features/CodeExecutionResult.tsx b/src/components/features/CodeExecutionResult.tsx new file mode 100644 index 0000000..2f7a5bf --- /dev/null +++ b/src/components/features/CodeExecutionResult.tsx @@ -0,0 +1,206 @@ +'use client'; + +import React, { useState } from 'react'; +import Image from 'next/image'; + +interface CodeExecutionResultProps { + /** 执行输出文本 */ + output?: string; + /** 错误信息 */ + error?: string; + /** Base64 编码的图片数组 */ + images?: string[]; + /** 执行语言 */ + language?: string; + /** 执行引擎 */ + engine?: 'pyodide' | 'piston'; + /** 执行时间 (ms) */ + executionTime?: number; + /** 是否执行成功 */ + success?: boolean; +} + +/** + * 代码执行结果组件 + * 支持显示文本输出和图形输出 + */ +export function CodeExecutionResult({ + output, + error, + images, + language, + engine, + executionTime, + success = true, +}: CodeExecutionResultProps) { + const [selectedImage, setSelectedImage] = useState(null); + + const hasOutput = output && output.trim().length > 0; + const hasError = error && error.trim().length > 0; + const hasImages = images && images.length > 0; + + // 如果没有任何内容,不渲染 + if (!hasOutput && !hasError && !hasImages) { + return null; + } + + return ( +
+ {/* 头部信息 */} +
+
+ + + {success ? '执行成功' : '执行失败'} + + {language && ( + + {language} + + )} +
+
+ {engine && ( + + {engine === 'pyodide' ? 'Pyodide' : 'Piston'} + + )} + {executionTime && ( + {executionTime}ms + )} +
+
+ + {/* 图片输出 */} + {hasImages && ( +
+
+ {images.map((img, index) => ( +
setSelectedImage(img)} + > + {`Chart +
+ 图表 {index + 1} +
+
+ ))} +
+
+ )} + + {/* 文本输出 */} + {hasOutput && ( +
+
输出
+
+            {output}
+          
+
+ )} + + {/* 错误信息 */} + {hasError && ( +
+
错误
+
+            {error}
+          
+
+ )} + + {/* 图片放大模态框 */} + {selectedImage && ( +
setSelectedImage(null)} + > +
+ Chart enlarged + +
+
+ )} +
+ ); +} + +/** + * Pyodide 加载状态组件 + */ +interface PyodideLoadingProps { + stage: 'loading' | 'ready' | 'error'; + message: string; + progress?: number; +} + +export function PyodideLoading({ stage, message, progress }: PyodideLoadingProps) { + if (stage === 'ready') { + return null; + } + + return ( +
+ {stage === 'loading' && ( + <> +
+
+
+
+
+
{message}
+ {progress !== undefined && ( +
+
+
+ )} +
+ + )} + {stage === 'error' && ( + <> +
+ + + +
+
{message}
+ + )} +
+ ); +} + +export default CodeExecutionResult;