import { Component, OnInit, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTableModule } from '@angular/material/table'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatDialogModule, MatDialog } from '@angular/material/dialog'; import { MatSortModule } from '@angular/material/sort'; import { ProjectDialogComponent } from './project-dialog/project-dialog'; // @ts-ignore: Component used in code but not in template import { ProjectService } from '../services/project.service'; import { ProjectAutoCaseService } from '../services/project-auto-case.service'; import { DesignerTeamAssignmentModalComponent, Designer, ProjectTeam } from '../../designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component'; import { mapStageToCorePhase, getCorePhaseName, getProjectStatusByStage, normalizeStage } from '../../../utils/project-stage-mapper'; interface Project { id: string; title: string; customer: string; customerId?: string; status: string; assignee: string; assigneeId?: string; createdAt?: Date; updatedAt?: Date; deadline?: Date; currentStage?: string; } @Component({ selector: 'app-project-management', standalone: true, imports: [ CommonModule, RouterModule, FormsModule, MatButtonModule, MatIconModule, MatTableModule, MatInputModule, MatSelectModule, MatPaginatorModule, MatDialogModule, MatSortModule, DesignerTeamAssignmentModalComponent ], templateUrl: './project-management.html', styleUrl: './project-management.scss' }) export class ProjectManagement implements OnInit { projects = signal([]); filteredProjects = signal([]); searchTerm = ''; statusFilter = ''; sortColumn = 'updatedAt'; sortDirection = 'desc'; pageSize = 10; currentPage = 0; loading = signal(false); // 团队分配相关属性 showTeamAssignmentModal = false; selectedProject: Project | null = null; projectTeams: ProjectTeam[] = []; currentTeamAssignment: any = { primaryTeamId: null, quotationAssignments: [], crossTeamCollaborators: [] }; currentQuotationItems: any[] = []; // 模拟项目团队数据 private mockProjectTeams: ProjectTeam[] = [ { id: 'team-1', name: '野生项目组', leaderId: 'designer-1', leaderName: '张佳乐', description: '专注于家庭装修设计,包括客厅、卧室、厨房等空间设计', members: [ { id: 'designer-1', name: '张佳乐', avatar: '/assets/avatars/zhang.jpg', teamId: 'team-1', teamName: '野生项目组', isTeamLeader: true, status: 'busy', idleDays: 0, recentOrders: 3, lastOrderDate: '2024-10-20', reviewDates: ['2024-10-25', '2024-10-28'], workload: 85, skills: ['空间设计', '建模', '渲染'], isInStagnantProject: false, availableDates: [], groupId: 'team-1', groupName: '野生项目组', isLeader: true, currentProjects: 3 }, { id: 'designer-2', name: '未知设计师', avatar: '/assets/avatars/unknown.jpg', teamId: 'team-1', teamName: '野生项目组', isTeamLeader: false, status: 'idle', idleDays: 6, recentOrders: 0, lastOrderDate: '2024-10-14', reviewDates: [], workload: 0, skills: ['软装', '后期'], isInStagnantProject: false, availableDates: ['2024-10-25', '2024-10-26', '2024-10-27'], groupId: 'team-1', groupName: '野生项目组', isLeader: false, currentProjects: 0 } ] }, { id: 'team-2', name: '无常项目组', leaderId: 'designer-3', leaderName: '江集', description: '专业商业空间设计,办公室、店铺等', members: [ { id: 'designer-3', name: '江集', avatar: '/assets/avatars/jiang.jpg', teamId: 'team-2', teamName: '无常项目组', isTeamLeader: true, status: 'busy', idleDays: 0, recentOrders: 2, lastOrderDate: '2024-10-21', reviewDates: ['2024-10-26'], workload: 70, skills: ['空间设计', '软装', '项目管理'], isInStagnantProject: false, availableDates: [], groupId: 'team-2', groupName: '无常项目组', isLeader: true, currentProjects: 2 } ] } ]; // 提供Math对象给模板使用 readonly Math = Math; // 状态颜色映射 statusColors: Record = { '待分配': '#FFAA00', '进行中': '#165DFF', '已完成': '#00B42A', '已暂停': '#FF7D00', '已延期': '#F53F3F', '已取消': '#86909C' }; // 状态文本映射 statusTexts: Record = { '待分配': '待分配', '进行中': '进行中', '已完成': '已完成', '已暂停': '已暂停', '已延期': '已延期', '已取消': '已取消' }; constructor( private dialog: MatDialog, private projectService: ProjectService, private projectAutoCaseService: ProjectAutoCaseService ) {} ngOnInit(): void { this.loadProjects(); } async loadProjects(): Promise { this.loading.set(true); try { const projects = await this.projectService.findProjects({ limit: 100 }); const projectList: Project[] = projects.map(p => { const json = this.projectService.toJSON(p); // 获取原始阶段 const rawStage = json.currentStage || json.stage || '订单分配'; // 🔄 规范化阶段名称(统一为四大核心阶段) const normalizedStage = normalizeStage(rawStage); // 🔄 根据阶段自动判断状态(与组长端逻辑保持一致) const autoStatus = getProjectStatusByStage(rawStage, json.status); console.log(`📊 [项目管理] "${json.title}":`, { 客户: json.customerName, 负责人: json.assigneeName, 角色: json.assigneeRole, 原始阶段: rawStage, 规范化阶段: normalizedStage, 状态: `${json.status} → ${autoStatus}` }); return { id: json.objectId, title: json.title || '未命名项目', customer: json.customerName || '未知客户', customerId: json.customerId, status: autoStatus, // 使用根据阶段自动判断的状态 assignee: json.assigneeName || '未分配', assigneeId: json.assigneeId, createdAt: json.createdAt?.iso || json.createdAt, updatedAt: json.updatedAt?.iso || json.updatedAt, deadline: json.deadline, currentStage: normalizedStage // 使用规范化后的阶段名称 }; }); this.projects.set(projectList); this.applyFilters(); } catch (error) { console.error('加载项目列表失败:', error); this.projects.set([]); } finally { this.loading.set(false); } } applyFilters(): void { let result = [...this.projects()]; // 搜索过滤 if (this.searchTerm) { const term = this.searchTerm.toLowerCase(); result = result.filter(project => project.title.toLowerCase().includes(term) || project.customer.toLowerCase().includes(term) || project.assignee.toLowerCase().includes(term) ); } // 状态过滤 if (this.statusFilter) { result = result.filter(project => project.status === this.statusFilter); } // 排序 - 按更新时间或创建时间 result.sort((a, b) => { if (this.sortColumn === 'updatedAt' || this.sortColumn === 'createdAt') { const dateA = a[this.sortColumn] ? new Date(a[this.sortColumn] as Date).getTime() : 0; const dateB = b[this.sortColumn] ? new Date(b[this.sortColumn] as Date).getTime() : 0; return this.sortDirection === 'asc' ? dateA - dateB : dateB - dateA; } else { const valueA = String((a as any)[this.sortColumn] || '').toLowerCase(); const valueB = String((b as any)[this.sortColumn] || '').toLowerCase(); return this.sortDirection === 'asc' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA); } }); this.filteredProjects.set(result); } onSearch(): void { this.applyFilters(); } onStatusFilterChange(): void { this.applyFilters(); } onSort(column: string): void { if (this.sortColumn === column) { this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'; } else { this.sortColumn = column; this.sortDirection = 'asc'; } this.applyFilters(); } // 简化的编辑对话框(只允许修改名字和分配组员) openEditDialog(project: Project): void { // TODO: 实现简单的编辑对话框,使用设计师分配组件 // 暂时使用alert提示 window?.fmode?.alert('编辑功能将在设计师分配组件对接完成后实现'); } // 打开团队分配弹窗 openTeamAssignmentModal(project: Project): void { this.selectedProject = project; // 加载项目团队数据 this.projectTeams = this.mockProjectTeams; // 初始化当前团队分配信息 this.currentTeamAssignment = { primaryTeamId: project.assigneeId || null, quotationAssignments: [], crossTeamCollaborators: [] }; // TODO: 从Parse Server加载报价项数据 this.currentQuotationItems = []; this.showTeamAssignmentModal = true; console.log('打开团队分配弹窗:', project, '团队数据:', this.projectTeams); } // 关闭团队分配弹窗 closeTeamAssignmentModal(): void { this.showTeamAssignmentModal = false; this.selectedProject = null; this.currentTeamAssignment = { primaryTeamId: null, quotationAssignments: [], crossTeamCollaborators: [] }; this.currentQuotationItems = []; } // 确认团队分配 confirmTeamAssignment(event: any): void { console.log('确认团队分配:', event); // TODO: 保存团队分配数据到Parse Server this.closeTeamAssignmentModal(); // 重新加载项目列表 this.loadProjects(); } formatCurrency(amount: number): string { return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', minimumFractionDigits: 0 }).format(amount); } get paginatedProjects(): Project[] { const startIndex = this.currentPage * this.pageSize; return this.filteredProjects().slice(startIndex, startIndex + this.pageSize); } get totalPages(): number { return Math.ceil(this.filteredProjects().length / this.pageSize); } // 项目状态统计计算属性 get inProgressProjectsCount(): number { return this.projects().filter(p => p.status === '进行中').length; } get completedProjectsCount(): number { return this.projects().filter(p => p.status === '已完成').length; } get pendingProjectsCount(): number { return this.projects().filter(p => p.status === '待分配').length; } // 项目总数 get totalProjectsCount(): number { return this.projects().length; } onPageChange(page: number): void { this.currentPage = page; } // 生成页码数组 getPageNumbers(): number[] { const totalPages = this.totalPages; const currentPage = this.currentPage; const pageNumbers: number[] = []; // 简单实现:显示所有页码 for (let i = 0; i < totalPages; i++) { pageNumbers.push(i); } return pageNumbers; } }