import { Component, OnInit, OnDestroy, signal, computed, Inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { Router, RouterModule } from '@angular/router'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { ProjectService } from '../../../services/project.service'; import { ConsultationOrderDialogComponent } from '../consultation-order/consultation-order-dialog.component'; import { Project, ProjectStatus, ProjectStage } from '../../../models/project.model'; // 定义项目列表项接口,包含计算后的属性 interface ProjectListItem extends Project { progress: number; daysUntilDeadline: number; isUrgent: boolean; tagDisplayText: string; } @Component({ selector: 'app-project-list', standalone: true, imports: [CommonModule, FormsModule, RouterModule, MatDialogModule], templateUrl: './project-list.html', styleUrls: ['./project-list.scss', '../customer-service-styles.scss'] }) export class ProjectList implements OnInit, OnDestroy { // 项目列表数据 projects = signal([]); // 原始项目数据(用于筛选) allProjects = signal([]); // 视图模式:卡片 / 列表 / 监控大盘(默认卡片) viewMode = signal<'card' | 'list' | 'dashboard'>('card'); // 看板列配置 columns = [ { id: 'pending', name: '待分配' }, { id: 'req', name: '需求深化' }, { id: 'delivery', name: '交付中' }, { id: 'done', name: '已完成' } ] as const; // 基础项目集合(服务端返回 + 本地生成),用于二次处理 private baseProjects: Project[] = []; // 消息监听器 private messageListener?: (event: MessageEvent) => void; // 添加toggleSidebar方法 toggleSidebar(): void { // 侧边栏切换逻辑 console.log('Toggle sidebar'); } // 筛选和排序状态 searchTerm = signal(''); statusFilter = signal('all'); stageFilter = signal('all'); sortBy = signal('deadline'); // 当前页码 currentPage = signal(1); // 每页显示数量 pageSize = 8; // 分页后的项目列表(列表模式下可用) paginatedProjects = computed(() => { const filteredProjects = this.projects(); const startIndex = (this.currentPage() - 1) * this.pageSize; return filteredProjects.slice(startIndex, startIndex + this.pageSize); }); // 总页数 totalPages = computed(() => { return Math.ceil(this.projects().length / this.pageSize); }); // 筛选和排序选项 statusOptions = [ { value: 'all', label: '全部' }, { value: 'pending', label: '待分配' }, { value: 'req', label: '需求深化' }, { value: 'delivery', label: '交付中' }, { value: 'done', label: '已完成' } ]; stageOptions = [ { value: 'all', label: '全部阶段' }, { value: '需求沟通', label: '需求沟通' }, { value: '建模', label: '建模' }, { value: '软装', label: '软装' }, { value: '渲染', label: '渲染' }, { value: '后期', label: '后期' }, { value: '投诉处理', label: '投诉处理' } ]; sortOptions = [ { value: 'deadline', label: '截止日期' }, { value: 'createdAt', label: '创建时间' }, { value: 'name', label: '项目名称' } ]; constructor( private projectService: ProjectService, private router: Router, private dialog: MatDialog ) {} ngOnInit(): void { // 读取上次的视图记忆 const saved = localStorage.getItem('cs.viewMode'); if (saved === 'card' || saved === 'list' || saved === 'dashboard') { this.viewMode.set(saved as 'card' | 'list' | 'dashboard'); } this.loadProjects(); // 添加消息监听器,处理来自iframe的导航请求 this.messageListener = (event: MessageEvent) => { // 验证消息来源(可以根据需要添加更严格的验证) if (event.data && event.data.type === 'navigate' && event.data.route) { this.router.navigate([event.data.route]); } }; window.addEventListener('message', this.messageListener); } ngOnDestroy(): void { // 清理消息监听器 if (this.messageListener) { window.removeEventListener('message', this.messageListener); } } // 视图切换 toggleView(mode: 'card' | 'list' | 'dashboard') { if (this.viewMode() !== mode) { this.viewMode.set(mode); localStorage.setItem('cs.viewMode', mode); } } // 加载项目列表 loadProjects(): void { this.projectService.getProjects().subscribe(projects => { this.allProjects.set(projects); // 生成基础列表(服务返回 + 模拟) this.baseProjects = [...projects, ...this.generateMockProjects()]; this.processProjects(this.baseProjects); }); } // 处理项目数据,添加计算属性 processProjects(projects: Project[]): void { const processedProjects = projects.map(project => { // 计算项目进度(模拟) const progress = this.calculateProjectProgress(project); // 计算距离截止日期的天数 const daysUntilDeadline = this.calculateDaysUntilDeadline(project.deadline); // 判断是否紧急(截止日期前3天或已逾期) const isUrgent = daysUntilDeadline <= 3 && project.status === '进行中'; // 生成标签显示文本 const tagDisplayText = this.generateTagDisplayText(project); return { ...project, progress, daysUntilDeadline, isUrgent, tagDisplayText }; }); this.projects.set(this.applyFiltersAndSorting(processedProjects)); } // 应用筛选和排序 applyFiltersAndSorting(projects: ProjectListItem[]): ProjectListItem[] { let filteredProjects = [...projects]; // 搜索筛选 if (this.searchTerm().trim()) { const searchLower = this.searchTerm().toLowerCase().trim(); filteredProjects = filteredProjects.filter(project => project.name.toLowerCase().includes(searchLower) || project.customerName.toLowerCase().includes(searchLower) ); } // 状态筛选(按看板列映射) if (this.statusFilter() !== 'all') { const col = this.statusFilter() as 'pending' | 'req' | 'delivery' | 'done'; filteredProjects = filteredProjects.filter(project => this.getColumnIdForProject(project) === col ); } // 阶段筛选 if (this.stageFilter() !== 'all') { filteredProjects = filteredProjects.filter(project => project.currentStage === this.stageFilter() ); } // 排序 filteredProjects.sort((a, b) => { switch (this.sortBy()) { case 'deadline': return new Date(a.deadline).getTime() - new Date(b.deadline).getTime(); case 'createdAt': return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); case 'name': return a.name.localeCompare(b.name); default: return 0; } }); return filteredProjects; } // 生成标签显示文本 generateTagDisplayText(project: Project): string { if (!project.customerTags || project.customerTags.length === 0) { return '普通项目'; } const tag = project.customerTags[0]; return `${tag.preference}${tag.needType}`; } // 计算项目进度(模拟) calculateProjectProgress(project: Project): number { if (project.status === '已完成') return 100; if (project.status === '已暂停' || project.status === '已延期') return 0; // 基于当前阶段计算进度 const stageProgress: Record = { '需求沟通': 20, '建模': 40, '软装': 60, '渲染': 80, '后期': 90, '投诉处理': 100, 订单创建: 0, 方案确认: 0, 尾款结算: 0, 客户评价: 0 }; return stageProgress[project.currentStage] || 0; } // 计算距离截止日期的天数 calculateDaysUntilDeadline(deadline: Date): number { const now = new Date(); const deadlineDate = new Date(deadline); const diffTime = deadlineDate.getTime() - now.getTime(); return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); } // 生成模拟项目数据 generateMockProjects(): Project[] { const statuses: ProjectStatus[] = ['进行中', '已完成', '已暂停', '已延期']; const stages: ProjectStage[] = ['需求沟通', '建模', '软装', '渲染', '后期', '投诉处理']; const preferences: ('现代' | '宋式' | '欧式')[] = ['现代', '宋式', '欧式']; const needTypes: ('硬装' | '软装')[] = ['硬装', '软装']; const sources: ('朋友圈' | '信息流')[] = ['朋友圈', '信息流']; const mockProjects: Project[] = []; for (let i = 1; i <= 12; i++) { const baseDate = new Date(); const createdDate = new Date(baseDate.setDate(baseDate.getDate() - Math.floor(Math.random() * 30))); baseDate.setDate(baseDate.getDate() + Math.floor(Math.random() * 15) + 5); const deadlineDate = new Date(baseDate); const preference = preferences[Math.floor(Math.random() * preferences.length)]; const needType = needTypes[Math.floor(Math.random() * needTypes.length)]; const source = sources[Math.floor(Math.random() * sources.length)]; const stage = stages[Math.floor(Math.random() * stages.length)]; const status = stage === '投诉处理' ? '已完成' : statuses[Math.floor(Math.random() * 3)]; mockProjects.push({ id: `mock-${i}`, name: `${preference}风格${needType === '硬装' ? '全屋' : '客厅'}设计`, customerName: `客户${String.fromCharCode(64 + i)}`, customerTags: [ { source: source, needType: needType, preference: preference, colorAtmosphere: i % 2 === 0 ? '简约明亮' : '温馨舒适' } ], highPriorityNeeds: i % 3 === 0 ? ['需快速交付', '重要客户'] : [], status: status, currentStage: stage, stage: stage, createdAt: createdDate, deadline: deadlineDate, assigneeId: i % 4 === 0 ? '' : `designer${i % 3 + 1}`, assigneeName: i % 4 === 0 ? '' : `设计师${String.fromCharCode(64 + (i % 3 + 1))}`, skillsRequired: [preference + '风格', needType] }); } return mockProjects; } // 列表/筛选交互(保留已有实现) onSearch(): void { // 搜索后重算 this.processProjects(this.baseProjects); } onStatusChange(event: Event): void { const value = (event.target as HTMLSelectElement).value; this.statusFilter.set(value); this.processProjects(this.baseProjects); } onStageChange(event: Event): void { const value = (event.target as HTMLSelectElement).value; this.stageFilter.set(value); this.processProjects(this.baseProjects); } onSortChange(event: Event): void { const value = (event.target as HTMLSelectElement).value; this.sortBy.set(value); this.processProjects(this.baseProjects); } goToPage(page: number): void { if (page >= 1 && page <= this.totalPages()) { this.currentPage.set(page); } } prevPage(): void { if (this.currentPage() > 1) { this.currentPage.update(v => v - 1); } } nextPage(): void { if (this.currentPage() < this.totalPages()) { this.currentPage.update(v => v + 1); } } pageNumbers = computed(() => { const total = this.totalPages(); const pages: number[] = []; const maxToShow = Math.min(total, 5); for (let i = 1; i <= maxToShow; i++) pages.push(i); return pages; }); getAbsValue(value: number): number { return Math.abs(value); } formatDate(date: Date): string { const d = new Date(date); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; } getStatusClass(status: string): string { switch (status) { case '进行中': return 'status-in-progress'; case '已完成': return 'status-completed'; case '已暂停': return 'status-paused'; case '已延期': return 'status-overdue'; default: return ''; } } getStageClass(stage: string): string { switch (stage) { case '需求沟通': return 'stage-communication'; case '建模': return 'stage-modeling'; case '软装': return 'stage-decoration'; case '渲染': return 'stage-rendering'; case '后期': return 'stage-postproduction'; case '投诉处理': return 'stage-completed'; case '订单创建': return 'stage-active'; case '方案确认': return 'stage-active'; case '尾款结算': return 'stage-completed'; case '客户评价': return 'stage-completed'; default: return ''; } } // 看板分组逻辑 private isPendingAssignment(p: Project): boolean { return !p.assigneeId || p.assigneeId.trim() === ''; } private isRequirementElaboration(p: Project): boolean { // 已分配但仍在需求沟通阶段 return !this.isCompleted(p) && !this.isPendingAssignment(p) && p.currentStage === '需求沟通'; } private isInDelivery(p: Project): boolean { const deliveryStages: ProjectStage[] = ['建模', '软装', '渲染', '后期']; return !this.isCompleted(p) && !this.isPendingAssignment(p) && deliveryStages.includes(p.currentStage); } private isCompleted(p: Project): boolean { return p.status === '已完成'; } getProjectsByColumn(columnId: 'pending' | 'req' | 'delivery' | 'done'): ProjectListItem[] { const list = this.projects(); switch (columnId) { case 'pending': return list.filter(p => this.isPendingAssignment(p)); case 'req': return list.filter(p => this.isRequirementElaboration(p)); case 'delivery': return list.filter(p => this.isInDelivery(p)); case 'done': return list.filter(p => this.isCompleted(p)); } } // 新增:根据项目状态与阶段推断所在看板列 getColumnIdForProject(project: ProjectListItem): 'pending' | 'req' | 'delivery' | 'done' { if (this.isPendingAssignment(project)) return 'pending'; if (this.isRequirementElaboration(project)) return 'req'; if (this.isInDelivery(project)) return 'delivery'; if (this.isCompleted(project)) return 'done'; return 'req'; } // 详情跳转到设计师项目详情页面,传递客服角色标识和当前阶段信息 navigateToProject(project: ProjectListItem, columnId: 'pending' | 'req' | 'delivery' | 'done') { // 根据columnId映射到对应的阶段 const stageMapping = { 'pending': '订单创建', 'req': project.currentStage || '需求沟通', // 使用项目实际阶段或默认阶段 'delivery': project.currentStage || '建模', // 使用项目实际阶段或默认阶段 'done': '客户评价' }; this.router.navigate(['/designer/project-detail', project.id], { queryParams: { role: 'customer-service', activeTab: 'progress', currentStage: stageMapping[columnId] } }); } // 新增:直接进入沟通管理(消息)标签 navigateToMessages(project: ProjectListItem) { this.router.navigate(['/customer-service/messages'], { queryParams: { projectId: project.id } }); } // 导航到创建订单页面 navigateToCreateOrder() { // 打开咨询订单弹窗 const dialogRef = this.dialog.open(ConsultationOrderDialogComponent, { width: '900px', maxWidth: '95vw', maxHeight: '90vh', panelClass: 'consultation-order-dialog' }); // 监听订单创建成功事件 dialogRef.componentInstance.orderCreated.subscribe((orderData: any) => { // 关闭弹窗 dialogRef.close(); // 跳转到新创建的项目详情页面 this.router.navigate([ '/designer/project-detail', orderData.orderId ], { queryParams: { role: 'customer-service', activeTab: 'overview' } }); }); } }