import { Component, OnInit, signal, computed, ViewChild, AfterViewChecked } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule, ActivatedRoute } from '@angular/router'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { ProjectService } from '../../../services/project.service'; import { Project, Task, Message, FileItem, CustomerFeedback, Milestone } from '../../../models/project.model'; import { ModificationRequestDialog } from './modification-request-dialog'; import { ComplaintWarningDialog } from './complaint-warning-dialog'; import { RefundRequestDialog } from './refund-request-dialog'; // 定义项目阶段接口 interface ProjectStage { name: string; completed: boolean; inProgress: boolean; startDate?: Date; endDate?: Date; responsible?: string; details?: string; } // 定义企业微信消息接口 interface WechatMessage { sender: string; content: string; timestamp: Date; } // 定义历史咨询记录接口 interface ConsultationRecord { id: string; date: Date; content: string; status: string; } // 定义合作项目接口 interface CooperationProject { id: string; name: string; startDate: Date; endDate?: Date; status: string; description: string; } // 定义历史反馈接口 interface HistoricalFeedback { id: string; date: Date; content: string; rating: number; response?: string; } @Component({ selector: 'app-project-detail', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule, MatDialogModule], templateUrl: './project-detail.html', styleUrls: ['./project-detail.scss', '../customer-service-styles.scss'] }) export class ProjectDetail implements OnInit, AfterViewChecked { // 项目ID projectId = ''; // 项目详情 project = signal(null); // 添加 toggleSidebar 方法 toggleSidebar(): void { // 这里可以实现侧边栏切换逻辑 // 如果需要与全局状态交互,可以注入相应的服务 console.log('Toggle sidebar'); } // 项目里程碑 milestones = signal([]); // 项目任务 tasks = signal([]); // 项目消息 messages = signal([]); // 项目文件 files = signal([]); // 客户反馈 feedbacks = signal([]); // 当前激活的标签页 activeTab = signal('overview'); // 新消息内容 newMessage = signal(''); // 项目阶段数据 - 进度时间轴 projectStages: ProjectStage[] = [ { name: '需求沟通', completed: true, inProgress: false, startDate: new Date('2023-06-01'), endDate: new Date('2023-06-08'), responsible: '客服小李', details: '完成客户需求确认、现场量房和初步方案讨论' }, { name: '建模', completed: true, inProgress: false, startDate: new Date('2023-06-09'), endDate: new Date('2023-06-18'), responsible: '张设计师', details: '创建3D模型和基础渲染' }, { name: '软装', completed: false, inProgress: true, startDate: new Date('2023-06-19'), responsible: '张设计师', details: '选择和搭配家具、灯具和装饰品' }, { name: '渲染', completed: false, inProgress: false, startDate: new Date('2023-06-26'), responsible: '张设计师', details: '生成最终渲染图' }, { name: '后期', completed: false, inProgress: false, startDate: new Date('2023-07-03'), responsible: '张设计师', details: '后期图像处理和优化' }, { name: '投诉处理', completed: false, inProgress: false, startDate: new Date('2023-07-10'), endDate: new Date('2023-07-15'), responsible: '客服小李', details: '项目验收、交付和投诉处理' } ]; // 企业微信聊天相关 @ViewChild('wechatMessages') wechatMessagesContainer: any; wechatMessagesList: WechatMessage[] = []; wechatInput = ''; scrollToBottom = false; // 历史服务记录相关 consultationRecords = signal([]); cooperationProjects = signal([]); historicalFeedbacks = signal([]); // 售后处理弹窗状态 showModificationRequest = false; showComplaintWarning = false; showRefundRequest = false; // 项目状态颜色映射 statusColors = { '进行中': 'primary', '待确认': 'warning', '已完成': 'success', '已暂停': 'secondary', '已取消': 'danger' }; // 计算完成进度 completionProgress = computed(() => { if (!this.tasks().length) return 0; const completedTasks = this.tasks().filter(task => task.isCompleted).length; return Math.round((completedTasks / this.tasks().length) * 100); }); constructor( private route: ActivatedRoute, private projectService: ProjectService, private dialog: MatDialog ) { // 获取路由参数中的项目ID this.route.paramMap.subscribe(params => { this.projectId = params.get('id') || ''; }); } ngOnInit(): void { this.loadProjectDetails(); } // 加载项目详情 loadProjectDetails(): void { // 模拟从服务获取项目数据 this.projectService.getProjectById(this.projectId).subscribe(project => { if (project) { this.project.set(project); } }); // 加载模拟数据 this.loadMockData(); } // 加载模拟数据 // 修复 loadMockData 方法中的任务对象,确保类型一致性 loadMockData(): void { // 初始化企业微信聊天 this.initWechatMessages(); // 初始化历史服务记录 this.initHistoricalServiceRecords(); // 模拟里程碑数据 this.milestones.set([ { id: 'm1', title: '客户需求确认', description: '确认客户的装修需求和风格偏好', dueDate: new Date('2023-06-10'), completedDate: new Date('2023-06-08'), isCompleted: true }, { id: 'm2', title: '设计方案提交', description: '提交初步设计方案供客户审阅', dueDate: new Date('2023-06-20'), completedDate: new Date('2023-06-18'), isCompleted: true }, { id: 'm3', title: '方案修改与确认', description: '根据客户反馈修改并最终确认方案', dueDate: new Date('2023-06-25'), completedDate: undefined, isCompleted: false }, { id: 'm4', title: '施工图制作', description: '制作详细的施工图纸', dueDate: new Date('2023-07-05'), completedDate: undefined, isCompleted: false }, { id: 'm5', title: '项目验收', description: '客户验收最终成果', dueDate: new Date('2023-07-15'), completedDate: undefined, isCompleted: false } ]); // 模拟任务数据 - 确保所有任务对象都有完整的必填属性 this.tasks.set([ { id: 't1', title: '现场量房', description: '前往客户现场进行房屋测量', projectId: this.projectId, projectName: '现代简约风格三居室设计', assignee: '张设计师', deadline: new Date('2023-06-05'), completedDate: new Date('2023-06-04'), isCompleted: true, isOverdue: false, priority: 'high', stage: '需求沟通' // 添加 stage 属性,确保符合 Task 接口 }, { id: 't2', title: '设计初稿', description: '完成设计方案初稿', projectId: this.projectId, projectName: '现代简约风格三居室设计', assignee: '张设计师', deadline: new Date('2023-06-15'), completedDate: new Date('2023-06-14'), isCompleted: true, isOverdue: false, priority: 'high', stage: '建模' }, { id: 't3', title: '客户沟通', description: '与客户沟通设计方案反馈', projectId: this.projectId, projectName: '现代简约风格三居室设计', assignee: '客服小李', deadline: new Date('2023-06-22'), completedDate: undefined, isCompleted: false, isOverdue: false, priority: 'medium', stage: '需求沟通' }, { id: 't4', title: '方案修改', description: '根据客户反馈修改设计方案', projectId: this.projectId, projectName: '现代简约风格三居室设计', assignee: '张设计师', deadline: new Date('2023-06-28'), completedDate: undefined, isCompleted: false, isOverdue: false, priority: 'high', stage: '软装' }, { id: 't5', title: '施工图绘制', description: '绘制详细的施工图纸', projectId: this.projectId, projectName: '现代简约风格三居室设计', assignee: '王设计师', deadline: new Date('2023-07-10'), completedDate: undefined, isCompleted: false, isOverdue: false, priority: 'medium', stage: '渲染' } ]); // 模拟消息数据 this.messages.set([ { id: 'msg1', sender: '客户王先生', content: '设计方案收到了,整体很满意,客厅的颜色可以再调整一下吗?', timestamp: new Date('2023-06-19T10:30:00'), isRead: true, type: 'text' }, { id: 'msg2', sender: '客服小李', content: '好的,我们会根据您的需求调整客厅颜色方案,稍后给您发送修改后的效果图。', timestamp: new Date('2023-06-19T10:45:00'), isRead: true, type: 'text' }, { id: 'msg3', sender: '张设计师', content: '修改后的客厅效果图已上传,请查收。', timestamp: new Date('2023-06-19T14:20:00'), isRead: true, type: 'text' }, { id: 'msg4', sender: '客户王先生', content: '这个效果很好,就按照这个方案进行吧!', timestamp: new Date('2023-06-20T09:15:00'), isRead: true, type: 'text' }, { id: 'msg5', sender: '客服小李', content: '收到,我们会尽快开始下一阶段的工作。', timestamp: new Date('2023-06-20T09:30:00'), isRead: true, type: 'text' } ]); // 模拟文件数据 this.files.set([ { id: 'file1', name: '设计方案初稿.pdf', type: 'document', size: '2.5MB', url: 'https://example.com/files/design-proposal.pdf', uploadedBy: '张设计师', uploadedAt: new Date('2023-06-15'), downloadCount: 12 }, { id: 'file2', name: '客厅效果图.png', type: 'image', size: '1.8MB', url: 'https://example.com/files/living-room.png', uploadedBy: '张设计师', uploadedAt: new Date('2023-06-18'), downloadCount: 8 }, { id: 'file3', name: '修改后客厅效果图.png', type: 'image', size: '2.1MB', url: 'https://example.com/files/living-room-revised.png', uploadedBy: '张设计师', uploadedAt: new Date('2023-06-19'), downloadCount: 5 }, { id: 'file4', name: '客户需求文档.docx', type: 'document', size: '1.2MB', url: 'https://example.com/files/requirements.docx', uploadedBy: '客服小李', uploadedAt: new Date('2023-06-05'), downloadCount: 10 }, { id: 'file5', name: '房屋测量数据.xlsx', type: 'spreadsheet', size: '0.8MB', url: 'https://example.com/files/measurement.xlsx', uploadedBy: '张设计师', uploadedAt: new Date('2023-06-04'), downloadCount: 7 } ]); // 模拟客户反馈 this.feedbacks.set([ { id: 'fb1', projectId: this.projectId, customerName: '王先生', content: '对设计方案整体满意,但希望客厅颜色能更柔和一些。', rating: 4, createdAt: new Date('2023-06-19'), status: '已解决', response: '已根据您的需求调整了客厅颜色方案,详见最新上传的效果图。', isSatisfied: true } ]); } // 切换标签页 switchTab(tab: string): void { this.activeTab.set(tab); } // 增强版发送消息功能 sendMessage(): void { if (this.newMessage().trim()) { // 添加发送动画效果 const sendBtn = document.querySelector('.message-actions .primary-btn'); if (sendBtn) { sendBtn.classList.add('sending'); setTimeout(() => sendBtn.classList.remove('sending'), 300); } const newMsg: Message = { id: `msg${Date.now()}`, sender: '客服小李', content: this.newMessage().trim(), timestamp: new Date(), isRead: true, type: 'text' }; this.messages.set([...this.messages(), newMsg]); this.newMessage.set(''); // 自动滚动到底部 setTimeout(() => { const container = document.querySelector('.messages-list'); if (container) { container.scrollTop = container.scrollHeight; } }, 100); } } // 增强版完成任务功能 completeTask(taskId: string): void { // 添加完成动画效果 const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`); if (taskElement) { taskElement.classList.add('completing'); setTimeout(() => taskElement.classList.remove('completing'), 500); } this.tasks.set( this.tasks().map(task => task.id === taskId ? { ...task, isCompleted: true, completedDate: new Date(), isOverdue: false } : task ) ); // 播放完成音效 this.playSound('complete'); } // 修复 completeMilestone 方法中的类型问题 completeMilestone(milestoneId: string): void { this.milestones.set( this.milestones().map(milestone => milestone.id === milestoneId ? { ...milestone, isCompleted: true, completedDate: new Date() } : milestone ) ); } // 增强 formatDate 和 formatDateTime 方法的类型安全 formatDate(date: Date | string | null | undefined): string { if (!date) return '-'; try { return new Date(date).toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }); } catch (error) { console.error('日期格式化错误:', error); return '-'; } } formatDateTime(date: Date | string | null | undefined): string { if (!date) return '-'; try { return new Date(date).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (error) { console.error('日期时间格式化错误:', error); return '-'; } } // 下载文件 downloadFile(file: FileItem): void { console.log('下载文件:', file.name); // 实际应用中,这里会处理文件下载逻辑 } // 查看文件预览 previewFile(file: FileItem): void { console.log('预览文件:', file.name); // 实际应用中,这里会打开文件预览 } // 获取项目状态的CSS类名 getProjectStatusClass(status: string | null | undefined): string { if (!status) return 'status-default'; switch (status) { case '进行中': return 'status-active'; case '待确认': return 'status-pending'; case '已完成': return 'status-completed'; default: return 'status-default'; } } // 获取反馈客户名称(带安全检查) getFeedbackCustomerName(feedback: CustomerFeedback | undefined | null): string { if (!feedback) return '未知客户'; return feedback.customerName || '未知客户'; } // 获取反馈评分(带安全检查) getFeedbackRating(feedback: CustomerFeedback | undefined | null): number { if (!feedback) return 0; return feedback.rating || 0; } // 消息输入事件处理 onMessageInput(event: Event): void { const target = event.target as HTMLTextAreaElement; this.newMessage.set(target.value); } // 添加正确的progressFillWidth计算属性 progressFillWidth = computed(() => { return `${this.completionProgress()}%`; }); // AfterViewChecked 接口实现,用于滚动到聊天窗口底部 ngAfterViewChecked(): void { if (this.scrollToBottom && this.wechatMessagesContainer) { this.wechatMessagesContainer.nativeElement.scrollTop = this.wechatMessagesContainer.nativeElement.scrollHeight; this.scrollToBottom = false; } } // 初始化企业微信聊天 initWechatMessages(): void { // 模拟企业微信聊天消息 this.wechatMessagesList = [ { sender: '客服小李', content: '您好,张先生,我们已经收到您的需求,正在为您制定设计方案。', timestamp: new Date('2023-06-01T10:30:00') }, { sender: '张先生', content: '好的,我希望客厅光线充足,储物空间充足,并且使用环保材料。', timestamp: new Date('2023-06-01T10:35:00') }, { sender: '客服小李', content: '明白了,我们会重点考虑这些需求。预计3天内可以完成初步方案。', timestamp: new Date('2023-06-01T10:40:00') }, { sender: '张设计师', content: '您好,我是负责您项目的设计师小张。今天下午我会去现场量房,请问您方便吗?', timestamp: new Date('2023-06-02T14:00:00') }, { sender: '张先生', content: '下午好的,我在小区门口等您。', timestamp: new Date('2023-06-02T14:05:00') }, { sender: '张设计师', content: '已完成量房,正在制作初步设计方案。', timestamp: new Date('2023-06-05T18:30:00') }, { sender: '客服小李', content: '张先生,初步设计方案已完成,您什么时候方便查看一下?', timestamp: new Date('2023-06-08T10:00:00') } ]; // 确保滚动到底部 setTimeout(() => { this.scrollToBottom = true; }, 100); } // 增强版发送企业微信消息 sendWechatMessage(): void { if (!this.wechatInput.trim()) return; // 添加发送动画 const sendBtn = document.querySelector('.wechat-input-area .send-btn'); if (sendBtn) { sendBtn.classList.add('sending'); setTimeout(() => sendBtn.classList.remove('sending'), 300); } const newMessage: WechatMessage = { sender: '客服小李', content: this.wechatInput.trim(), timestamp: new Date() }; this.wechatMessagesList = [...this.wechatMessagesList, newMessage]; this.wechatInput = ''; this.scrollToBottom = true; // 播放发送音效 this.playSound('message'); // 模拟对方回复 setTimeout(() => { const replyMessage: WechatMessage = { sender: '张先生', content: '收到,我稍后查看。', timestamp: new Date() }; this.wechatMessagesList = [...this.wechatMessagesList, replyMessage]; this.scrollToBottom = true; // 播放接收音效 this.playSound('notification'); }, 2000); } // 新增播放音效方法 playSound(type: 'message' | 'notification' | 'complete'): void { // 实际项目中这里会播放对应的音效 console.log(`播放${type}音效`); } // 初始化历史服务记录 initHistoricalServiceRecords(): void { // 模拟过往咨询记录 this.consultationRecords.set([ { id: 'cons1', date: new Date('2023-01-15'), content: '咨询关于厨房改造的可行性和预算', status: '已解决' }, { id: 'cons2', date: new Date('2023-03-20'), content: '询问装修材料环保认证相关问题', status: '已解决' }, { id: 'cons3', date: new Date('2023-05-10'), content: '了解装修分期付款方案', status: '已解决' } ]); // 模拟合作项目记录 this.cooperationProjects.set([ { id: 'proj1', name: '2022年现代简约卧室设计项目', startDate: new Date('2022-08-15'), endDate: new Date('2022-10-30'), status: '已完成', description: '为客户设计并实施了现代简约风格的卧室改造,包括定制衣柜和床头背景墙' }, { id: 'proj2', name: '2023年欧式厨房设计项目', startDate: new Date('2023-02-01'), endDate: new Date('2023-04-15'), status: '已完成', description: '设计并安装了全套欧式风格厨房,包括橱柜、台面和电器选型' } ]); // 模拟历史反馈/评价 this.historicalFeedbacks.set([ { id: 'fb1', date: new Date('2022-11-05'), content: '卧室设计非常满意,空间利用合理,风格符合预期', rating: 5, response: '感谢您的好评,我们会继续努力为您提供优质服务' }, { id: 'fb2', date: new Date('2023-04-20'), content: '厨房装修质量很好,但工期比预期稍长', rating: 4, response: '感谢您的反馈,我们已经优化了施工流程,会在后续项目中改进' } ]); } // 格式化为时间显示 formatTime(date: Date): string { if (!date) return ''; return new Date(date).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } // 获取当前日期的计算属性 currentDate = computed(() => { return new Date(); }); // 售后处理入口方法 openModificationRequest(): void { const dialogRef = this.dialog.open(ModificationRequestDialog, { width: '500px', data: { projectId: this.projectId, projectName: this.project()?.name || '现代简约风格三居室设计', projectStatus: this.project()?.status || '进行中' } }); dialogRef.afterClosed().subscribe(result => { if (result) { console.log('申请修改提交成功', result); // 这里可以添加提交成功后的处理逻辑,如刷新数据、显示通知等 } }); } openComplaintWarning(): void { const dialogRef = this.dialog.open(ComplaintWarningDialog, { width: '500px', data: { projectId: this.projectId, projectName: this.project()?.name || '现代简约风格三居室设计' } }); dialogRef.afterClosed().subscribe(result => { if (result) { console.log('投诉预警提交成功', result); // 这里可以添加提交成功后的处理逻辑 } }); } openRefundRequest(): void { const dialogRef = this.dialog.open(RefundRequestDialog, { width: '500px', data: { projectId: this.projectId, projectName: this.project()?.name || '现代简约风格三居室设计', projectAmount: 85000 // 模拟项目金额 } }); dialogRef.afterClosed().subscribe(result => { if (result) { console.log('申请退款提交成功', result); // 这里可以添加提交成功后的处理逻辑 } }); } }