import { Component, OnInit, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { ProjectService } from '../../../services/project.service'; import { Task } from '../../../models/project.model'; import { SkillRadarComponent } from './skill-radar/skill-radar.component'; import { PersonalBoard } from '../personal-board/personal-board'; import { FmodeQuery, FmodeObject, FmodeUser } from 'fmode-ng/core'; import { WxworkAuth } from 'fmode-ng/core'; interface ShiftTask { id: string; projectId: string; projectName: string; taskDescription: string; priority: '高' | '中' | '低'; shiftDate: string; status: '待处理' | '处理中' | '已完成'; } interface ProjectTimelineItem { id: string; name: string; deadline: string; status: string; } @Component({ selector: 'app-dashboard', standalone: true, imports: [CommonModule, RouterModule, SkillRadarComponent, PersonalBoard], templateUrl: './dashboard.html', styleUrl: './dashboard.scss' }) export class Dashboard implements OnInit { // 视图管理 activeDashboard: 'main' | 'skills' | 'personal' = 'main'; // 新增:工作台视图模式(卡片/列表) viewMode: 'card' | 'list' = 'list'; tasks: Task[] = []; overdueTasks: Task[] = []; urgentTasks: Task[] = []; pendingFeedbacks: {task: Task, feedback: any}[] = []; reminderMessage: string = ''; feedbackProjectId: string = ''; countdowns: Map = new Map(); // 代班信息相关属性 shiftTasks: ShiftTask[] = []; // 个人项目饱和度相关属性 workloadPercentage: number = 0; projectTimeline: ProjectTimelineItem[] = []; private wxAuth: WxworkAuth | null = null; private currentUser: FmodeUser | null = null; constructor(private projectService: ProjectService) { this.initAuth(); } // 初始化企业微信认证 private initAuth(): void { try { this.wxAuth = new WxworkAuth({ cid: 'cDL6R1hgSi' // 公司帐套ID }); console.log('✅ 设计师仪表板企业微信认证初始化成功'); } catch (error) { console.error('❌ 设计师仪表板企业微信认证初始化失败:', error); } } async ngOnInit(): Promise { await this.authenticateAndLoadData(); } // 认证并加载数据 private async authenticateAndLoadData(): Promise { try { // 执行企业微信认证和登录 const { user } = await this.wxAuth!.authenticateAndLogin(); this.currentUser = user; if (user) { console.log('✅ 设计师登录成功:', user.get('username')); await this.loadDashboardData(); } else { console.error('❌ 设计师登录失败'); } } catch (error) { console.error('❌ 设计师认证过程出错:', error); // 降级到模拟数据 this.loadMockData(); } } // 加载仪表板数据 private async loadDashboardData(): Promise { try { await Promise.all([ this.loadTasks(), this.loadShiftTasks(), this.calculateWorkloadPercentage(), this.loadProjectTimeline() ]); console.log('✅ 设计师仪表板数据加载完成'); } catch (error) { console.error('❌ 设计师仪表板数据加载失败:', error); throw error; } } // 降级到模拟数据 private loadMockData(): void { console.warn('⚠️ 使用模拟数据'); this.loadTasks(); this.loadShiftTasks(); this.calculateWorkloadPercentage(); this.loadProjectTimeline(); } // 切换视图方法 switchDashboard(view: 'main' | 'skills' | 'personal'): void { this.activeDashboard = view; } // 新增:切换卡片/列表视图 toggleView(): void { this.viewMode = this.viewMode === 'card' ? 'list' : 'card'; } // 获取前N个任务的方法 getTopTasks(count: number): Task[] { // 过滤掉紧急任务和超期任务 const regularTasks = this.tasks.filter(task => !this.urgentTasks.some(urgent => urgent.id === task.id) && !this.overdueTasks.some(overdue => overdue.id === task.id) ); // 返回指定数量的任务 return regularTasks.slice(0, count); } // 获取工作量颜色的方法 getWorkloadColor(): string { if (this.workloadPercentage >= 80) { return '#ff4560'; // 红色 } else if (this.workloadPercentage >= 50) { return '#ffa726'; // 橙色 } else { return '#66bb6a'; // 绿色 } } // 获取工作量状态的方法 getWorkloadStatus(): string { if (this.workloadPercentage >= 80) { return '工作量饱和'; } else if (this.workloadPercentage >= 50) { return '工作量适中'; } else if (this.workloadPercentage > 0) { return '工作量轻松'; } else { return '暂无工作任务'; } } loadTasks(): void { this.projectService.getTasks().subscribe(tasks => { // 按阶段优先级排序:建模 > 渲染 > 对图 > 反馈处理 > 后期 > 其他 this.tasks = tasks.sort((a, b) => { const stagePriority: Record = { '建模': 5, '渲染': 4, '对图': 3, '反馈处理': 2, '后期': 1, '投诉处理': 0 }; const priorityA = stagePriority[a.stage] || 0; const priorityB = stagePriority[b.stage] || 0; if (priorityA !== priorityB) { return priorityB - priorityA; } // 优先级相同时,按截止日期排序 return a.deadline.getTime() - b.deadline.getTime(); }); // 筛选超期任务 this.overdueTasks = this.tasks.filter(task => task.isOverdue); // 筛选紧急任务(渲染超时预警,交付前3小时/1小时) this.urgentTasks = this.tasks.filter(task => { const now = new Date(); const diffHours = (task.deadline.getTime() - now.getTime()) / (1000 * 60 * 60); return diffHours <= 3 && diffHours > 0 && task.stage === '渲染'; }); // 设置反馈项目ID if (this.overdueTasks.length > 0) { this.feedbackProjectId = this.overdueTasks[0].projectId; } // 加载待处理反馈 this.loadPendingFeedbacks(); // 启动倒计时 this.startCountdowns(); }); } loadPendingFeedbacks(): void { this.pendingFeedbacks = []; // 模拟加载待处理反馈数据 this.tasks.forEach(task => { // 使用模拟数据代替API调用 const mockFeedbacks = [ { id: 'fb-' + task.id, projectId: task.projectId, content: '客户对色彩不满意,需要调整', isSatisfied: false, problemLocation: '色彩', expectedEffect: '更明亮的色调', referenceCase: '无', status: '待处理' as const, createdAt: new Date(Date.now() - 30 * 60 * 1000) // 30分钟前 }, { id: 'fb-' + task.id + '-2', projectId: task.projectId, content: '家具款式需要调整', isSatisfied: false, problemLocation: '家具', expectedEffect: '更现代的款式', referenceCase: '无', status: '待处理' as const, createdAt: new Date(Date.now() - 45 * 60 * 1000) // 45分钟前 } ]; const pending = mockFeedbacks.filter(feedback => feedback.status === '待处理' && !feedback.isSatisfied ); if (pending.length > 0) { this.pendingFeedbacks.push({task, feedback: pending[0]}); } }); } startCountdowns(): void { // 清除之前的定时器 this.countdowns.clear(); // 为所有任务启动倒计时,确保列表视图也有剩余时间显示 this.tasks.forEach(task => { this.updateCountdown(task.id, task.deadline); }); // 定期更新倒计时 setInterval(() => { this.tasks.forEach(task => { this.updateCountdown(task.id, task.deadline); }); }, 60000); // 每分钟更新一次 } updateCountdown(taskId: string, deadline: Date): void { const now = new Date(); const diffMs = deadline.getTime() - now.getTime(); if (diffMs <= 0) { this.countdowns.set(taskId, '已超期'); return; } const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (diffHours > 0) { this.countdowns.set(taskId, `${diffHours}小时${diffMinutes}分钟`); } else { this.countdowns.set(taskId, `${diffMinutes}分钟`); } } getTaskCountdown(taskId: string): string { return this.countdowns.get(taskId) || ''; } // 新增:列表视图专用剩余时间格式化(若未在countdowns中,直接计算) getListTimeLeft(task: Task): string { const cached = this.getTaskCountdown(task.id); if (cached) return cached; const now = new Date(); const diffMs = task.deadline.getTime() - now.getTime(); if (diffMs <= 0) return '已超期'; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); const diffHours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); if (diffDays > 0) return `${diffDays}天${diffHours}小时`; const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (diffHours > 0) return `${diffHours}小时${diffMinutes}分钟`; return `${diffMinutes}分钟`; } // 新增:按紧急度排序 getTasksSortedByUrgency(): Task[] { return this.tasks .filter(t => !t.isCompleted) .slice() .sort((a, b) => this.getUrgencyScore(b) - this.getUrgencyScore(a)); } // 新增:紧急度评分,数值越大越紧急 private getUrgencyScore(task: Task): number { if (task.isOverdue) return 10000; const now = new Date().getTime(); const hoursLeft = (task.deadline.getTime() - now) / (1000 * 60 * 60); let base = 0; if (hoursLeft <= 3) base = 9000; else if (hoursLeft <= 24) base = 7000; else if (hoursLeft <= 72) base = 4000; else base = 1000; // 渲染阶段适度提高权重 const stageBoost = task.stage === '渲染' ? 300 : 0; return base + stageBoost; } // 新增:紧急度标签 getUrgencyLevel(task: Task): '超期' | '高' | '中' | '低' { if (task.isOverdue) return '超期'; const now = new Date().getTime(); const hoursLeft = (task.deadline.getTime() - now) / (1000 * 60 * 60); if (hoursLeft <= 24) return '高'; if (hoursLeft <= 72) return '中'; return '低'; } // 新增:紧急度样式类 getUrgencyClass(task: Task): string { const level = this.getUrgencyLevel(task); switch (level) { case '超期': return 'urgency-overdue'; case '高': return 'urgency-high'; case '中': return 'urgency-medium'; default: return 'urgency-low'; } } getTaskStageProgress(taskId: string): number { const task = this.tasks.find(t => t.id === taskId); if (!task) return 0; // 为不同阶段设置固定的模拟进度值 const stageProgressMap: Record = { '建模': 22, '渲染': 23, '对图': 50, '反馈处理': 80, '后期': 75, '投诉处理': 100 }; // 对于渲染任务,如果有实际的渲染进度数据,使用它 if (task.stage === '渲染') { // 在实际应用中,这里会从服务中获取真实的进度 // this.projectService.getRenderProgress(task.projectId).subscribe(progress => { // if (progress) { // return progress.completionRate; // } // }); } return stageProgressMap[task.stage] || 0; } markTaskAsCompleted(taskId: string): void { this.projectService.markTaskAsCompleted(taskId).subscribe(() => { this.loadTasks(); // 重新加载任务列表 }); } handleFeedback(taskId: string): void { const task = this.tasks.find(t => t.id === taskId); if (task) { // 跳转到项目详情的反馈处理页面 window.location.href = `/designer/project-detail/${task.projectId}#feedback`; } } generateReminderMessage(): void { this.projectService.generateReminderMessage('overdue').subscribe(message => { this.reminderMessage = message; }); } clearReminder(): void { this.reminderMessage = ''; } // 代班任务相关方法 loadShiftTasks(): void { // 在实际应用中,这里应该从服务中获取代班任务 // 这里使用模拟数据 this.shiftTasks = [ { id: 'shift1', projectId: 'project1', projectName: '现代风格客厅设计', taskDescription: '小图修改反馈和渲染进度跟踪', priority: '高', shiftDate: '2025-09-15', status: '待处理' }, { id: 'shift2', projectId: 'project2', projectName: '北欧风卧室装修', taskDescription: '查看客户反馈并提供初步修改建议', priority: '中', shiftDate: '2025-09-16', status: '待处理' }, { id: 'shift3', projectId: 'project3', projectName: '新中式书房改造', taskDescription: '完成剩余渲染任务', priority: '低', shiftDate: '2025-09-17', status: '处理中' } ]; } // 打开添加代班任务的模态框 openShiftModal(): void { // 在实际应用中,这里应该打开一个模态框让用户添加代班任务 // 这里使用alert模拟 window.fmode?.alert?.({ header: '添加代班任务', message: '将打开添加代班任务的表单', buttons: [{ text: '确定', role: 'confirm' }] }); // 实际实现可能是:this.modalService.openShiftModal(); } // 查看代班任务详情 viewShiftDetail(shiftId: string): void { const shift = this.shiftTasks.find(s => s.id === shiftId); if (shift) { // 实际应用中,这里应该打开详情页面或模态框 console.log('查看代班任务详情:', shift); window?.fmode?.alert(`代班任务详情:\n项目:${shift.projectName}\n任务:${shift.taskDescription}\n优先级:${shift.priority}\n代班日期:${shift.shiftDate}`); } } // 标记代班任务完成 markShiftComplete(shiftId: string): void { const shiftIndex = this.shiftTasks.findIndex(s => s.id === shiftId); if (shiftIndex !== -1) { // 在实际应用中,这里应该调用API更新状态 this.shiftTasks[shiftIndex].status = '已完成'; window?.fmode?.alert('代班任务已标记为完成'); } } // 计算项目饱和度 calculateWorkloadPercentage(): void { // 在实际应用中,这里应该从服务中获取真实的项目饱和度数据 // 这里使用模拟数据,根据当前任务数量计算饱和度 const totalCapacity = 5; // 假设设计师最大同时处理5个项目 const currentProjects = this.tasks.length; // 计算饱和度百分比 this.workloadPercentage = Math.round((currentProjects / totalCapacity) * 100); // 确保百分比在0-100之间 this.workloadPercentage = Math.min(Math.max(this.workloadPercentage, 0), 100); } // 加载项目排期表 loadProjectTimeline(): void { // 在实际应用中,这里应该从服务中获取项目排期数据 // 这里使用模拟数据 this.projectTimeline = [ { id: 'timeline1', name: '现代风格客厅设计', deadline: '2025-09-20', status: '进行中' }, { id: 'timeline2', name: '北欧风卧室装修', deadline: '2025-09-25', status: '进行中' }, { id: 'timeline3', name: '新中式书房改造', deadline: '2025-09-30', status: '进行中' } ].sort((a, b) => new Date(a.deadline).getTime() - new Date(b.deadline).getTime()); } }