This commit is contained in:
dengqichen 2025-12-30 15:03:38 +08:00
parent a798dee401
commit 1aa84e9ffe
6 changed files with 42 additions and 9 deletions

View File

@ -73,7 +73,7 @@ export const LogStreamViewer: React.FC<LogStreamViewerProps> = ({
console.log('[LogStreamViewer] Render - status:', currentStatus, 'error:', error, 'shouldShowError:', shouldShowError, 'isConnecting:', isConnecting); console.log('[LogStreamViewer] Render - status:', currentStatus, 'error:', error, 'shouldShowError:', shouldShowError, 'isConnecting:', isConnecting);
return ( return (
<div className={`flex flex-col h-full ${className}`}> <div className={`flex flex-col h-full relative ${className}`}>
{/* 自定义控制栏 */} {/* 自定义控制栏 */}
{customToolbar && ( {customToolbar && (
<div className="flex-shrink-0"> <div className="flex-shrink-0">
@ -82,7 +82,7 @@ export const LogStreamViewer: React.FC<LogStreamViewerProps> = ({
)} )}
{/* 日志显示区域 */} {/* 日志显示区域 */}
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden relative">
{shouldShowError ? ( {shouldShowError ? (
<div className="h-full flex items-center justify-center p-4 bg-gray-950"> <div className="h-full flex items-center justify-center p-4 bg-gray-950">
<div className="text-center max-w-md"> <div className="text-center max-w-md">

View File

@ -12,6 +12,7 @@ export const MonacoLogViewer: React.FC<MonacoLogViewerProps> = ({
fontSize = 12, fontSize = 12,
theme = 'vs-dark', theme = 'vs-dark',
height = '100%', height = '100%',
wordWrap = false,
onReady, onReady,
className = '', className = '',
style = {}, style = {},
@ -27,6 +28,15 @@ export const MonacoLogViewer: React.FC<MonacoLogViewerProps> = ({
autoScrollRef.current = autoScroll; autoScrollRef.current = autoScroll;
}, [autoScroll]); }, [autoScroll]);
// 更新wordWrap设置
useEffect(() => {
if (editorRef.current) {
editorRef.current.updateOptions({
wordWrap: wordWrap ? 'on' : 'off',
});
}
}, [wordWrap]);
// Monaco Editor挂载完成 // Monaco Editor挂载完成
const handleEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => { const handleEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
editorRef.current = editor; editorRef.current = editor;
@ -125,7 +135,7 @@ export const MonacoLogViewer: React.FC<MonacoLogViewerProps> = ({
}; };
return ( return (
<div className={`w-full h-full ${className}`} style={{ height, ...style }}> <div className={`w-full h-full relative ${className}`} style={{ height, ...style }}>
<Editor <Editor
height="100%" height="100%"
defaultLanguage="plaintext" defaultLanguage="plaintext"
@ -137,7 +147,7 @@ export const MonacoLogViewer: React.FC<MonacoLogViewerProps> = ({
readOnly: true, readOnly: true,
minimap: { enabled: false }, minimap: { enabled: false },
scrollBeyondLastLine: false, scrollBeyondLastLine: false,
wordWrap: 'off', wordWrap: wordWrap ? 'on' : 'off',
lineNumbers: 'on', lineNumbers: 'on',
glyphMargin: false, glyphMargin: false,
folding: false, folding: false,

View File

@ -159,6 +159,9 @@ export interface MonacoLogViewerProps {
/** 高度 */ /** 高度 */
height?: string | number; height?: string | number;
/** 自动换行 */
wordWrap?: boolean;
/** 就绪回调 */ /** 就绪回调 */
onReady?: (api: LogViewerAPI) => void; onReady?: (api: LogViewerAPI) => void;

View File

@ -200,8 +200,8 @@ export function TerminalWindowManager<TResource = any>({
<div <div
key={win.id} key={win.id}
style={{ style={{
visibility: win.isMinimized ? 'hidden' : 'visible', // 使用 display:none 彻底隐藏最小化窗口避免Monaco滚动条残影
pointerEvents: win.isMinimized ? 'none' : 'auto', display: win.isMinimized ? 'none' : 'block',
}} }}
> >
<DraggableWindow <DraggableWindow

View File

@ -214,8 +214,8 @@ export const DraggableWindow: React.FC<DraggableWindowProps> = ({
</div> </div>
</div> </div>
{/* 窗口内容 */} {/* 窗口内容 - relative定位确保子元素的absolute定位正确 */}
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden relative">
{children} {children}
</div> </div>

View File

@ -28,6 +28,7 @@ import {
Loader2, Loader2,
ChevronUp, ChevronUp,
ChevronDown, ChevronDown,
WrapText,
} from 'lucide-react'; } from 'lucide-react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { import {
@ -76,6 +77,7 @@ const LogViewerContent: React.FC<{
const [loadingPods, setLoadingPods] = useState(app.runtimeType === 'K8S'); const [loadingPods, setLoadingPods] = useState(app.runtimeType === 'K8S');
const [isControlBarCollapsed, setIsControlBarCollapsed] = useState(false); const [isControlBarCollapsed, setIsControlBarCollapsed] = useState(false);
const [status, setStatus] = useState<LogStreamStatus>(LogStreamStatus.DISCONNECTED); const [status, setStatus] = useState<LogStreamStatus>(LogStreamStatus.DISCONNECTED);
const [wordWrap, setWordWrap] = useState(true);
const controlApiRef = useRef<LogStreamControlAPI | null>(null); const controlApiRef = useRef<LogStreamControlAPI | null>(null);
const logApiRef = useRef<LogViewerAPI | null>(null); const logApiRef = useRef<LogViewerAPI | null>(null);
@ -392,6 +394,9 @@ const LogViewerContent: React.FC<{
controlApiRef.current.send(JSON.stringify(stopMessage)); controlApiRef.current.send(JSON.stringify(stopMessage));
// 立即更新状态为DISCONNECTED确保UI正确显示开始按钮
setStatus(LogStreamStatus.DISCONNECTED);
setTimeout(() => { setTimeout(() => {
controlApiRef.current?.disconnect(); controlApiRef.current?.disconnect();
}, 100); }, 100);
@ -537,6 +542,20 @@ const LogViewerContent: React.FC<{
</TooltipTrigger> </TooltipTrigger>
<TooltipContent></TooltipContent> <TooltipContent></TooltipContent>
</Tooltip> </Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="sm"
variant={wordWrap ? "default" : "outline"}
onClick={() => setWordWrap(!wordWrap)}
className="h-7 w-7 p-0"
>
<WrapText className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>{wordWrap ? '关闭换行' : '自动换行'}</TooltipContent>
</Tooltip>
</div> </div>
</div> </div>
</TooltipProvider> </TooltipProvider>
@ -587,6 +606,7 @@ const LogViewerContent: React.FC<{
theme: 'vs-dark', theme: 'vs-dark',
fontSize: 13, fontSize: 13,
height: '100%', height: '100%',
wordWrap: wordWrap,
}} }}
onReady={(controlApi, logApi) => { onReady={(controlApi, logApi) => {
controlApiRef.current = controlApi; controlApiRef.current = controlApi;