import { Component, OnInit, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ActivatedRoute, Router } from '@angular/router'; import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ProjectService } from '../../../services/project.service'; import { Project, RenderProgress, ModelCheckItem, CustomerFeedback, DesignerChange, Settlement, ProjectStage } from '../../../models/project.model'; import { ConsultationOrderPanelComponent } from '../../../shared/components/consultation-order-panel/consultation-order-panel.component'; import { RequirementsConfirmCardComponent } from '../../../shared/components/requirements-confirm-card/requirements-confirm-card'; import { SettlementCardComponent } from '../../../shared/components/settlement-card/settlement-card'; import { CustomerReviewCardComponent } from '../../../shared/components/customer-review-card/customer-review-card'; import { ComplaintCardComponent } from '../../../shared/components/complaint-card/complaint-card'; import { VerticalNavComponent } from './components/vertical-nav/vertical-nav.component'; interface ExceptionHistory { id: string; type: 'failed' | 'stuck' | 'quality' | 'other'; description: string; submitTime: Date; status: '待处理' | '处理中' | '已解决'; response?: string; } interface ProjectMember { id: string; name: string; role: string; avatar: string; skillMatch: number; progress: number; contribution: number; } interface ProjectFile { id: string; name: string; type: string; size: string; date: string; url: string; } interface TimelineEvent { id: string; time: string; title: string; action: string; description: string; } // 新增:四大板块键类型(顶层声明,供组件与模板共同使用) type SectionKey = 'order' | 'requirements' | 'delivery' | 'aftercare'; @Component({ selector: 'app-project-detail', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule, ConsultationOrderPanelComponent, RequirementsConfirmCardComponent, SettlementCardComponent, CustomerReviewCardComponent, ComplaintCardComponent, VerticalNavComponent], templateUrl: './project-detail.html', styleUrls: ['./project-detail.scss', './debug-styles.scss'] }) export class ProjectDetail implements OnInit, OnDestroy { // 项目基本数据 projectId: string = ''; project: Project | undefined; renderProgress: RenderProgress | undefined; modelCheckItems: ModelCheckItem[] = []; feedbacks: CustomerFeedback[] = []; designerChanges: DesignerChange[] = []; settlements: Settlement[] = []; requirementChecklist: string[] = []; reminderMessage: string = ''; isLoadingRenderProgress: boolean = false; errorLoadingRenderProgress: boolean = false; feedbackTimeoutCountdown: number = 0; private countdownInterval: any; projects: {id: string, name: string, status: string}[] = []; showDropdown: boolean = false; currentStage: string = ''; // 新增:10阶段顺序(串式流程) stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '尾款结算', '客户评价', '投诉处理']; // 新增:阶段展开状态(默认全部收起,当前阶段在数据加载后自动展开) expandedStages: Partial> = { '订单创建': false, '需求沟通': false, '方案确认': false, '建模': false, '软装': false, '渲染': false, '后期': false, '尾款结算': false, '客户评价': false, '投诉处理': false, }; // 新增:四大板块定义与展开状态 sections: Array<{ key: SectionKey; label: string; stages: ProjectStage[] }> = [ { key: 'order', label: '订单创建', stages: ['订单创建'] }, { key: 'requirements', label: '确认需求', stages: ['需求沟通', '方案确认'] }, { key: 'delivery', label: '交付执行', stages: ['建模', '软装', '渲染'] }, { key: 'aftercare', label: '售后', stages: ['尾款结算', '客户评价', '投诉处理'] } ]; expandedSection: SectionKey | null = null; // 渲染异常反馈相关属性 exceptionType: 'failed' | 'stuck' | 'quality' | 'other' = 'failed'; exceptionDescription: string = ''; exceptionScreenshotUrl: string | null = null; exceptionHistories: ExceptionHistory[] = []; isSubmittingFeedback: boolean = false; selectedScreenshot: File | null = null; screenshotPreview: string | null = null; showExceptionForm: boolean = false; // 标签页相关 activeTab: 'progress' | 'members' | 'files' = 'progress'; tabs = [ { id: 'progress', name: '项目进度' }, { id: 'members', name: '项目成员' }, { id: 'files', name: '项目文件' } ]; // 标准化阶段(视图层映射) standardPhases: Array<'待分配' | '需求方案' | '项目执行' | '收尾验收' | '归档'> = ['待分配', '需求方案', '项目执行', '收尾验收', '归档']; // 文件上传(通用) acceptedFileTypes: string = '.doc,.docx,.pdf,.jpg,.jpeg,.png,.zip,.rar,.max,.obj'; isUploadingFile: boolean = false; projectMembers: ProjectMember[] = []; // 项目文件数据 projectFiles: ProjectFile[] = []; // 团队协作时间轴 timelineEvents: TimelineEvent[] = []; // ============ 阶段图片上传状态(新增) ============ allowedImageTypes: string = '.jpg,.jpeg,.png'; // 增加审核状态reviewStatus与是否已同步synced标记(仅由组长操作) whiteModelImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = []; softDecorImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = []; renderLargeImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = []; showRenderUploadModal: boolean = false; pendingRenderLargeItems: Array<{ id: string; name: string; url: string; file: File }> = []; // 视图上下文:根据路由前缀识别角色视角(客服/设计师/组长) private roleContext: 'customer-service' | 'designer' | 'team-leader' = 'designer'; constructor( private route: ActivatedRoute, private projectService: ProjectService, private router: Router, private fb: FormBuilder, ) {} // 切换标签页 switchTab(tabId: 'progress' | 'members' | 'files') { this.activeTab = tabId; } // 类型安全的标签页检查方法 isActiveTab(tabId: 'progress' | 'members' | 'files'): boolean { return this.activeTab === tabId; } // 根据事件类型获取作者名称 getEventAuthor(action: string): string { // 根据不同的action类型返回对应的作者名称 switch(action) { case '完成': case '更新': case '优化': return '李设计师'; case '收到': return '李客服'; case '提交': return '赵建模师'; default: return '王组长'; } } // 切换项目 switchProject(projectId: string): void { this.projectId = projectId; this.loadProjectData(); this.loadProjectMembers(); this.loadProjectFiles(); this.loadTimelineEvents(); // 更新URL但不触发组件重载 this.router.navigate([], { relativeTo: this.route, queryParamsHandling: 'merge', queryParams: { id: projectId } }); } // 返回工作台 backToWorkbench(): void { this.router.navigate(['/designer/dashboard']); } // 检查阶段是否已完成 isStageCompleted(stage: ProjectStage): boolean { if (!this.project) return false; // 定义阶段顺序 const stageOrder = [ '订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价', '投诉处理' ]; // 获取当前阶段和检查阶段的索引 const currentStageIndex = stageOrder.indexOf(this.project.currentStage); const checkStageIndex = stageOrder.indexOf(stage); // 如果检查阶段在当前阶段之前,则已完成 return checkStageIndex < currentStageIndex; } // 获取阶段状态:completed/active/pending getStageStatus(stage: ProjectStage): 'completed' | 'active' | 'pending' { const order = this.stageOrder; const current = this.project?.currentStage as ProjectStage | undefined; const currentIdx = current ? order.indexOf(current) : -1; const idx = order.indexOf(stage); if (idx === -1) return 'pending'; if (currentIdx === -1) return 'pending'; if (idx < currentIdx) return 'completed'; if (idx === currentIdx) return 'active'; return 'pending'; } // 切换阶段展开/收起,并保持单展开 toggleStage(stage: ProjectStage): void { // 已移除所有展开按钮,本方法保留以兼容模板其它引用,如无需可进一步删除调用点和方法 const exclusivelyOpen = true; if (exclusivelyOpen) { Object.keys(this.expandedStages).forEach((key) => (this.expandedStages[key as ProjectStage] = false)); this.expandedStages[stage] = true; } else { this.expandedStages[stage] = !this.expandedStages[stage]; } } // 查看阶段详情(已不再通过按钮触发,保留以兼容日志或未来调用) viewStageDetails(stage: ProjectStage): void { // 以往这里有 alert/导航行为,现清空用户交互,避免误触 return; } ngOnInit(): void { this.route.paramMap.subscribe(params => { this.projectId = params.get('id') || ''; // 根据当前URL检测视图上下文 this.roleContext = this.detectRoleContextFromUrl(); this.loadProjectData(); this.loadExceptionHistories(); this.loadProjectMembers(); this.loadProjectFiles(); this.loadTimelineEvents(); }); // 新增:监听查询参数,支持通过 activeTab 设置初始标签页 this.route.queryParamMap.subscribe(qp => { const raw = qp.get('activeTab'); const alias: Record = { requirements: 'progress', overview: 'progress' }; const tab = raw && (raw in alias ? alias[raw] : raw); if (tab === 'progress' || tab === 'members' || tab === 'files') { this.activeTab = tab; } }); // 添加点击事件监听器,当点击页面其他位置时关闭下拉菜单 document.addEventListener('click', this.closeDropdownOnClickOutside); // 初始化客户表单(与客服端保持一致) this.customerForm = this.fb.group({ name: ['', Validators.required], phone: ['', [Validators.required, Validators.pattern(/^1[3-9]\d{9}$/)]], wechat: [''], customerType: ['新客户'], source: [''], remark: [''], demandType: [''], followUpStatus: [''] }); // 自动生成下单时间 this.orderTime = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); } // 在组件销毁时移除事件监听器和清理资源 ngOnDestroy(): void { if (this.countdownInterval) { clearInterval(this.countdownInterval); } document.removeEventListener('click', this.closeDropdownOnClickOutside); // 释放所有 blob 预览 URL const revokeList: string[] = []; this.whiteModelImages.forEach(i => { if (i.url.startsWith('blob:')) revokeList.push(i.url); }); this.softDecorImages.forEach(i => { if (i.url.startsWith('blob:')) revokeList.push(i.url); }); this.renderLargeImages.forEach(i => { if (i.url.startsWith('blob:')) revokeList.push(i.url); }); this.pendingRenderLargeItems.forEach(i => { if (i.url.startsWith('blob:')) revokeList.push(i.url); }); revokeList.forEach(u => URL.revokeObjectURL(u)); } // ============ 角色视图与只读控制(新增) ============ private detectRoleContextFromUrl(): 'customer-service' | 'designer' | 'team-leader' { const url = this.router.url || ''; if (url.includes('/customer-service/')) return 'customer-service'; if (url.includes('/team-leader/')) return 'team-leader'; return 'designer'; } isDesignerView(): boolean { return this.roleContext === 'designer'; } isTeamLeaderView(): boolean { return this.roleContext === 'team-leader'; } isCustomerServiceView(): boolean { return this.roleContext === 'customer-service'; } // 只读规则:客服视角为只读 isReadOnly(): boolean { return this.isCustomerServiceView(); } // 计算当前激活板块:优先用户点击的 expandedSection;否则取当前阶段所属板块;再否则回退首个板块 private getActiveSectionKey(): SectionKey { if (this.expandedSection) return this.expandedSection; const current = this.project?.currentStage as ProjectStage | undefined; return current ? this.getSectionKeyForStage(current) : this.sections[0].key; } // 返回当前板块的全部阶段(所有角色一致): // 设计师也可查看 订单创建/确认需求/售后 板块内容 getVisibleStages(): ProjectStage[] { const activeKey = this.getActiveSectionKey(); const sec = this.sections.find(s => s.key === activeKey); return sec ? sec.stages : []; } // ============ 组长:同步上传与审核(新增,模拟实现) ============ syncUploadedImages(phase: 'white' | 'soft' | 'render'): void { if (!this.isTeamLeaderView()) return; const markSynced = (arr: Array<{ reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }>) => { arr.forEach(img => { if (!img.synced) img.synced = true; if (!img.reviewStatus) img.reviewStatus = 'pending'; }); }; if (phase === 'white') markSynced(this.whiteModelImages); if (phase === 'soft') markSynced(this.softDecorImages); if (phase === 'render') markSynced(this.renderLargeImages); alert('已同步该阶段的图片信息(模拟)'); } reviewImage(imageId: string, phase: 'white' | 'soft' | 'render', status: 'approved' | 'rejected'): void { if (!this.isTeamLeaderView()) return; const setStatus = (arr: Array<{ id: string; reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }>) => { const target = arr.find(i => i.id === imageId); if (target) { target.reviewStatus = status; if (!target.synced) target.synced = true; // 审核时自动视为已同步 } }; if (phase === 'white') setStatus(this.whiteModelImages); if (phase === 'soft') setStatus(this.softDecorImages); if (phase === 'render') setStatus(this.renderLargeImages); } getImageReviewStatusText(img: { reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }): string { const synced = img.synced ? '已同步' : '未同步'; const map: Record = { 'pending': '待审', 'approved': '已通过', 'rejected': '已驳回' }; const st = img.reviewStatus ? map[img.reviewStatus] : '未标记'; return `${st} · ${synced}`; } // 点击页面其他位置时关闭下拉菜单 private closeDropdownOnClickOutside = (event: MouseEvent): void => { const targetElement = event.target as HTMLElement; const projectSwitcher = targetElement.closest('.project-switcher'); if (!projectSwitcher && this.showDropdown) { this.showDropdown = false; } }; loadProjectData(): void { if (this.projectId) { this.loadProjectDetails(); this.loadRenderProgress(); this.loadModelCheckItems(); this.loadCustomerFeedbacks(); this.loadDesignerChanges(); this.loadSettlements(); this.loadRequirementChecklist(); } // 初始化项目列表数据(模拟) this.projects = [ { id: '1', name: '现代风格客厅设计', status: '进行中' }, { id: '2', name: '北欧风卧室装修', status: '已完成' }, { id: '3', name: '新中式书房改造', status: '进行中' }, { id: '4', name: '工业风餐厅设计', status: '待处理' } ]; } // 加载项目成员数据 loadProjectMembers(): void { // 模拟API请求获取项目成员数据 setTimeout(() => { this.projectMembers = [ { id: '1', name: '李设计师', role: '主设计师', avatar: '李', skillMatch: 95, progress: 65, contribution: 75 }, { id: '2', name: '陈设计师', role: '助理设计师', avatar: '陈', skillMatch: 88, progress: 80, contribution: 60 }, { id: '3', name: '王组长', role: '项目组长', avatar: '王', skillMatch: 92, progress: 70, contribution: 70 }, { id: '4', name: '赵建模师', role: '3D建模师', avatar: '赵', skillMatch: 90, progress: 90, contribution: 85 } ]; }, 600); } // 加载项目文件数据 loadProjectFiles(): void { // 模拟API请求获取项目文件数据 setTimeout(() => { this.projectFiles = [ { id: '1', name: '客厅设计方案V2.0.pdf', type: 'pdf', size: '2.5MB', date: '2024-02-10', url: '#' }, { id: '2', name: '材质库集合.rar', type: 'rar', size: '45.8MB', date: '2024-02-08', url: '#' }, { id: '3', name: '客厅渲染预览1.jpg', type: 'jpg', size: '3.2MB', date: '2024-02-14', url: '#' }, { id: '4', name: '3D模型文件.max', type: 'max', size: '87.5MB', date: '2024-02-12', url: '#' }, { id: '5', name: '客户需求文档.docx', type: 'docx', size: '1.2MB', date: '2024-01-15', url: '#' }, { id: '6', name: '客厅渲染预览2.jpg', type: 'jpg', size: '3.8MB', date: '2024-02-15', url: '#' } ]; }, 700); } // 加载团队协作时间轴数据 loadTimelineEvents(): void { // 模拟API请求获取时间轴数据 setTimeout(() => { this.timelineEvents = [ { id: '1', time: '2024-02-15 14:30', title: '渲染完成', action: '完成', description: '客厅主视角渲染已完成,等待客户确认' }, { id: '2', time: '2024-02-14 10:15', title: '材质调整', action: '更新', description: '根据客户反馈调整了沙发和窗帘材质' }, { id: '3', time: '2024-02-12 16:45', title: '模型优化', action: '优化', description: '优化了模型面数,提高渲染效率' }, { id: '4', time: '2024-02-10 09:30', title: '客户反馈', action: '收到', description: '收到客户关于颜色和储物空间的反馈意见' }, { id: '5', time: '2024-02-08 15:20', title: '模型提交', action: '提交', description: '完成3D模型搭建并提交审核' } ]; }, 800); } // 加载历史反馈记录 loadExceptionHistories(): void { this.projectService.getExceptionHistories(this.projectId).subscribe(histories => { this.exceptionHistories = histories; }); } loadProjectDetails(): void { this.projectService.getProjectById(this.projectId).subscribe(project => { this.project = project; // 设置当前阶段 if (project) { this.currentStage = project.currentStage || ''; // 重置展开状态并默认展开当前阶段 this.stageOrder.forEach(s => this.expandedStages[s] = false); if (this.stageOrder.includes(project.currentStage)) { this.expandedStages[project.currentStage] = true; } // 新增:根据当前阶段默认展开所属板块 const currentSec = this.getSectionKeyForStage(project.currentStage as ProjectStage); this.expandedSection = currentSec; } // 检查技能匹配度 this.checkSkillMismatch(); }); } // 整理项目详情 organizeProject(): void { // 模拟整理项目逻辑 alert('项目详情已整理'); } // 检查当前阶段是否显示特定卡片 shouldShowCard(cardType: string): boolean { // 改为始终显示:各阶段详情在看板下方就地展示,不再受当前阶段限制 return true; } loadRenderProgress(): void { this.isLoadingRenderProgress = true; this.errorLoadingRenderProgress = false; // 模拟API加载过程 setTimeout(() => { this.projectService.getRenderProgress(this.projectId).subscribe(progress => { this.renderProgress = progress; this.isLoadingRenderProgress = false; // 模拟API加载失败的情况 if (!progress) { this.errorLoadingRenderProgress = true; // 通知技术组长 this.notifyTeamLeader('render-failed'); } else { // 检查是否需要显示超时预警 this.checkRenderTimeout(); } }); }, 1000); } loadModelCheckItems(): void { this.projectService.getModelCheckItems().subscribe(items => { this.modelCheckItems = items; }); } loadCustomerFeedbacks(): void { this.projectService.getCustomerFeedbacks().subscribe(feedbacks => { this.feedbacks = feedbacks.filter(f => f.projectId === this.projectId); // 为反馈添加分类标签 this.tagCustomerFeedbacks(); // 检查是否有需要处理的反馈并启动倒计时 this.checkFeedbackTimeout(); }); } loadDesignerChanges(): void { // 在实际应用中,这里应该从服务中获取设计师变更记录 // 这里使用模拟数据 this.designerChanges = [ { id: 'dc1', projectId: this.projectId, oldDesignerId: 'designer2', oldDesignerName: '设计师B', newDesignerId: 'designer1', newDesignerName: '设计师A', changeTime: new Date('2025-09-05'), acceptanceTime: new Date('2025-09-05'), historicalAchievements: ['完成初步建模', '确定色彩方案'], completedWorkload: 30 } ]; } loadSettlements(): void { this.projectService.getSettlements().subscribe(settlements => { this.settlements = settlements.filter(s => s.projectId === this.projectId); }); } loadRequirementChecklist(): void { this.projectService.generateRequirementChecklist(this.projectId).subscribe(checklist => { this.requirementChecklist = checklist; }); } updateModelCheckItem(itemId: string, isPassed: boolean): void { this.projectService.updateModelCheckItem(itemId, isPassed).subscribe(() => { this.loadModelCheckItems(); // 重新加载检查项 }); } updateFeedbackStatus(feedbackId: string, status: '处理中' | '已解决'): void { this.projectService.updateFeedbackStatus(feedbackId, status).subscribe(() => { this.loadCustomerFeedbacks(); // 重新加载反馈 // 清除倒计时 if (this.countdownInterval) { clearInterval(this.countdownInterval); this.feedbackTimeoutCountdown = 0; } }); } updateProjectStage(stage: ProjectStage): void { if (this.project) { this.projectService.updateProjectStage(this.projectId, stage).subscribe(() => { this.loadProjectDetails(); // 重新加载项目详情 }); } } // 新增:根据给定阶段跳转到下一阶段 advanceToNextStage(afterStage: ProjectStage): void { const idx = this.stageOrder.indexOf(afterStage); if (idx >= 0 && idx < this.stageOrder.length - 1) { const next = this.stageOrder[idx + 1]; this.updateProjectStage(next); // 可选:更新展开状态,折叠当前、展开下一阶段,提升体验 if (this.expandedStages[afterStage] !== undefined) this.expandedStages[afterStage] = false as any; if (this.expandedStages[next] !== undefined) this.expandedStages[next] = true as any; } } generateReminderMessage(): void { this.projectService.generateReminderMessage('stagnation').subscribe(message => { this.reminderMessage = message; // 3秒后自动清除提醒 setTimeout(() => { this.reminderMessage = ''; }, 3000); }); } // ============ 新增:标准化阶段映射与紧急程度 ============ // 计算距离截止日期的天数(向下取整) getDaysToDeadline(): number | null { if (!this.project?.deadline) return null; const now = new Date(); const deadline = new Date(this.project.deadline); const diffMs = deadline.getTime() - now.getTime(); return Math.floor(diffMs / (1000 * 60 * 60 * 24)); } // 是否延期/临期/提示 getUrgencyBadge(): 'overdue' | 'due_3' | 'due_7' | null { const d = this.getDaysToDeadline(); if (d === null) return null; if (d < 0) return 'overdue'; if (d <= 3) return 'due_3'; if (d <= 7) return 'due_7'; return null; } // 是否存在不满意或待处理投诉/反馈 hasPendingComplaint(): boolean { return this.feedbacks.some(f => !f.isSatisfied || f.status === '待处理'); } // 将现有细分阶段映射为标准化阶段 mapToStandardPhase(stage: ProjectStage): '待分配' | '需求方案' | '项目执行' | '收尾验收' | '归档' { const mapping: Record = { '订单创建': '待分配', '需求沟通': '需求方案', '方案确认': '需求方案', '建模': '项目执行', '软装': '项目执行', '渲染': '项目执行', '后期': '项目执行', '尾款结算': '收尾验收', '客户评价': '收尾验收', '投诉处理': '收尾验收' }; return mapping[stage] ?? '待分配'; } getStandardPhaseIndex(): number { if (!this.project?.currentStage) return 0; const phase = this.mapToStandardPhase(this.project.currentStage); return this.standardPhases.indexOf(phase); } isStandardPhaseCompleted(phase: '待分配' | '需求方案' | '项目执行' | '收尾验收' | '归档'): boolean { return this.standardPhases.indexOf(phase) < this.getStandardPhaseIndex(); } isStandardPhaseCurrent(phase: '待分配' | '需求方案' | '项目执行' | '收尾验收' | '归档'): boolean { return this.standardPhases.indexOf(phase) === this.getStandardPhaseIndex(); } // ============ 新增:项目报告导出 ============ exportProjectReport(): void { if (!this.project) return; const lines: string[] = []; const d = this.getDaysToDeadline(); lines.push(`项目名称: ${this.project.name}`); lines.push(`当前阶段(细分): ${this.project.currentStage}`); lines.push(`当前阶段(标准化): ${this.mapToStandardPhase(this.project.currentStage)}`); if (this.project.deadline) { lines.push(`截止日期: ${this.formatDate(this.project.deadline)}`); lines.push(`剩余天数: ${d !== null ? d : '-'}天`); } lines.push(`技能需求: ${(this.project.skillsRequired || []).join('、')}`); lines.push('—— 渲染进度 ——'); lines.push(this.renderProgress ? `状态: ${this.renderProgress.status}, 完成度: ${this.renderProgress.completionRate}%` : '无渲染进度数据'); lines.push('—— 客户反馈 ——'); lines.push(this.feedbacks.length ? `${this.feedbacks.length} 条` : '暂无'); lines.push('—— 设计师变更 ——'); lines.push(this.designerChanges.length ? `${this.designerChanges.length} 条` : '暂无'); lines.push('—— 交付文件 ——'); lines.push(this.projectFiles.length ? this.projectFiles.map(f => `• ${f.name} (${f.type}, ${f.size})`).join('\n') : '暂无'); const content = lines.join('\n'); const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${this.project.name || '项目'}-阶段报告.txt`; a.click(); URL.revokeObjectURL(url); } // ============ 新增:通用文件上传(含4K图片校验) ============ async onGeneralFilesSelected(event: Event): Promise { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files); this.isUploadingFile = true; for (const file of files) { // 对图片进行4K校验(最大边 >= 4000px) if (/\.(jpg|jpeg|png)$/i.test(file.name)) { const ok = await this.validateImage4K(file).catch(() => false); if (!ok) { alert(`图片不符合4K标准(最大边需≥4000像素):${file.name}`); continue; } } // 简化:直接追加到本地列表(实际应上传到服务器) const fakeType = (file.name.split('.').pop() || '').toLowerCase(); const sizeMB = (file.size / (1024 * 1024)).toFixed(1) + 'MB'; const nowStr = this.formatDate(new Date()); this.projectFiles.unshift({ id: `file-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, name: file.name, type: fakeType, size: sizeMB, date: nowStr, url: '#' }); } this.isUploadingFile = false; // 清空选择 input.value = ''; } validateImage4K(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const img = new Image(); img.onload = () => { const maxSide = Math.max(img.width, img.height); resolve(maxSide >= 4000); }; img.onerror = () => reject('image load error'); img.src = reader.result as string; }; reader.onerror = () => reject('read error'); reader.readAsDataURL(file); }); } // 可选:列表 trackBy,优化渲染 trackById(_: number, item: { id: string }): string { return item.id; } retryLoadRenderProgress(): void { this.loadRenderProgress(); } // 检查是否所有模型检查项都已通过 areAllModelChecksPassed(): boolean { return this.modelCheckItems.every(item => item.isPassed); } // 获取技能匹配度警告 getSkillMismatchWarning(): string | null { if (!this.project) return null; // 模拟技能匹配度检查 const designerSkills = ['现代风格', '硬装']; const requiredSkills = this.project.skillsRequired; const mismatchedSkills = requiredSkills.filter(skill => !designerSkills.includes(skill)); if (mismatchedSkills.length > 0) { return `警告:您不擅长${mismatchedSkills.join('、')},建议联系组长协调`; } return null; } // 检查渲染是否超时 checkRenderTimeout(): void { if (!this.renderProgress || !this.project) return; // 模拟交付前3小时预警 const deliveryTime = new Date(this.project.deadline); const currentTime = new Date(); const timeDifference = deliveryTime.getTime() - currentTime.getTime(); const hoursRemaining = Math.floor(timeDifference / (1000 * 60 * 60)); if (hoursRemaining <= 3 && hoursRemaining > 0) { // 弹窗预警 alert('渲染进度预警:交付前3小时,请关注渲染进度'); } if (hoursRemaining <= 1 && hoursRemaining > 0) { // 更严重的预警 alert('渲染进度严重预警:交付前1小时,渲染可能无法按时完成!'); } } // 为客户反馈添加分类标签 tagCustomerFeedbacks(): void { this.feedbacks.forEach(feedback => { // 添加分类标签 if (feedback.content.includes('色彩') || feedback.problemLocation?.includes('色彩')) { (feedback as any).tag = '色彩问题'; } else if (feedback.content.includes('家具') || feedback.problemLocation?.includes('家具')) { (feedback as any).tag = '家具款式问题'; } else if (feedback.content.includes('光线') || feedback.content.includes('照明')) { (feedback as any).tag = '光线问题'; } else { (feedback as any).tag = '其他问题'; } }); } // 获取反馈标签的辅助方法 getFeedbackTag(feedback: CustomerFeedback): string { return (feedback as any).tag || ''; } // 检查反馈超时 checkFeedbackTimeout(): void { const pendingFeedbacks = this.feedbacks.filter(f => f.status === '待处理'); if (pendingFeedbacks.length > 0) { // 启动1小时倒计时 this.feedbackTimeoutCountdown = 3600; // 3600秒 = 1小时 this.startCountdown(); } } // 启动倒计时 startCountdown(): void { this.countdownInterval = setInterval(() => { if (this.feedbackTimeoutCountdown > 0) { this.feedbackTimeoutCountdown--; } else { clearInterval(this.countdownInterval); // 超时提醒 alert('客户反馈已超过1小时未响应,请立即处理!'); this.notifyTeamLeader('feedback-overdue'); } }, 1000); } // 通知技术组长 notifyTeamLeader(type: 'render-failed' | 'feedback-overdue' | 'skill-mismatch'): void { // 实际应用中应调用消息服务通知组长 console.log(`通知技术组长:${type} - 项目ID: ${this.projectId}`); } // 检查技能匹配度并提示 checkSkillMismatch(): void { const warning = this.getSkillMismatchWarning(); if (warning) { // 显示技能不匹配警告 if (confirm(`${warning}\n是否联系技术组长协调支持?`)) { this.notifyTeamLeader('skill-mismatch'); } } } // 发起设计师变更 initiateDesignerChange(reason: string): void { // 实际应用中应调用API发起变更 console.log(`发起设计师变更,原因:${reason}`); alert('已发起设计师变更申请,请等待新设计师承接'); } // 确认承接变更项目 acceptDesignerChange(changeId: string): void { // 实际应用中应调用API确认承接 console.log(`确认承接设计师变更:${changeId}`); alert('已确认承接项目,系统已记录时间戳和责任人'); } // 格式化倒计时显示 formatCountdown(seconds: number): string { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const remainingSeconds = seconds % 60; return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; } // 提交异常反馈 submitExceptionFeedback(): void { if (!this.exceptionDescription.trim() || this.isSubmittingFeedback) { alert('请填写异常类型和描述'); return; } this.isSubmittingFeedback = true; // 模拟提交反馈到服务器 setTimeout(() => { const newException: ExceptionHistory = { id: `exception-${Date.now()}`, type: this.exceptionType, description: this.exceptionDescription, submitTime: new Date(), status: '待处理' }; // 添加到历史记录中 this.exceptionHistories.unshift(newException); // 通知客服和技术支持 this.notifyTechnicalSupport(newException); // 清空表单 this.exceptionDescription = ''; this.clearExceptionScreenshot(); this.showExceptionForm = false; // 显示成功消息 alert('异常反馈已提交,技术支持将尽快处理'); this.isSubmittingFeedback = false; }, 1000); } // 上传异常截图 uploadExceptionScreenshot(event: Event): void { const input = event.target as HTMLInputElement; if (input.files && input.files[0]) { const file = input.files[0]; // 在实际应用中,这里应该上传文件到服务器 // 这里我们使用FileReader来生成一个预览URL const reader = new FileReader(); reader.onload = (e) => { this.exceptionScreenshotUrl = e.target?.result as string; }; reader.readAsDataURL(file); } } // 清除异常截图 clearExceptionScreenshot(): void { this.exceptionScreenshotUrl = null; const input = document.getElementById('screenshot-upload') as HTMLInputElement; if (input) { input.value = ''; } } // 联系组长 contactTeamLeader() { alert(`已联系${this.project?.assigneeName || '项目组长'}`); } // 处理渲染超时预警 handleRenderTimeout() { alert('已发送渲染超时预警通知'); } // 通知技术支持 notifyTechnicalSupport(exception: ExceptionHistory): void { // 实际应用中应调用消息服务通知技术支持和客服 console.log(`通知技术支持和客服:渲染异常 - 项目ID: ${this.projectId}`); console.log(`异常类型: ${this.getExceptionTypeText(exception.type)}, 描述: ${exception.description}`); } // 获取异常类型文本 getExceptionTypeText(type: string): string { const typeMap: Record = { 'failed': '渲染失败', 'stuck': '渲染卡顿', 'quality': '渲染质量问题', 'other': '其他问题' }; return typeMap[type] || type; } // 格式化日期 formatDate(date: Date | string): string { const d = typeof date === 'string' ? new Date(date) : date; const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const hours = String(d.getHours()).padStart(2, '0'); const minutes = String(d.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}`; } // 将字节格式化为易读尺寸 private formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes}B`; const kb = bytes / 1024; if (kb < 1024) return `${kb.toFixed(1)}KB`; const mb = kb / 1024; if (mb < 1024) return `${mb.toFixed(1)}MB`; const gb = mb / 1024; return `${gb.toFixed(2)}GB`; } // 生成缩略图条目(并创建本地预览URL) private makeImageItem(file: File): { id: string; name: string; url: string; size: string } { const id = `img-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const url = URL.createObjectURL(file); return { id, name: file.name, url, size: this.formatFileSize(file.size) }; } // 释放对象URL private revokeUrl(url: string): void { try { if (url && url.startsWith('blob:')) URL.revokeObjectURL(url); } catch {} } // =========== 建模阶段:白模上传 =========== onWhiteModelSelected(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files).filter(f => /\.(jpg|jpeg|png)$/i.test(f.name)); const items = files.map(f => this.makeImageItem(f)); this.whiteModelImages.unshift(...items); input.value = ''; } removeWhiteModelImage(id: string): void { const target = this.whiteModelImages.find(i => i.id === id); if (target) this.revokeUrl(target.url); this.whiteModelImages = this.whiteModelImages.filter(i => i.id !== id); } // 新增:建模阶段 确认上传并自动进入下一阶段(软装) confirmWhiteModelUpload(): void { if (this.whiteModelImages.length === 0) return; this.advanceToNextStage('建模'); } // =========== 软装阶段:小图上传(建议≤1MB,不强制) =========== onSoftDecorSmallPicsSelected(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files).filter(f => /\.(jpg|jpeg|png)$/i.test(f.name)); const warnOversize = files.filter(f => f.size > 1024 * 1024); if (warnOversize.length > 0) { // 仅提示,不阻断 console.warn('软装小图建议≤1MB,以下文件较大:', warnOversize.map(f => f.name)); } const items = files.map(f => this.makeImageItem(f)); this.softDecorImages.unshift(...items); input.value = ''; } // 拖拽上传相关属性 isDragOver: boolean = false; // 图片预览相关属性 showImagePreview: boolean = false; previewImageData: any = null; // 图片预览方法 previewImage(img: any): void { this.previewImageData = img; this.showImagePreview = true; } closeImagePreview(): void { this.showImagePreview = false; this.previewImageData = null; } downloadImage(img: any): void { if (img) { const link = document.createElement('a'); link.href = img.url; link.download = img.name; link.click(); } } removeImageFromPreview(): void { if (this.previewImageData) { // 根据图片类型调用相应的删除方法 if (this.whiteModelImages.find(i => i.id === this.previewImageData.id)) { this.removeWhiteModelImage(this.previewImageData.id); } else if (this.softDecorImages.find(i => i.id === this.previewImageData.id)) { this.removeSoftDecorImage(this.previewImageData.id); } else if (this.renderLargeImages.find(i => i.id === this.previewImageData.id)) { this.removeRenderLargeImage(this.previewImageData.id); } this.closeImagePreview(); } } // 拖拽事件处理 onDragOver(event: DragEvent): void { event.preventDefault(); event.stopPropagation(); this.isDragOver = true; } onDragLeave(event: DragEvent): void { event.preventDefault(); event.stopPropagation(); this.isDragOver = false; } onFileDrop(event: DragEvent, type: 'whiteModel' | 'softDecor' | 'render'): void { event.preventDefault(); event.stopPropagation(); this.isDragOver = false; const files = event.dataTransfer?.files; if (!files || files.length === 0) return; // 创建模拟的input事件 const mockEvent = { target: { files: files } } as any; // 根据类型调用相应的处理方法 switch (type) { case 'whiteModel': this.onWhiteModelSelected(mockEvent); break; case 'softDecor': this.onSoftDecorSmallPicsSelected(mockEvent); break; case 'render': this.onRenderLargePicsSelected(mockEvent); break; } } // 触发文件输入框 triggerFileInput(type: 'whiteModel' | 'softDecor' | 'render'): void { let inputId: string; switch (type) { case 'whiteModel': inputId = 'whiteModelFileInput'; break; case 'softDecor': inputId = 'softDecorFileInput'; break; case 'render': inputId = 'renderFileInput'; break; } const input = document.querySelector(`#${inputId}`) as HTMLInputElement; if (input) { input.click(); } } removeSoftDecorImage(id: string): void { const target = this.softDecorImages.find(i => i.id === id); if (target) this.revokeUrl(target.url); this.softDecorImages = this.softDecorImages.filter(i => i.id !== id); } // 新增:软装阶段 确认上传并自动进入下一阶段(渲染) confirmSoftDecorUpload(): void { if (this.softDecorImages.length === 0) return; this.advanceToNextStage('软装'); } // =========== 渲染阶段:大图上传(弹窗 + 4K校验) =========== openRenderUploadModal(): void { this.showRenderUploadModal = true; this.pendingRenderLargeItems = []; } closeRenderUploadModal(): void { // 关闭时释放临时预览URL this.pendingRenderLargeItems.forEach(i => this.revokeUrl(i.url)); this.pendingRenderLargeItems = []; this.showRenderUploadModal = false; } async onRenderLargePicsSelected(event: Event): Promise { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files).filter(f => /\.(jpg|jpeg|png)$/i.test(f.name)); for (const f of files) { const ok = await this.validateImage4K(f).catch(() => false); if (!ok) { alert(`图片不符合4K标准(最大边需≥4000像素):${f.name}`); continue; } const item = this.makeImageItem(f); // 直接添加到正式列表,不再使用待确认列表 this.renderLargeImages.unshift({ id: item.id, name: item.name, url: item.url, size: this.formatFileSize(f.size) }); } input.value = ''; } confirmRenderUpload(): void { // 将待确认的图片加入正式列表(此处模拟上传成功) const toAdd = this.pendingRenderLargeItems.map(i => ({ id: i.id, name: i.name, url: i.url, size: this.formatFileSize(i.file.size) })); this.renderLargeImages.unshift(...toAdd); this.closeRenderUploadModal(); // 新增:渲染阶段确认后,自动进入下一阶段(后期) this.advanceToNextStage('渲染'); } removeRenderLargeImage(id: string): void { const target = this.renderLargeImages.find(i => i.id === id); if (target) this.revokeUrl(target.url); this.renderLargeImages = this.renderLargeImages.filter(i => i.id !== id); } // 根据阶段映射所属板块 getSectionKeyForStage(stage: ProjectStage): SectionKey { switch (stage) { case '订单创建': return 'order'; case '需求沟通': case '方案确认': return 'requirements'; case '建模': case '软装': case '渲染': return 'delivery'; case '尾款结算': case '客户评价': case '投诉处理': return 'aftercare'; default: return 'order'; } } // 获取板块状态:completed | 'active' | 'pending' getSectionStatus(key: SectionKey): 'completed' | 'active' | 'pending' { const current = this.project?.currentStage as ProjectStage | undefined; if (!current) return 'pending'; const currentSection = this.getSectionKeyForStage(current); const sectionOrder = this.sections.map(s => s.key); const currentIdx = sectionOrder.indexOf(currentSection); const idx = sectionOrder.indexOf(key); if (idx === -1 || currentIdx === -1) return 'pending'; if (idx < currentIdx) return 'completed'; if (idx === currentIdx) return 'active'; return 'pending'; } // 切换四大板块(单展开) toggleSection(key: SectionKey): void { this.expandedSection = key; // 点击板块按钮时,滚动到该板块的第一个可见阶段卡片 const sec = this.sections.find(s => s.key === key); if (sec) { // 设计师仅滚动到可见的三大执行阶段,否则取该板块第一个阶段 const candidate = this.isDesignerView() ? sec.stages.find(st => ['建模', '软装', '渲染'].includes(st)) || sec.stages[0] : sec.stages[0]; this.scrollToStage(candidate); } } // 阶段到锚点的映射 stageToAnchor(stage: ProjectStage): string { const map: Record = { '订单创建': 'order', '需求沟通': 'requirements-talk', '方案确认': 'proposal-confirm', '建模': 'modeling', '软装': 'softdecor', '渲染': 'render', '后期': 'aftercare', '尾款结算': 'settlement', '客户评价': 'customer-review', '投诉处理': 'complaint' }; return `stage-${map[stage] || 'unknown'}`; } // 平滑滚动到指定阶段卡片 scrollToStage(stage: ProjectStage): void { const anchor = this.stageToAnchor(stage); const el = document.getElementById(anchor); if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } // 订单创建阶段:客户信息(迁移自客服端"客户信息"卡片) orderCreationMethod: 'miniprogram' | 'manual' = 'miniprogram'; isSyncing: boolean = false; orderTime: string = ''; customerForm!: FormGroup; customerSearchKeyword: string = ''; customerSearchResults: Array<{ id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }> = []; selectedOrderCustomer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string } | null = null; demandTypes = [ { value: 'price', label: '价格敏感' }, { value: 'quality', label: '质量敏感' }, { value: 'comprehensive', label: '综合要求' } ]; followUpStatus = [ { value: 'quotation', label: '待报价' }, { value: 'confirm', label: '待确认需求' }, { value: 'lost', label: '已失联' } ]; // 需求关键信息同步数据 requirementKeyInfo = { colorAtmosphere: { description: '', mainColor: '', colorTemp: '', materials: [] as string[] }, spaceStructure: { lineRatio: 0, blankRatio: 0, flowWidth: 0, aspectRatio: 0, ceilingHeight: 0 }, materialWeights: { fabricRatio: 0, woodRatio: 0, metalRatio: 0, smoothness: 0, glossiness: 0 }, presetAtmosphere: { name: '', rgb: '', colorTemp: '', materials: [] as string[] } }; // 客户信息:搜索/选择/清空/同步/快速填写 逻辑 searchCustomer(): void { if (this.customerSearchKeyword.trim().length >= 2) { this.customerSearchResults = [ { id: '1', name: '张先生', phone: '138****5678', customerType: '老客户', source: '官网咨询', avatar: "data:image/svg+xml,%3Csvg width='64' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23E6E6E6'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" }, { id: '2', name: '李女士', phone: '139****1234', customerType: 'VIP客户', source: '推荐介绍', avatar: "data:image/svg+xml,%3Csvg width='65' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23DCDCDC'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" } ]; } else { this.customerSearchResults = []; } } selectCustomer(customer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }): void { this.selectedOrderCustomer = customer; this.customerForm.patchValue({ name: customer.name, phone: customer.phone, wechat: customer.wechat || '', customerType: customer.customerType || '新客户', source: customer.source || '', remark: customer.remark || '' }); this.customerSearchResults = []; this.customerSearchKeyword = ''; } clearSelectedCustomer(): void { this.selectedOrderCustomer = null; this.customerForm.reset({ customerType: '新客户' }); } quickFillCustomerInfo(keyword: string): void { const k = (keyword || '').trim(); if (!k) return; // 模拟:若有搜索结果,选择第一条 if (this.customerSearchResults.length === 0) this.searchCustomer(); if (this.customerSearchResults.length > 0) { this.selectCustomer(this.customerSearchResults[0]); } } syncMiniprogramCustomerInfo(): void { if (this.isSyncing) return; this.isSyncing = true; setTimeout(() => { // 模拟从小程序同步到客户表单 this.customerForm.patchValue({ name: '小程序用户', phone: '13800001234', wechat: 'wx_user_001', customerType: '新客户', source: '小程序下单' }); this.isSyncing = false; }, 1000); } downloadFile(file: ProjectFile): void { // 实现文件下载逻辑 const link = document.createElement('a'); link.href = file.url; link.download = file.name; link.click(); } previewFile(file: ProjectFile): void { // 预览文件逻辑 console.log('预览文件:', file.name); } // 同步需求关键信息到客户信息卡片 syncRequirementKeyInfo(requirementData: any): void { if (requirementData) { // 同步色彩氛围信息 if (requirementData.colorIndicators) { this.requirementKeyInfo.colorAtmosphere = { description: requirementData.colorIndicators.colorRange || '', mainColor: `rgb(${requirementData.colorIndicators.mainColor?.r || 0}, ${requirementData.colorIndicators.mainColor?.g || 0}, ${requirementData.colorIndicators.mainColor?.b || 0})`, colorTemp: `${requirementData.colorIndicators.colorTemperature || 0}K`, materials: [] }; } // 同步空间结构信息 if (requirementData.spaceIndicators) { this.requirementKeyInfo.spaceStructure = { lineRatio: requirementData.spaceIndicators.lineRatio || 0, blankRatio: requirementData.spaceIndicators.blankRatio || 0, flowWidth: requirementData.spaceIndicators.flowWidth || 0, aspectRatio: requirementData.spaceIndicators.aspectRatio || 0, ceilingHeight: requirementData.spaceIndicators.ceilingHeight || 0 }; } // 同步材质权重信息 if (requirementData.materialIndicators) { this.requirementKeyInfo.materialWeights = { fabricRatio: requirementData.materialIndicators.fabricRatio || 0, woodRatio: requirementData.materialIndicators.woodRatio || 0, metalRatio: requirementData.materialIndicators.metalRatio || 0, smoothness: requirementData.materialIndicators.smoothness || 0, glossiness: requirementData.materialIndicators.glossiness || 0 }; } // 同步预设氛围信息 if (requirementData.selectedPresetAtmosphere) { this.requirementKeyInfo.presetAtmosphere = { name: requirementData.selectedPresetAtmosphere.name || '', rgb: requirementData.selectedPresetAtmosphere.rgb || '', colorTemp: requirementData.selectedPresetAtmosphere.colorTemp || '', materials: requirementData.selectedPresetAtmosphere.materials || [] }; } console.log('需求关键信息已同步:', this.requirementKeyInfo); } else { // 模拟数据用于演示 this.requirementKeyInfo = { colorAtmosphere: { description: '温馨暖调', mainColor: 'rgb(255, 230, 180)', colorTemp: '2700K', materials: ['木质', '布艺'] }, spaceStructure: { lineRatio: 60, blankRatio: 30, flowWidth: 0.9, aspectRatio: 1.6, ceilingHeight: 2.8 }, materialWeights: { fabricRatio: 50, woodRatio: 30, metalRatio: 20, smoothness: 7, glossiness: 4 }, presetAtmosphere: { name: '现代简约', rgb: '200,220,240', colorTemp: '5000K', materials: ['金属', '玻璃'] } }; } } // 获取同步的关键信息摘要 getRequirementSummary(): string[] { const summary: string[] = []; if (this.requirementKeyInfo.colorAtmosphere.description) { summary.push(`色彩氛围: ${this.requirementKeyInfo.colorAtmosphere.description}`); } if (this.requirementKeyInfo.spaceStructure.aspectRatio > 0) { summary.push(`空间比例: ${this.requirementKeyInfo.spaceStructure.aspectRatio.toFixed(1)}`); } if (this.requirementKeyInfo.materialWeights.woodRatio > 0) { summary.push(`木质占比: ${this.requirementKeyInfo.materialWeights.woodRatio}%`); } if (this.requirementKeyInfo.presetAtmosphere.name) { summary.push(`预设氛围: ${this.requirementKeyInfo.presetAtmosphere.name}`); } return summary; } // 处理咨询订单表单提交 onConsultationOrderSubmit(formData: any): void { console.log('咨询订单表单提交:', formData); // 这里可以添加处理表单提交的逻辑 // 例如:保存订单信息、更新项目状态等 } }