This commit is contained in:
dengqichen 2025-12-17 09:54:23 +08:00
parent 8f09e63ea1
commit 8117b61fb1
6 changed files with 232 additions and 366 deletions

View File

@ -101,7 +101,7 @@ export const ApplicationCard: React.FC<ApplicationCardProps> = ({
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="runtime" className="text-xs"> <TabsTrigger value="runtime" className="text-xs">
<Activity className="h-3 w-3 mr-1" /> <Activity className="h-3 w-3 mr-1" />
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>

View File

@ -11,14 +11,6 @@ export const DockerRuntimeStatus: React.FC<DockerRuntimeStatusProps> = ({
dockerServerName, dockerServerName,
dockerContainerName, dockerContainerName,
}) => { }) => {
// MOCK数据 - 后续替换为真实API
const mockStatus = {
status: 'running',
uptime: '2h 15m',
cpu: { used: 0.5, total: 2, percentage: 25 },
memory: { used: 512, total: 1024, percentage: 50 },
};
return ( return (
<div className="space-y-3"> <div className="space-y-3">
{/* 运行时类型 */} {/* 运行时类型 */}
@ -27,70 +19,16 @@ export const DockerRuntimeStatus: React.FC<DockerRuntimeStatusProps> = ({
Docker Docker
</Badge> </Badge>
{/* 运行状态 */} {/* 运行状态 - 研发中 */}
<div className="p-3 rounded-lg border bg-muted/30"> <div className="p-6 rounded-lg border bg-muted/30">
<div className="flex items-center gap-2 mb-3"> <div className="text-center space-y-3">
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" /> <div className="text-sm font-medium text-muted-foreground"></div>
<span className="text-sm font-medium"></span> <div className="space-y-2">
</div> <div className="h-3 bg-muted rounded animate-pulse" />
<div className="h-3 bg-muted rounded animate-pulse w-3/4 mx-auto" />
<div className="space-y-2 text-xs"> <div className="h-3 bg-muted rounded animate-pulse w-1/2 mx-auto" />
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">Running</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{mockStatus.uptime}</span>
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-muted-foreground">CPU</span>
<span className="font-medium">{mockStatus.cpu.percentage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div
className="bg-blue-500 h-1.5 rounded-full transition-all"
style={{ width: `${mockStatus.cpu.percentage}%` }}
/>
</div>
<div className="text-[10px] text-muted-foreground text-right">
{mockStatus.cpu.used}/{mockStatus.cpu.total} cores
</div>
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{mockStatus.memory.percentage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div
className="bg-green-500 h-1.5 rounded-full transition-all"
style={{ width: `${mockStatus.memory.percentage}%` }}
/>
</div>
<div className="text-[10px] text-muted-foreground text-right">
{mockStatus.memory.used}MB/{mockStatus.memory.total}MB
</div>
</div>
</div>
</div>
{/* 配置详情 */}
<div className="p-3 rounded-lg border bg-muted/30">
<div className="text-xs font-medium text-muted-foreground mb-2"></div>
<div className="space-y-2 text-xs">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{dockerServerName || '-'}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{dockerContainerName || '-'}</span>
</div> </div>
<div className="text-xs text-muted-foreground pt-2">...</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,14 +13,6 @@ export const K8sRuntimeStatus: React.FC<K8sRuntimeStatusProps> = ({
k8sNamespaceName, k8sNamespaceName,
k8sDeploymentName, k8sDeploymentName,
}) => { }) => {
// MOCK数据 - 后续替换为真实API
const mockStatus = {
status: 'running',
podCount: { ready: 3, total: 3 },
cpu: { used: 0.9, total: 2, percentage: 45 },
memory: { used: 1.2, total: 2, percentage: 60 },
};
return ( return (
<div className="space-y-3"> <div className="space-y-3">
{/* 运行时类型 */} {/* 运行时类型 */}
@ -29,71 +21,16 @@ export const K8sRuntimeStatus: React.FC<K8sRuntimeStatusProps> = ({
Kubernetes Kubernetes
</Badge> </Badge>
{/* 运行状态 */} {/* 运行状态 - 研发中 */}
<div className="p-3 rounded-lg border bg-muted/30"> <div className="p-6 rounded-lg border bg-muted/30">
<div className="flex items-center gap-2 mb-3"> <div className="text-center space-y-3">
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" /> <div className="text-sm font-medium text-muted-foreground"></div>
<span className="text-sm font-medium"></span> <div className="space-y-2">
</div> <div className="h-3 bg-muted rounded animate-pulse" />
<div className="h-3 bg-muted rounded animate-pulse w-3/4 mx-auto" />
<div className="space-y-2 text-xs"> <div className="h-3 bg-muted rounded animate-pulse w-1/2 mx-auto" />
<div className="flex items-center justify-between">
<span className="text-muted-foreground">Pod</span>
<span className="font-medium">
{mockStatus.podCount.ready}/{mockStatus.podCount.total} Running
</span>
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-muted-foreground">CPU</span>
<span className="font-medium">{mockStatus.cpu.percentage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div
className="bg-blue-500 h-1.5 rounded-full transition-all"
style={{ width: `${mockStatus.cpu.percentage}%` }}
/>
</div>
<div className="text-[10px] text-muted-foreground text-right">
{mockStatus.cpu.used}/{mockStatus.cpu.total} cores
</div>
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{mockStatus.memory.percentage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div
className="bg-green-500 h-1.5 rounded-full transition-all"
style={{ width: `${mockStatus.memory.percentage}%` }}
/>
</div>
<div className="text-[10px] text-muted-foreground text-right">
{mockStatus.memory.used}GB/{mockStatus.memory.total}GB
</div>
</div>
</div>
</div>
{/* 配置详情 */}
<div className="p-3 rounded-lg border bg-muted/30">
<div className="text-xs font-medium text-muted-foreground mb-2"></div>
<div className="space-y-2 text-xs">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{k8sSystemName || '-'}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{k8sNamespaceName || '-'}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{k8sDeploymentName || '-'}</span>
</div> </div>
<div className="text-xs text-muted-foreground pt-2">...</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -98,7 +98,7 @@ export const LogStreamViewer: React.FC<LogStreamViewerProps> = ({
return ( return (
<div <div
ref={scrollRef} ref={scrollRef}
className="h-full overflow-x-auto overflow-y-auto font-mono text-xs bg-gray-950 text-gray-100 p-4 rounded-md" className="h-full w-full overflow-x-auto overflow-y-auto font-mono text-xs bg-gray-950 text-gray-100 p-3 [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar]:h-2 [&::-webkit-scrollbar-track]:bg-gray-900 [&::-webkit-scrollbar-thumb]:bg-gray-700 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:hover:bg-gray-600"
> >
{logs.map((log) => ( {logs.map((log) => (
<div key={log.id} className="mb-1 whitespace-pre-wrap break-words"> <div key={log.id} className="mb-1 whitespace-pre-wrap break-words">

View File

@ -24,6 +24,8 @@ import {
Loader2, Loader2,
ScrollText, ScrollText,
Circle, Circle,
ChevronUp,
ChevronDown,
} from "lucide-react"; } from "lucide-react";
import { LogStreamViewer } from './LogStreamViewer'; import { LogStreamViewer } from './LogStreamViewer';
import { useLogStream } from '../hooks/useLogStream'; import { useLogStream } from '../hooks/useLogStream';
@ -53,6 +55,7 @@ export const LogViewerWindow: React.FC<LogViewerWindowProps> = ({
const [podName, setPodName] = useState(''); const [podName, setPodName] = useState('');
const [podNames, setPodNames] = useState<string[]>([]); const [podNames, setPodNames] = useState<string[]>([]);
const [loadingPods, setLoadingPods] = useState(false); const [loadingPods, setLoadingPods] = useState(false);
const [isControlBarCollapsed, setIsControlBarCollapsed] = useState(false);
const closeAllRef = useRef<(() => void) | null>(null); const closeAllRef = useRef<(() => void) | null>(null);
const onCloseReadyRef = useRef(onCloseReady); const onCloseReadyRef = useRef(onCloseReady);
@ -190,34 +193,32 @@ export const LogViewerWindow: React.FC<LogViewerWindowProps> = ({
} }
}; };
// 构建动态标题
const buildTitle = useCallback(() => {
const runtimeConfig = getRuntimeIcon(); const runtimeConfig = getRuntimeIcon();
const RuntimeIcon = runtimeConfig.icon; const RuntimeIcon = runtimeConfig.icon;
const getStatusIndicator = () => { let statusColor = 'text-gray-500';
switch (status) { switch (status) {
case LogStreamStatus.CONNECTING: case LogStreamStatus.CONNECTING:
return { color: 'text-yellow-500', label: '连接中' }; statusColor = 'text-yellow-500';
break;
case LogStreamStatus.CONNECTED: case LogStreamStatus.CONNECTED:
return { color: 'text-blue-500', label: '已连接' }; statusColor = 'text-blue-500';
break;
case LogStreamStatus.STREAMING: case LogStreamStatus.STREAMING:
return { color: 'text-green-500', label: '流式传输中' }; statusColor = 'text-green-500';
break;
case LogStreamStatus.PAUSED: case LogStreamStatus.PAUSED:
return { color: 'text-orange-500', label: '已暂停' }; statusColor = 'text-orange-500';
break;
case LogStreamStatus.STOPPED: case LogStreamStatus.STOPPED:
return { color: 'text-gray-500', label: '已停止' }; statusColor = 'text-gray-500';
break;
case LogStreamStatus.ERROR: case LogStreamStatus.ERROR:
return { color: 'text-red-500', label: '错误' }; statusColor = 'text-red-500';
default: break;
return { color: 'text-gray-500', label: '未连接' };
} }
};
const statusIndicator = getStatusIndicator();
// 构建动态标题
const buildTitle = useCallback(() => {
const RuntimeIcon = runtimeConfig.icon;
const statusColor = statusIndicator.color;
return ( return (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@ -238,14 +239,15 @@ export const LogViewerWindow: React.FC<LogViewerWindowProps> = ({
<span className="text-sm font-normal"></span> <span className="text-sm font-normal"></span>
</div> </div>
); );
}, [app.applicationName, runtimeConfig, statusIndicator.color]); }, [app.applicationName, app.runtimeType, status]);
// 状态变化时更新标题 // 状态变化时更新标题
useEffect(() => { useEffect(() => {
console.log('[LogViewerWindow] 更新标题, status:', status, 'updateWindowTitleRef:', !!updateWindowTitleRef.current);
if (updateWindowTitleRef.current) { if (updateWindowTitleRef.current) {
updateWindowTitleRef.current(buildTitle()); updateWindowTitleRef.current(buildTitle());
} }
}, [buildTitle]); }, [buildTitle, status]);
const handleRestart = () => { const handleRestart = () => {
clearLogs(); clearLogs();
@ -265,9 +267,35 @@ export const LogViewerWindow: React.FC<LogViewerWindowProps> = ({
return ( return (
<div className="flex flex-col h-full w-full"> <div className="flex flex-col h-full w-full">
{/* 紧凑控制栏 */} {/* 紧凑控制栏 - 收缩时完全隐藏 */}
<div className="px-3 py-1.5 border-b flex-shrink-0 bg-muted/20"> <div className={isControlBarCollapsed ? 'hidden' : 'flex-shrink-0 border-b bg-muted/30'}>
<div className="flex items-center justify-end gap-2"> <div className="px-3 py-1.5">
<div className="flex items-center justify-between gap-2">
<TooltipProvider>
{/* 折叠/展开按钮 */}
<Tooltip>
<TooltipTrigger asChild>
<Button
size="sm"
variant="outline"
onClick={() => setIsControlBarCollapsed(!isControlBarCollapsed)}
className={`p-0 transition-all ${isControlBarCollapsed ? 'h-5 w-5' : 'h-7 w-7'}`}
>
{isControlBarCollapsed ? (
<ChevronDown className="h-3 w-3" />
) : (
<ChevronUp className="h-4 w-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>
{isControlBarCollapsed ? '展开控制栏' : '折叠控制栏'}
</TooltipContent>
</Tooltip>
{/* 控制按钮区域 - 折叠时隐藏 */}
{!isControlBarCollapsed && (
<div className="flex items-center gap-2">
<Input <Input
type="number" type="number"
value={lines} value={lines}
@ -309,7 +337,6 @@ export const LogViewerWindow: React.FC<LogViewerWindowProps> = ({
</> </>
)} )}
<TooltipProvider>
<div className="flex gap-1"> <div className="flex gap-1">
{/* 启动/恢复/重试按钮 */} {/* 启动/恢复/重试按钮 */}
{(status === LogStreamStatus.DISCONNECTED || {(status === LogStreamStatus.DISCONNECTED ||
@ -398,12 +425,34 @@ export const LogViewerWindow: React.FC<LogViewerWindowProps> = ({
<TooltipContent></TooltipContent> <TooltipContent></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</div>
)}
</TooltipProvider> </TooltipProvider>
</div> </div>
</div> </div>
</div>
{/* 日志显示区域 - 无padding直接填充 */}
<div className="flex-1 w-full overflow-hidden relative">
{/* 悬浮展开按钮 - 收缩时显示 */}
{isControlBarCollapsed && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="sm"
variant="outline"
onClick={() => setIsControlBarCollapsed(false)}
className="absolute top-2 right-2 z-10 h-6 w-6 p-0 bg-background/80 backdrop-blur-sm hover:bg-background"
>
<ChevronDown className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent></TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{/* 日志显示区域 */}
<div className="flex-1 overflow-hidden">
<LogStreamViewer <LogStreamViewer
logs={logs} logs={logs}
status={status} status={status}

View File

@ -11,15 +11,6 @@ export const ServerRuntimeStatus: React.FC<ServerRuntimeStatusProps> = ({
serverName, serverName,
logQueryCommand, logQueryCommand,
}) => { }) => {
// MOCK数据 - 后续替换为真实API
const mockStatus = {
status: 'running',
pid: 12345,
uptime: '2h 15m',
cpu: { percentage: 15 },
memory: { used: 256 },
};
return ( return (
<div className="space-y-3"> <div className="space-y-3">
{/* 运行时类型 */} {/* 运行时类型 */}
@ -28,65 +19,16 @@ export const ServerRuntimeStatus: React.FC<ServerRuntimeStatusProps> = ({
</Badge> </Badge>
{/* 运行状态 */} {/* 运行状态 - 研发中 */}
<div className="p-3 rounded-lg border bg-muted/30"> <div className="p-6 rounded-lg border bg-muted/30">
<div className="flex items-center gap-2 mb-3"> <div className="text-center space-y-3">
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" /> <div className="text-sm font-medium text-muted-foreground"></div>
<span className="text-sm font-medium"></span> <div className="space-y-2">
</div> <div className="h-3 bg-muted rounded animate-pulse" />
<div className="h-3 bg-muted rounded animate-pulse w-3/4 mx-auto" />
<div className="space-y-2 text-xs"> <div className="h-3 bg-muted rounded animate-pulse w-1/2 mx-auto" />
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">Running</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">PID</span>
<span className="font-medium font-mono">{mockStatus.pid}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{mockStatus.uptime}</span>
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-muted-foreground">CPU</span>
<span className="font-medium">{mockStatus.cpu.percentage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div
className="bg-blue-500 h-1.5 rounded-full transition-all"
style={{ width: `${mockStatus.cpu.percentage}%` }}
/>
</div>
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{mockStatus.memory.used}MB</span>
</div>
</div>
</div>
</div>
{/* 配置详情 */}
<div className="p-3 rounded-lg border bg-muted/30">
<div className="text-xs font-medium text-muted-foreground mb-2"></div>
<div className="space-y-2 text-xs">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{serverName || '-'}</span>
</div>
<div className="flex flex-col gap-1">
<span className="text-muted-foreground"></span>
<code className="font-mono text-[10px] bg-muted px-2 py-1 rounded break-all">
{logQueryCommand || '-'}
</code>
</div> </div>
<div className="text-xs text-muted-foreground pt-2">...</div>
</div> </div>
</div> </div>
</div> </div>