import { Component, OnInit, OnDestroy, signal, computed, Inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { Router, RouterModule, ActivatedRoute } 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'; import { FmodeParse, FmodeObject } from 'fmode-ng/parse'; import { ProfileService } from '../../../services/profile.service'; import { normalizeStage, getProjectStatusByStage } from '../../../utils/project-stage-mapper'; const Parse = FmodeParse.with('nova'); // 定义项目列表项接口,包含计算后的属性 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: 'order', name: '订单分配' }, { id: 'requirements', name: '确认需求' }, { id: 'delivery', name: '交付执行' }, { id: 'aftercare', 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: 'order', label: '订单分配' }, { value: 'requirements', label: '确认需求' }, { value: 'delivery', label: '交付执行' }, { value: 'aftercare', 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: '项目名称' } ]; // Parse相关 company: FmodeObject | null = null; currentProfile: FmodeObject | null = null; isLoading = signal(false); loadError = signal(null); constructor( private projectService: ProjectService, private router: Router, private route: ActivatedRoute, private dialog: MatDialog, private profileService: ProfileService ) {} async ngOnInit(): Promise { // 读取上次的视图记忆 const saved = localStorage.getItem('cs.viewMode'); if (saved === 'card' || saved === 'list' || saved === 'dashboard') { this.viewMode.set(saved as 'card' | 'list' | 'dashboard'); } // 初始化用户和公司信息 await this.initializeUserAndCompany(); // 清理重复的Product记录(确保每个项目在每个阶段只出现一次) await this.cleanupDuplicateProducts(); // 加载真实项目数据 await this.loadProjects(); // 处理来自dashboard的查询参数 this.route.queryParams.subscribe(params => { const filter = params['filter']; if (filter === 'all') { // 显示所有项目 - 重置筛选 this.statusFilter.set('all'); console.log('✅ 显示所有项目'); } else if (filter === 'pending') { // 筛选待分配项目 - 使用'order'列ID this.statusFilter.set('order'); console.log('✅ 筛选待分配项目(订单分配阶段)'); } }); // 添加消息监听器,处理来自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); // 🔍 添加全局调试方法 (window as any).debugCustomerServiceProjects = () => { console.log('🔍 [客服端项目调试] 当前项目列表:'); const projects = this.allProjects(); projects.forEach((project, index) => { console.log(`项目${index + 1}: "${project.name}"`); console.log(` - currentStage: ${project.currentStage}`); console.log(` - stage: ${project.stage}`); console.log(` - status: ${project.status}`); console.log(` - 看板列: ${this.getColumnIdForProject(project as ProjectListItem)}`); console.log(' ---'); }); }; console.log('🔍 调试方法已添加到全局: window.debugCustomerServiceProjects()'); } // 🎯 判断是否为交付执行阶段(用于统一显示) private isDeliveryExecutionStage(stage: string): boolean { if (!stage) return false; const trimmedStage = stage.trim(); const lowerStage = trimmedStage.toLowerCase(); return trimmedStage === '交付执行' || lowerStage === 'delivery' || // 建模相关 trimmedStage === '建模' || trimmedStage === '建模阶段' || trimmedStage === '白模' || trimmedStage === '白膜' || lowerStage === 'modeling' || // 软装相关 trimmedStage === '软装' || trimmedStage === '软装阶段' || lowerStage === 'soft_decor' || lowerStage === 'decoration' || // 渲染相关 trimmedStage === '渲染' || trimmedStage === '渲染阶段' || lowerStage === 'rendering' || // 后期相关 trimmedStage === '后期制作' || trimmedStage === '后期处理' || trimmedStage === '后期' || lowerStage === 'postproduction' || // 评审修改相关 trimmedStage === '评审' || trimmedStage === '方案评审' || trimmedStage === '修改' || trimmedStage === '方案修改' || trimmedStage === '修订' || lowerStage === 'review' || lowerStage === 'revision' || // 其他可能的交付执行子阶段 trimmedStage === '设计' || trimmedStage === '设计阶段' || trimmedStage === '制作' || trimmedStage === '制作阶段' || trimmedStage === '完善' || trimmedStage === '优化' || trimmedStage === '调整'; } // 🔍 验证项目分配统计 private validateProjectDistribution(projects: Project[]): void { const orderCount = projects.filter(p => this.isOrderAssignment(p)).length; const requirementsCount = projects.filter(p => this.isRequirementsConfirmation(p)).length; const deliveryCount = projects.filter(p => this.isDeliveryExecution(p)).length; const aftercareCount = projects.filter(p => this.isAftercare(p)).length; console.log('🔍 [客服端项目分配统计]:'); console.log(` 订单分配: ${orderCount} 个项目`); console.log(` 确认需求: ${requirementsCount} 个项目`); console.log(` 交付执行: ${deliveryCount} 个项目`); console.log(` 售后: ${aftercareCount} 个项目`); console.log(` 总计: ${projects.length} 个项目`); // 🎯 与期望值对比 const expected = { order: 3, requirements: 4, delivery: 8, aftercare: 5 }; console.log('🎯 [期望值对比]:'); console.log(` 订单分配: 实际${orderCount} vs 期望${expected.order} ${orderCount === expected.order ? '✅' : '❌'}`); console.log(` 确认需求: 实际${requirementsCount} vs 期望${expected.requirements} ${requirementsCount === expected.requirements ? '✅' : '❌'}`); console.log(` 交付执行: 实际${deliveryCount} vs 期望${expected.delivery} ${deliveryCount === expected.delivery ? '✅' : '❌'}`); console.log(` 售后: 实际${aftercareCount} vs 期望${expected.aftercare} ${aftercareCount === expected.aftercare ? '✅' : '❌'}`); // 🔍 如果数量不匹配,显示详细信息 if (orderCount !== expected.order) { console.log('🔍 [订单分配阶段项目详情]:'); projects.filter(p => this.isOrderAssignment(p)).forEach(p => { console.log(` - "${p.name}": currentStage="${p.currentStage}"`); }); } if (deliveryCount !== expected.delivery) { console.log('🔍 [交付执行阶段项目详情]:'); projects.filter(p => this.isDeliveryExecution(p)).forEach(p => { console.log(` - "${p.name}": currentStage="${p.currentStage}"`); }); // 🔍 显示所有项目的原始阶段,帮助识别遗漏的阶段 console.log('🔍 [所有项目的原始阶段]:'); projects.forEach(p => { const isDelivery = this.isDeliveryExecution(p); console.log(` - "${p.name}": "${p.currentStage}" ${isDelivery ? '✅交付执行' : ''}`); }); } } 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); } } // 初始化用户和公司信息 private async initializeUserAndCompany(): Promise { try { // 方法1: 从localStorage获取公司ID(参考team-leader的实现) const companyId = localStorage.getItem('company'); if (companyId) { // 创建公司指针对象 const CompanyClass = Parse.Object.extend('Company'); this.company = new CompanyClass(); this.company.id = companyId; console.log('✅ 从localStorage加载公司ID:', companyId); } else { // 方法2: 从Profile获取公司信息 this.currentProfile = await this.profileService.getCurrentProfile(); if (!this.currentProfile) { throw new Error('无法获取用户信息'); } // 获取公司信息 this.company = this.currentProfile.get('company'); if (!this.company) { throw new Error('无法获取公司信息'); } console.log('✅ 从Profile加载公司信息:', this.company.get('name')); } } catch (error) { console.error('❌ 初始化用户和公司信息失败:', error); this.loadError.set('加载用户信息失败,请刷新页面重试'); } } // 获取公司指针 private getCompanyPointer() { if (!this.company) { throw new Error('公司信息未初始化'); } return { __type: 'Pointer', className: 'Company', objectId: this.company.id }; } /** * 清理重复的Product记录 * 对于同一个项目中相同名称的Product,只保留最早创建的,删除其他重复的 */ private async cleanupDuplicateProducts(): Promise { if (!this.company) { console.warn('公司信息未加载,跳过重复清理'); return; } try { console.log('🔍 开始检查重复的Product记录...'); // 查询所有Product const ProductQuery = new Parse.Query('Product'); ProductQuery.equalTo('company', this.getCompanyPointer()); ProductQuery.notEqualTo('isDeleted', true); ProductQuery.limit(1000); const allProducts = await ProductQuery.find(); console.log(`📦 找到 ${allProducts.length} 个Product记录`); // 按项目分组,然后按产品名称检测重复 const projectMap = new Map>(); for (const product of allProducts) { const projectId = product.get('project')?.id; const productName = (product.get('productName') || '').trim().toLowerCase(); if (!projectId || !productName) continue; if (!projectMap.has(projectId)) { projectMap.set(projectId, new Map()); } const productsByName = projectMap.get(projectId)!; if (!productsByName.has(productName)) { productsByName.set(productName, []); } productsByName.get(productName)!.push(product); } // 找出并删除重复的Product let duplicateCount = 0; const duplicatesToDelete: any[] = []; for (const [projectId, productsByName] of projectMap.entries()) { for (const [productName, products] of productsByName.entries()) { if (products.length > 1) { console.log(`⚠️ 项目 ${projectId} 中发现重复空间: "${productName}" (${products.length}个)`); // 按创建时间排序,保留最早的,删除其他的 products.sort((a, b) => { const timeA = a.get('createdAt')?.getTime() || 0; const timeB = b.get('createdAt')?.getTime() || 0; return timeA - timeB; }); // 保留第一个,删除其他的 for (let i = 1; i < products.length; i++) { duplicatesToDelete.push(products[i]); duplicateCount++; console.log(` 🗑️ 标记删除: ${products[i].get('productName')} (${products[i].id})`); } } } } // 批量删除重复的Product if (duplicatesToDelete.length > 0) { console.log(`🗑️ 准备删除 ${duplicatesToDelete.length} 个重复Product...`); for (const product of duplicatesToDelete) { try { product.set('isDeleted', true); product.set('data', { ...product.get('data'), deletedAt: new Date(), deletedReason: '重复产品,自动清理' }); await product.save(); console.log(` ✅ 已删除: ${product.get('productName')} (${product.id})`); } catch (error) { console.error(` ❌ 删除失败: ${product.id}`, error); } } console.log(`✅ 重复Product清理完成,共删除 ${duplicateCount} 个`); } else { console.log('✅ 未发现重复的Product记录'); } } catch (error) { console.error('❌ 清理重复Product失败:', error); // 不阻塞主流程 } } // 加载项目列表(从Parse Server) async loadProjects(): Promise { if (!this.company) { console.warn('公司信息未加载,跳过项目加载'); return; } this.isLoading.set(true); this.loadError.set(null); try { const ProjectQuery = new Parse.Query('Project'); ProjectQuery.equalTo('company', this.getCompanyPointer()); // 不强制要求isDeleted字段,兼容没有该字段的数据 ProjectQuery.notEqualTo('isDeleted', true); ProjectQuery.include('contact', 'assignee', 'owner'); ProjectQuery.descending('updatedAt'); ProjectQuery.limit(500); // 获取最多500个项目 const projectObjects = await ProjectQuery.find(); console.log(`✅ 从Parse Server加载了 ${projectObjects.length} 个项目`); // 🔍 调试:检查前5个项目的阶段数据 if (projectObjects.length > 0) { console.log('🔍 [客服端调试] 检查前5个项目的阶段数据:'); for (let i = 0; i < Math.min(5, projectObjects.length); i++) { const proj = projectObjects[i]; const title = proj.get('title') || '未命名项目'; const currentStage = proj.get('currentStage'); const stage = proj.get('stage'); const status = proj.get('status'); console.log(` 项目${i + 1}: "${title}"`); console.log(` - currentStage: ${currentStage}`); console.log(` - stage: ${stage}`); console.log(` - status: ${status}`); console.log(` - 最终使用阶段: ${currentStage || stage || '订单分配'}`); console.log(' ---'); } } // 如果没有数据,打印调试信息 if (projectObjects.length === 0) { console.warn('⚠️ 未找到项目数据,请检查:'); console.warn('1. Parse Server中是否有Project数据'); console.warn('2. 当前公司ID:', this.company.id); console.warn('3. 数据是否正确关联到当前公司'); } // 转换为Project接口格式(并从Product表同步最新阶段) const projects: Project[] = await Promise.all(projectObjects.map(async (obj: FmodeObject) => { const contact = obj.get('contact'); const assignee = obj.get('assignee'); // 🔄 直接从Project表读取阶段(与组长端保持严格一致) let rawStage = obj.get('currentStage') || obj.get('stage') || '订单分配'; console.log(`📊 项目 ${obj.get('title')} 原始阶段数据: currentStage=${obj.get('currentStage')}, stage=${obj.get('stage')}`); // 🔥 关键修复:统一交付执行子阶段的显示 let finalStage = rawStage; // 🎯 将交付执行的所有子阶段统一显示为"交付执行" const tempProject = { currentStage: rawStage, status: obj.get('status') }; if (this.isDeliveryExecutionStage(rawStage)) { finalStage = '交付执行'; console.log(`🔄 统一阶段显示: "${obj.get('title')}" 从 "${rawStage}" → "交付执行"`); } // 🔄 根据阶段自动判断状态(与组长端、管理端保持一致) const projectStatus = obj.get('status'); const autoStatus = getProjectStatusByStage(rawStage, projectStatus); console.log(`📊 客服项目 "${obj.get('title')}": 原始阶段=${rawStage}, 最终阶段=${finalStage}, 原状态=${projectStatus}, 自动状态=${autoStatus}`); // 确保updatedAt是Date对象 const updatedAt = obj.get('updatedAt'); const createdAt = obj.get('createdAt'); return { id: obj.id, name: obj.get('title') || '未命名项目', customerName: contact?.get('name') || '未知客户', customerId: contact?.id || '', status: autoStatus as ProjectStatus, // 使用根据阶段自动判断的状态 currentStage: finalStage as ProjectStage, stage: finalStage as ProjectStage, // stage和currentStage保持一致 assigneeId: assignee?.id || '', assigneeName: assignee?.get('name') || '未分配', deadline: obj.get('deadline') || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), createdAt: createdAt instanceof Date ? createdAt : (createdAt ? new Date(createdAt) : new Date()), updatedAt: updatedAt instanceof Date ? updatedAt : (updatedAt ? new Date(updatedAt) : new Date()), description: obj.get('description') || '', priority: obj.get('priority') || 'medium', customerTags: [], highPriorityNeeds: [], skillsRequired: [], contact: contact }; })); this.allProjects.set(projects); this.baseProjects = projects; this.processProjects(projects); // 🔍 验证项目分配统计 this.validateProjectDistribution(projects); console.log('项目数据处理完成'); } catch (error) { console.error('加载项目列表失败:', error); this.loadError.set('加载项目列表失败,请刷新页面重试'); this.projects.set([]); } finally { this.isLoading.set(false); } } // 映射Parse Server状态到前端状态 private mapStatus(parseStatus: string): ProjectStatus { const statusMap: Record = { '进行中': '进行中', '已完成': '已完成', '已暂停': '已暂停', '已延期': '已延期' }; return statusMap[parseStatus] || '进行中'; } // 映射Parse Server阶段到前端阶段 private mapStage(parseStage: string): ProjectStage { // 直接返回Parse Server的阶段,不做转换 // Parse Server的currentStage字段包含:订单分配、需求沟通、建模、软装、渲染、后期、尾款结算、投诉处理等 if (!parseStage) { return '需求沟通'; // 默认阶段 } return parseStage as ProjectStage; } // 处理项目数据,添加计算属性 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 'order' | 'requirements' | 'delivery' | 'aftercare'; 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 = { // 四大核心阶段 '订单分配': 0, '确认需求': 25, '交付执行': 60, '售后归档': 95, // 细分阶段(向后兼容) '需求沟通': 20, '方案确认': 30, '建模': 40, '软装': 50, '渲染': 70, '后期': 85, '尾款结算': 90, '客户评价': 100, '投诉处理': 100 }; 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)); } // 列表/筛选交互(保留已有实现) 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-completed'; case '订单分配': return 'stage-active'; case '方案确认': return 'stage-active'; case '尾款结算': return 'stage-completed'; case '客户评价': return 'stage-completed'; default: return ''; } } // 看板分组逻辑 - 按照订单分配、确认需求、交付执行、售后四个阶段 // 🔥 修复:直接使用原始阶段名称进行匹配(与组长端完全一致) private isOrderAssignment(p: Project): boolean { const stage = (p.currentStage as string)?.trim(); if (!stage) return false; const lowerStage = stage.toLowerCase(); return stage === '订单分配' || lowerStage === 'order' || stage === '待分配' || stage === '待审批'; } private isRequirementsConfirmation(p: Project): boolean { const stage = (p.currentStage as string)?.trim(); if (!stage) return false; const lowerStage = stage.toLowerCase(); return stage === '确认需求' || lowerStage === 'requirements' || stage === '需求沟通' || stage === '需求确认' || stage === '方案规划' || stage === '方案确认' || stage === '方案深化'; } private isDeliveryExecution(p: Project): boolean { const stage = (p.currentStage as string)?.trim(); if (!stage) return false; const lowerStage = stage.toLowerCase(); // 🔥 扩展交付执行阶段的识别范围,包含所有子阶段 return stage === '交付执行' || lowerStage === 'delivery' || // 建模相关 stage === '建模' || stage === '建模阶段' || stage === '白模' || stage === '白膜' || lowerStage === 'modeling' || // 软装相关 stage === '软装' || stage === '软装阶段' || lowerStage === 'soft_decor' || lowerStage === 'decoration' || // 渲染相关 stage === '渲染' || stage === '渲染阶段' || lowerStage === 'rendering' || // 后期相关 stage === '后期制作' || stage === '后期处理' || stage === '后期' || lowerStage === 'postproduction' || // 评审修改相关 stage === '评审' || stage === '方案评审' || stage === '修改' || stage === '方案修改' || stage === '修订' || lowerStage === 'review' || lowerStage === 'revision' || // 其他可能的交付执行子阶段 stage === '设计' || stage === '设计阶段' || stage === '制作' || stage === '制作阶段' || stage === '完善' || stage === '优化' || stage === '调整'; } private isAftercare(p: Project): boolean { const stage = (p.currentStage as string)?.trim(); if (!stage) return false; const lowerStage = stage.toLowerCase(); return stage === '售后归档' || lowerStage === 'aftercare' || stage === '售后' || stage === '归档' || stage === '尾款结算' || stage === '客户评价' || stage === '投诉处理' || stage === '已归档' || p.status === '已完成'; } getProjectsByColumn(columnId: 'order' | 'requirements' | 'delivery' | 'aftercare'): ProjectListItem[] { const list = this.projects(); let result: ProjectListItem[] = []; switch (columnId) { case 'order': result = list.filter(p => this.isOrderAssignment(p)); break; case 'requirements': result = list.filter(p => this.isRequirementsConfirmation(p)); break; case 'delivery': result = list.filter(p => this.isDeliveryExecution(p)); break; case 'aftercare': result = list.filter(p => this.isAftercare(p)); break; default: result = []; } // 🔍 调试日志 console.log(`🔍 [getProjectsByColumn] ${columnId}: ${result.length} 个项目`); if (result.length > 0) { result.forEach(p => { console.log(` - "${p.name}": currentStage="${p.currentStage}"`); }); } return result; } // 新增:根据项目状态与阶段推断所在看板列 getColumnIdForProject(project: ProjectListItem): 'order' | 'requirements' | 'delivery' | 'aftercare' { if (this.isOrderAssignment(project)) return 'order'; if (this.isRequirementsConfirmation(project)) return 'requirements'; if (this.isDeliveryExecution(project)) return 'delivery'; if (this.isAftercare(project)) return 'aftercare'; return 'requirements'; // 默认为确认需求阶段 } // 详情跳转到wxwork项目详情页面(与组长、管理员保持一致) navigateToProject(project: ProjectListItem) { // 获取公司ID const cid = localStorage.getItem('company') || ''; if (!cid) { console.error('未找到公司ID,无法跳转到项目详情页'); return; } // ✅ 根据项目实际阶段决定路由(不使用columnId) // wxwork路由支持的阶段:order, requirements, delivery, aftercare, issues const stageRouteMap: Record = { '订单分配': 'order', '确认需求': 'requirements', '方案确认': 'requirements', '方案深化': 'requirements', '交付执行': 'delivery', '建模': 'delivery', '软装': 'delivery', '渲染': 'delivery', '后期': 'delivery', '白模': 'delivery', '白膜': 'delivery', '售后归档': 'aftercare', '售后': 'aftercare', '尾款结算': 'aftercare', '客户评价': 'aftercare', '投诉处理': 'aftercare' }; const currentStage = project.currentStage || '订单分配'; const stagePath = stageRouteMap[currentStage] || 'order'; console.log(`🎯 [客服端跳转] 项目"${project.name}"`, { currentStage, stagePath, projectId: project.id }); // ✅ 标记从客服板块进入(用于控制"确认订单"按钮权限) try { localStorage.setItem('enterFromCustomerService', '1'); localStorage.setItem('customerServiceMode', 'true'); console.log('✅ 已标记从客服板块进入,允许确认订单'); } catch (e) { console.warn('无法设置 localStorage 标记:', e); } // 跳转到wxwork路由的项目详情页(纯净页面,无管理端侧边栏) // 路由格式:/wxwork/:cid/project/:projectId/:stage this.router.navigate(['/wxwork', cid, 'project', project.id, stagePath]); } // 新增:直接进入沟通管理(消息)标签 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(); // 准备同步数据 const syncData = { customerInfo: orderData.customerInfo, requirementInfo: orderData.requirementInfo, preferenceTags: orderData.preferenceTags, assignedDesigner: orderData.assignedDesigner }; // 跳转到新创建的项目详情页面,传递同步数据 this.router.navigate([ '/designer/project-detail', orderData.orderId ], { queryParams: { role: 'customer-service', activeTab: 'overview', syncData: JSON.stringify(syncData) } }); }); } }