feat: Add LogStream component for real-time log display with Server-Sent Events, historical log loading, and interactive controls.
This commit is contained in:
@@ -170,26 +170,26 @@ export default function LogStream({ hideHeader = false, className = '' }: LogStr
|
||||
|
||||
{/* 紧凑型控制栏 (当 hideHeader=true 时显示) */}
|
||||
{hideHeader && (
|
||||
<div className="flex items-center justify-between gap-2 px-4 py-2 border-b border-white/5 bg-slate-900/40 backdrop-blur-md flex-shrink-0">
|
||||
<div className="flex items-center justify-between gap-2 px-4 py-2.5 border-b border-white/5 bg-slate-900/40 backdrop-blur-md flex-shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`w-1.5 h-1.5 rounded-full ${connected ? 'bg-green-500 shadow-[0_0_6px_rgba(34,197,94,0.4)]' : 'bg-red-500 shadow-[0_0_6px_rgba(239,68,68,0.4)]'}`}
|
||||
className={`w-2 h-2 rounded-full ${connected ? 'bg-green-500 shadow-[0_0_6px_rgba(34,197,94,0.4)]' : 'bg-red-500 shadow-[0_0_6px_rgba(239,68,68,0.4)]'}`}
|
||||
/>
|
||||
<span className="text-[10px] font-medium text-slate-500 uppercase tracking-wider">系统输出</span>
|
||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-wider">系统输出</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={togglePause}
|
||||
className="flex items-center gap-1.5 text-[10px] text-slate-400 hover:text-blue-400 transition-colors py-0.5 px-1.5 rounded hover:bg-white/5"
|
||||
className="flex items-center gap-1.5 text-xs text-slate-400 hover:text-blue-400 transition-colors py-1 px-2 rounded hover:bg-white/5"
|
||||
>
|
||||
{paused ? <Play className="h-3 w-3" /> : <Pause className="h-3 w-3" />}
|
||||
{paused ? <Play className="h-3.5 w-3.5" /> : <Pause className="h-3.5 w-3.5" />}
|
||||
{paused ? '开始' : '暂停'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
className="flex items-center gap-1.5 text-[10px] text-slate-400 hover:text-red-400 transition-colors py-0.5 px-1.5 rounded hover:bg-white/5"
|
||||
className="flex items-center gap-1.5 text-xs text-slate-400 hover:text-red-400 transition-colors py-1 px-2 rounded hover:bg-white/5"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
清空
|
||||
</button>
|
||||
</div>
|
||||
@@ -198,24 +198,24 @@ export default function LogStream({ hideHeader = false, className = '' }: LogStr
|
||||
|
||||
<div
|
||||
ref={logContainerRef}
|
||||
className="flex-1 overflow-y-auto bg-[#0a0c10] p-4 font-mono text-[11px] leading-relaxed selection:bg-blue-500/30 custom-scrollbar"
|
||||
className="flex-1 overflow-y-auto bg-[#0a0c10] p-4 font-mono text-sm leading-relaxed selection:bg-blue-500/30 custom-scrollbar"
|
||||
>
|
||||
{logs.length === 0 ? (
|
||||
<div className="h-full flex flex-col items-center justify-center text-slate-600 gap-2 opacity-50">
|
||||
<Terminal className="h-8 w-8 stroke-[1px]" />
|
||||
<p>等待系统日志输出...</p>
|
||||
<p className="text-base">等待系统日志输出...</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-0.5">
|
||||
<div className="space-y-1">
|
||||
{logs.map((log, i) => (
|
||||
<div key={i} className="group flex gap-3 py-0.5 border-l-2 border-transparent hover:border-blue-500/30 hover:bg-white/5 transition-all">
|
||||
<span className="text-slate-600 flex-shrink-0 select-none opacity-60 group-hover:opacity-100">
|
||||
<div key={i} className="group flex gap-3 py-1 border-l-2 border-transparent hover:border-blue-500/30 hover:bg-white/5 transition-all">
|
||||
<span className="text-slate-500 flex-shrink-0 select-none opacity-70 group-hover:opacity-100">
|
||||
{formatTime(log.timestamp)}
|
||||
</span>
|
||||
|
||||
{log.step && (
|
||||
<span
|
||||
className={`px-1.5 rounded-[4px] text-[9px] font-bold uppercase flex-shrink-0 h-4 flex items-center ${stepColors[log.step] || 'bg-slate-500/20 text-slate-400'}`}
|
||||
className={`px-1.5 rounded text-xs font-bold uppercase flex-shrink-0 h-5 flex items-center ${stepColors[log.step] || 'bg-slate-500/20 text-slate-400'}`}
|
||||
>
|
||||
{stepLabels[log.step] || log.step}
|
||||
</span>
|
||||
@@ -225,12 +225,12 @@ export default function LogStream({ hideHeader = false, className = '' }: LogStr
|
||||
{log.level === 'success' ? '✓' : log.level === 'error' ? '✗' : '•'}
|
||||
</span>
|
||||
|
||||
<span className="text-slate-300 break-all leading-normal group-hover:text-white transition-colors">
|
||||
<span className="text-slate-200 break-all leading-normal group-hover:text-white transition-colors">
|
||||
{log.message}
|
||||
</span>
|
||||
|
||||
{log.email && (
|
||||
<span className="text-slate-500 flex-shrink-0 ml-auto opacity-0 group-hover:opacity-100 transition-opacity bg-slate-800/50 px-1.5 rounded">
|
||||
<span className="text-slate-400 flex-shrink-0 ml-auto opacity-0 group-hover:opacity-100 transition-opacity bg-slate-800/50 px-2 rounded text-xs">
|
||||
{log.email}
|
||||
</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user