diff --git a/web/src/components/playground/CodeViewer.js b/web/src/components/playground/CodeViewer.js new file mode 100644 index 00000000..a4d21e8e --- /dev/null +++ b/web/src/components/playground/CodeViewer.js @@ -0,0 +1,194 @@ +import React, { useState } from 'react'; +import { Button, Tooltip, Toast } from '@douyinfe/semi-ui'; +import { Copy } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { copy } from '../../helpers/utils'; + +// VS Code 深色主题样式 +const codeThemeStyles = { + container: { + backgroundColor: '#1e1e1e', + color: '#d4d4d4', + fontFamily: 'Consolas, "Courier New", Monaco, "SF Mono", monospace', + fontSize: '13px', + lineHeight: '1.4', + borderRadius: '8px', + border: '1px solid #3c3c3c', + position: 'relative', + overflow: 'hidden', + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)', + }, + content: { + height: '100%', + overflowY: 'auto', + overflowX: 'auto', + padding: '16px', + margin: 0, + whiteSpace: 'pre', + wordBreak: 'normal', + background: '#1e1e1e', + }, + copyButton: { + position: 'absolute', + top: '12px', + right: '12px', + zIndex: 10, + backgroundColor: 'rgba(45, 45, 45, 0.9)', + border: '1px solid rgba(255, 255, 255, 0.1)', + color: '#d4d4d4', + borderRadius: '6px', + transition: 'all 0.2s ease', + }, + copyButtonHover: { + backgroundColor: 'rgba(60, 60, 60, 0.95)', + borderColor: 'rgba(255, 255, 255, 0.2)', + transform: 'scale(1.05)', + }, + noContent: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + color: '#666', + fontSize: '14px', + fontStyle: 'italic', + backgroundColor: 'var(--semi-color-fill-0)', + borderRadius: '8px', + } +}; + +// 自定义 JSON 高亮器(使用 VS Code 深色主题配色) +const highlightJson = (str) => { + return str.replace( + /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g, + (match) => { + let color = '#b5cea8'; // 数字颜色 (绿色) + if (/^"/.test(match)) { + if (/:$/.test(match)) { + color = '#9cdcfe'; // 键名颜色 (蓝色) + } else { + color = '#ce9178'; // 字符串值颜色 (橙色) + } + } else if (/true|false/.test(match)) { + color = '#569cd6'; // 布尔值颜色 (蓝色) + } else if (/null/.test(match)) { + color = '#569cd6'; // null 值颜色 (蓝色) + } + return `${match}`; + } + ); +}; + +const CodeViewer = ({ content, title, language = 'json' }) => { + const { t } = useTranslation(); + const [copied, setCopied] = useState(false); + const [isHoveringCopy, setIsHoveringCopy] = useState(false); + + const handleCopy = async () => { + try { + let textToCopy = content; + + // 如果是对象,转换为格式化的 JSON 字符串 + if (typeof content === 'object' && content !== null) { + textToCopy = JSON.stringify(content, null, 2); + } + + const success = await copy(textToCopy); + if (success) { + setCopied(true); + Toast.success(t('已复制到剪贴板')); + setTimeout(() => setCopied(false), 2000); + } else { + Toast.error(t('复制失败')); + } + } catch (err) { + Toast.error(t('复制失败')); + console.error('Copy failed:', err); + } + }; + + // 格式化内容 + const getFormattedContent = () => { + if (!content) return ''; + + if (typeof content === 'object') { + try { + return JSON.stringify(content, null, 2); + } catch (e) { + return String(content); + } + } else if (typeof content === 'string') { + // 尝试解析并重新格式化 JSON + try { + const parsed = JSON.parse(content); + return JSON.stringify(parsed, null, 2); + } catch (e) { + return content; + } + } + + return String(content); + }; + + // 获取高亮的 HTML + const getHighlightedContent = () => { + const formattedContent = getFormattedContent(); + + if (language === 'json') { + return highlightJson(formattedContent); + } + + // 对于非 JSON 内容,使用简单的文本高亮 + return formattedContent; + }; + + if (!content) { + return ( +
+ + {title === 'preview' ? t('正在构造请求体预览...') : + title === 'request' ? t('暂无请求数据') : + t('暂无响应数据')} + +
+ ); + } + + return ( +
+ {/* 复制按钮 */} +
setIsHoveringCopy(true)} + onMouseLeave={() => setIsHoveringCopy(false)} + > + +
+ + {/* 代码内容 */} +
+
+ ); +}; + +export default CodeViewer; \ No newline at end of file diff --git a/web/src/components/playground/DebugPanel.js b/web/src/components/playground/DebugPanel.js index 8487d555..8f5a8cef 100644 --- a/web/src/components/playground/DebugPanel.js +++ b/web/src/components/playground/DebugPanel.js @@ -16,6 +16,7 @@ import { Send, } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import CodeViewer from './CodeViewer'; const DebugPanel = ({ debugData, @@ -129,17 +130,11 @@ const DebugPanel = ({ {t('预览请求体')}
} itemKey="preview"> -
- {debugData.previewRequest ? ( -
-                  {JSON.stringify(debugData.previewRequest, null, 2)}
-                
- ) : ( - - {t('正在构造请求体预览...')} - - )} -
+ } itemKey="request"> -
- {debugData.request ? ( - <> -
-                    {JSON.stringify(debugData.request, null, 2)}
-                  
- - ) : ( - - {t('暂无请求数据')} - - )} -
+
} itemKey="response"> -
- {debugData.response ? ( -
-                  {debugData.response}
-                
- ) : ( - - {t('暂无响应数据')} - - )} -
+