From 70902a2541036e3bc8e0f7e25d352c563c24ad86 Mon Sep 17 00:00:00 2001 From: gaoziman <2942894660@qq.com> Date: Sun, 21 Dec 2025 03:20:31 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=89=A7=E8=A1=8C=E7=BB=93=E6=9E=9C):?= =?UTF-8?q?=20=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 CSS 变量适配亮暗主题 - 添加结果折叠/展开功能 - 添加输出复制功能 - 优化成功/错误状态显示样式 - 显示执行时间和引擎信息 --- .../features/CodeExecutionResult.tsx | 212 +++++++++++++----- 1 file changed, 156 insertions(+), 56 deletions(-) diff --git a/src/components/features/CodeExecutionResult.tsx b/src/components/features/CodeExecutionResult.tsx index 2f7a5bf..74fb401 100644 --- a/src/components/features/CodeExecutionResult.tsx +++ b/src/components/features/CodeExecutionResult.tsx @@ -2,6 +2,16 @@ import React, { useState } from 'react'; import Image from 'next/image'; +import { Check, X, Copy, ChevronDown, ChevronUp, Loader2, Clock } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { ExecutionResult, EngineType } from '@/lib/code-runner/types'; + +// 引擎名称映射 +const engineLabels: Record = { + sandbox: 'JavaScript', + pyodide: 'Python', + remote: '云端', +}; interface CodeExecutionResultProps { /** 执行输出文本 */ @@ -13,7 +23,7 @@ interface CodeExecutionResultProps { /** 执行语言 */ language?: string; /** 执行引擎 */ - engine?: 'pyodide' | 'piston'; + engine?: EngineType | 'piston'; /** 执行时间 (ms) */ executionTime?: number; /** 是否执行成功 */ @@ -34,6 +44,8 @@ export function CodeExecutionResult({ success = true, }: CodeExecutionResultProps) { const [selectedImage, setSelectedImage] = useState(null); + const [isExpanded, setIsExpanded] = useState(true); + const [copied, setCopied] = useState(false); const hasOutput = output && output.trim().length > 0; const hasError = error && error.trim().length > 0; @@ -44,78 +56,166 @@ export function CodeExecutionResult({ return null; } + // 复制输出 + const handleCopy = async () => { + const textToCopy = success ? output : error || ''; + try { + await navigator.clipboard.writeText(textToCopy || ''); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy:', err); + } + }; + + // 获取引擎显示名称 + const getEngineLabel = () => { + if (!engine) return ''; + if (engine === 'piston') return '云端'; + return engineLabels[engine] || engine; + }; + return ( -
+
{/* 头部信息 */} -
+
setIsExpanded(!isExpanded)} + >
- - - {success ? '执行成功' : '执行失败'} + {/* 状态图标 */} + {success ? ( + + ) : ( + + )} + {/* 状态文本 */} + + {success ? '输出' : '错误'} + {/* 语言 */} {language && ( - + {language} )} -
-
- {engine && ( - - {engine === 'pyodide' ? 'Pyodide' : 'Piston'} + {/* 执行时间 */} + {executionTime !== undefined && ( + + + {executionTime}ms )} - {executionTime && ( - {executionTime}ms +
+ +
+ {/* 引擎标签 */} + {engine && ( + + {getEngineLabel()} + )} + {/* 复制按钮 */} + + {/* 展开/收起按钮 */} +
- {/* 图片输出 */} - {hasImages && ( -
-
- {images.map((img, index) => ( -
setSelectedImage(img)} - > - {`Chart -
- 图表 {index + 1} -
+ {/* 内容区域(可折叠) */} + {isExpanded && ( + <> + {/* 图片输出 */} + {hasImages && ( +
+
+ {images.map((img, index) => ( +
setSelectedImage(img)} + > + {`Chart +
+ 图表 {index + 1} +
+
+ ))}
- ))} -
-
- )} +
+ )} - {/* 文本输出 */} - {hasOutput && ( -
-
输出
-
-            {output}
-          
-
- )} + {/* 文本输出 */} + {hasOutput && ( +
+
+                {output}
+              
+
+ )} - {/* 错误信息 */} - {hasError && ( -
-
错误
-
-            {error}
-          
-
+ {/* 错误信息 */} + {hasError && ( +
+
+                {error}
+              
+
+ )} + )} {/* 图片放大模态框 */}