import { Component, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewChecked, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatSelectModule } from '@angular/material/select'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatTooltipModule } from '@angular/material/tooltip'; import { FormsModule } from '@angular/forms'; import { Subscription } from 'rxjs'; import { LogService } from '../services/log.service'; import { SessionService } from '../services/session.service'; import { AuthService } from '../services/auth.service'; import { LogEntry } from '../models/log.model'; import { Session } from '../models/session.model'; @Component({ selector: 'app-log-display', standalone: true, imports: [ CommonModule, MatCardModule, MatButtonModule, MatIconModule, MatSelectModule, MatFormFieldModule, MatCheckboxModule, MatTooltipModule, FormsModule ], templateUrl: './log-display.component.html', styleUrl: './log-display.component.scss' }) export class LogDisplayComponent implements OnInit, OnDestroy, AfterViewChecked { @Input() instanceId?: string; // 实例ID参数,用于过滤该实例的会话日志 @ViewChild('logContent') private logContent!: ElementRef; allLogs: LogEntry[] = []; filteredLogs: LogEntry[] = []; availableSessions: Session[] = []; selectedLevel = 'all'; selectedSessionId = 'all'; autoScroll = true; isPaused = false; private subscriptions: Subscription = new Subscription(); private shouldScroll = false; constructor( private logService: LogService, private sessionService: SessionService, private authService: AuthService ) {} ngOnInit() { // 订阅日志流 this.subscriptions.add( this.logService.logStream$.subscribe(log => { if (!this.isPaused) { this.allLogs.push(log); this.applyFilters(); this.shouldScroll = this.autoScroll; } }) ); // 订阅会话列表(用于过滤)- 使用BehaviorSubject避免重复请求 this.subscriptions.add( this.sessionService.sessions$.subscribe(sessions => { this.availableSessions = sessions; // 如果有instanceId,过滤属于该实例的会话(移除实例过滤,直接使用所有会话) // 注意:instanceService已移除,不再支持实例过滤 }) ); // 初始化一些示例日志 this.addSampleLogs(); } ngAfterViewChecked() { if (this.shouldScroll) { this.scrollToBottom(); this.shouldScroll = false; } } ngOnDestroy() { this.subscriptions.unsubscribe(); } private addSampleLogs() { // 添加一些示例日志 const sampleLogs: LogEntry[] = [ { id: '1', timestamp: new Date(), level: 'info', message: '日志系统已初始化', source: 'log-service' }, { id: '2', timestamp: new Date(Date.now() - 60000), level: 'info', message: '连接到 opencode 服务', source: 'opencode-client' }, { id: '3', timestamp: new Date(Date.now() - 30000), level: 'debug', message: '用户登录成功', sessionID: 'session-123', source: 'auth-service' } ]; this.allLogs = [...sampleLogs, ...this.allLogs]; this.applyFilters(); } applyFilters() { this.filteredLogs = this.allLogs.filter(log => { // 级别过滤 if (this.selectedLevel !== 'all' && log.level !== this.selectedLevel) { return false; } // 会话过滤 if (this.selectedSessionId !== 'all') { if (!log.sessionID) return false; if (log.sessionID !== this.selectedSessionId) return false; } // 实例过滤(如果设置了instanceId) return true; }); // 按时间排序(最新的在前面) this.filteredLogs.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } getLevelLabel(level: string): string { const labels: { [key: string]: string } = { 'info': '信息', 'warn': '警告', 'error': '错误', 'debug': '调试' }; return labels[level] || level; } getSessionTitle(sessionId: string): string { const session = this.availableSessions.find(s => s.id === sessionId); return session ? session.title : sessionId; } toggleAutoScroll() { this.autoScroll = !this.autoScroll; if (this.autoScroll) { this.shouldScroll = true; } } togglePause() { this.isPaused = !this.isPaused; } clearLogs() { this.allLogs = []; this.filteredLogs = []; } private scrollToBottom() { try { this.logContent.nativeElement.scrollTop = this.logContent.nativeElement.scrollHeight; } catch (err) { console.error('滚动失败:', err); } } }