import { Component, OnInit, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Router } from '@angular/router'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatChipsModule } from '@angular/material/chips'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Subscription, forkJoin } from 'rxjs'; import { SessionService } from '../services/session.service'; import { WindowService } from '../services/window.service'; import { AgentService } from '../services/agent.service'; import { EventService } from '../services/event.service'; import { MenuService } from '../services/menu.service'; import { Session } from '../models/session.model'; @Component({ selector: 'app-home', standalone: true, imports: [ CommonModule, MatCardModule, MatButtonModule, MatIconModule, MatChipsModule, MatProgressSpinnerModule, MatTooltipModule ], templateUrl: './home.component.html', styleUrl: './home.component.scss', }) export class HomeComponent implements OnInit, OnDestroy { projects: Session[] = []; isLoading = false; showNewProjectModal = false; // 跟踪正在加载的项目(project_id集合) loadingProjects = new Set(); // 存储项目最新消息的映射(project_id -> 最新消息内容) private projectMessages = new Map(); private subscriptions: Subscription = new Subscription(); constructor( private sessionService: SessionService, private windowService: WindowService, private agentService: AgentService, private eventService: EventService, private menuService: MenuService, private router: Router ) {} ngOnInit() { this.loadProjects(); // 订阅会话更新事件,更新项目列表 this.subscriptions.add( this.sessionService.sessions$.subscribe(sessions => { this.updateProjects(sessions); }) ); // 订阅事件流,接收实时更新 this.subscriptions.add( this.eventService.allEvents$.subscribe(event => { const payload = event.payload; // 处理会话更新事件,更新对应项目卡片 if (payload.type === 'session.updated') { this.refreshProjects(); } // 处理消息更新事件,更新项目卡片中的最新消息预览 if (payload.type === 'message.updated') { this.handleMessageUpdated(event); } // 处理消息部分更新事件(流式输出) if (payload.type === 'message.part.updated') { this.handleMessagePartUpdated(event); } }) ); } loadProjects() { this.isLoading = true; this.sessionService.loadSessions(); } updateProjects(sessions: Session[]) { // 按project_id去重,获取唯一项目 const projectMap = new Map(); sessions.forEach(session => { const projectId = session.project_id || session.id; // 保留最新或第一个会话(简单起见,保留第一个遇到的) if (!projectMap.has(projectId)) { projectMap.set(projectId, session); } }); const newProjects = Array.from(projectMap.values()); this.projects = newProjects; this.isLoading = false; // 清理不再存在的项目的消息缓存 this.cleanupProjectMessages(newProjects); } // 清理无效项目的消息缓存 private cleanupProjectMessages(currentProjects: Session[]) { const currentProjectIds = new Set(currentProjects.map(p => p.project_id || p.id)); // 删除不再存在的项目的消息 for (const projectId of this.projectMessages.keys()) { if (!currentProjectIds.has(projectId)) { this.projectMessages.delete(projectId); } } } refreshProjects() { this.sessionService.loadSessions(); } getAgentDisplayName(agentId: string): string { return this.agentService.getAgentDisplayName(agentId); } getStatusDisplayName(status: string): string { const statusMap: Record = { 'requirement_document': '需求文档', 'technical_document': '技术文档', 'code': '代码开发', 'test': '测试', 'release': '发布' }; return statusMap[status] || status; } getStatusClass(status: string): string { return `status-${status}`; } formatDate(dateString?: string): string { if (!dateString) return '未知'; const date = new Date(dateString); return date.toLocaleDateString('zh-CN'); } runProject(project: Session) { console.log('运行项目:', project.title); // TODO: 发送需求文档开始工作 // 1. 打开项目页签 this.editProject(project); // 2. 发送需求文档到AI(如果存在需求文档) alert('运行功能待实现'); } // 检查项目是否正在加载 isProjectLoading(projectId: string): boolean { return this.loadingProjects.has(projectId); } editProject(project: Session) { const projectId = project.project_id || project.id; console.log('🔍 [HomeComponent] 编辑项目:', project.title, '项目ID:', projectId); // 设置加载状态 this.loadingProjects.add(projectId); // 使用 WindowService 打开项目标签页 const success = this.windowService.openProjectTab(projectId); if (!success) { // WindowService 已处理错误提示,这里只需清理加载状态 console.error('🔍 [HomeComponent] 打开项目标签页失败'); } this.loadingProjects.delete(projectId); } deleteProject(project: Session) { if (confirm(`确定删除项目 "${project.title}" 吗?`)) { console.log('删除项目:', project.title); // TODO: 调用删除API alert('删除功能待实现'); } } onProjectCreated(session: Session) { console.log('新项目创建:', session.title); // 刷新项目列表 this.refreshProjects(); } // 处理消息更新事件 private handleMessageUpdated(event: any) { const payload = event.payload; const messageInfo = payload.properties?.info; if (!messageInfo) return; // 提取会话ID(可能来自不同字段) const sessionId = messageInfo.sessionID || messageInfo.sessionId || messageInfo.session_id; if (!sessionId) return; // 查找对应的项目ID(会话ID可能等于项目ID,或需要映射) // 这里简单假设会话ID就是项目ID,或者通过projects数组查找 const project = this.projects.find(p => p.id === sessionId || p.project_id === sessionId); if (!project) return; const projectId = project.project_id || project.id; const content = messageInfo.content || ''; // 只存储AI消息(assistant角色) if (messageInfo.role === 'assistant' && content.trim()) { // 截断过长的消息 const truncatedContent = content.length > 100 ? content.substring(0, 100) + '...' : content; this.projectMessages.set(projectId, truncatedContent); console.log(`🔍 [HomeComponent] 更新项目 ${projectId} 的最新消息:`, truncatedContent); } } // 处理消息部分更新事件(流式输出) private handleMessagePartUpdated(event: any) { const payload = event.payload; const part = payload.properties?.part; const delta = payload.properties?.delta; if (!part || !delta) return; // 提取会话ID const sessionId = part.sessionID || part.sessionId || part.session_id; if (!sessionId) return; // 查找对应的项目 const project = this.projects.find(p => p.id === sessionId || p.project_id === sessionId); if (!project) return; const projectId = project.project_id || project.id; // 只处理文本类型的部分更新 if (part.type === 'text' || part.type === 'reasoning') { // 获取当前消息或初始化 const currentMessage = this.projectMessages.get(projectId) || ''; const newMessage = currentMessage + delta; // 截断过长的消息 const truncatedMessage = newMessage.length > 100 ? newMessage.substring(0, 100) + '...' : newMessage; this.projectMessages.set(projectId, truncatedMessage); console.log(`🔍 [HomeComponent] 流式更新项目 ${projectId} 的消息:`, truncatedMessage.substring(0, 50)); } } // 获取项目的最新消息预览 getProjectLatestMessage(projectId: string): string { const message = this.projectMessages.get(projectId); if (message) { return message; } // 默认消息 return '项目已创建,等待需求文档'; } ngOnDestroy() { this.subscriptions.unsubscribe(); } }