import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { UploadSuccessModalComponent } from '../upload-success-modal/upload-success-modal.component'; import { GlobalPromptComponent } from '../global-prompt/global-prompt.component'; import { ColorAnalysisService, ColorAnalysisResult } from '../../services/color-analysis.service'; import { FullReportOverlayComponent } from '../full-report-overlay/full-report-overlay.component'; import { CadAnalysisService, CadAnalysisResult } from '../../services/cad-analysis.service'; // 素材文件接口 interface MaterialFile { id: string; name: string; type: 'text' | 'image' | 'cad'; url?: string; content?: string; size?: string; uploadTime: Date; analysis?: MaterialAnalysis; } // 素材解析结果接口 interface MaterialAnalysis { // 文本类解析 atmosphere?: { description: string; rgb?: string; colorTemp?: string }; residents?: { type: string; details: string }; scenes?: { preference: string; requirements: string }; keywords?: { positive: string[]; negative: string[] }; // 图片类解析 mainColors?: { rgb: string; percentage: number }[]; colorTemperature?: string; materialRatio?: { material: string; percentage: number }[]; spaceRatio?: number; // CAD类解析 structuralElements?: { type: string; position: string; changeable: boolean }[]; spaceMetrics?: { room: string; ratio: string; width: string }[]; flowMetrics?: { area: string; width: string; compliance: boolean }[]; } // 需求指标接口 interface RequirementMetric { id: string; category: 'color' | 'space' | 'material'; name: string; value: any; unit?: string; range?: { min: number; max: number }; description?: string; } // 协作评论接口 interface CollaborationComment { id: string; author: string; role: 'customer-service' | 'designer' | 'client'; content: string; timestamp: Date; requirementId?: string; status: 'pending' | 'resolved'; } // 需求项接口 interface RequirementItem { id: string; title: string; description: string; priority: 'high' | 'medium' | 'low'; status: 'pending' | 'confirmed' | 'rejected'; tags?: string[]; metrics?: RequirementMetric[]; comments?: CollaborationComment[]; lastUpdated: Date; showComments?: boolean; } @Component({ selector: 'app-requirements-confirm-card', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule, UploadSuccessModalComponent, GlobalPromptComponent, FullReportOverlayComponent], providers: [CadAnalysisService], templateUrl: './requirements-confirm-card.html', styleUrls: ['./requirements-confirm-card.scss'], changeDetection: ChangeDetectionStrategy.Default // 确保使用默认变更检测策略 }) export class RequirementsConfirmCardComponent implements OnInit, OnDestroy { @Input() projectId?: string; @Output() requirementConfirmed = new EventEmitter(); @Output() progressUpdated = new EventEmitter(); @Output() stageCompleted = new EventEmitter<{ stage: string; allStagesCompleted: boolean }>(); @Output() dataUpdated = new EventEmitter(); // 新增:实时数据更新事件 // 表单 materialForm!: FormGroup; commentForm!: FormGroup; materialUploadForm!: FormGroup; // 数据 materialFiles: MaterialFile[] = []; materials: MaterialFile[] = []; requirementMetrics: RequirementMetric[] = []; collaborationComments: CollaborationComment[] = []; requirementItems: RequirementItem[] = []; // 状态 activeTab: 'materials' | 'mapping' | 'collaboration' | 'progress' = 'materials'; isUploading = false; isAnalyzing = false; dragOver = false; showConsistencyWarning = false; warningMessage = ''; // 自动保存相关状态 autoSaveEnabled = true; lastSaveTime?: Date; hasUnsavedChanges = false; isSaving = false; saveStatus: 'saved' | 'saving' | 'error' | 'unsaved' = 'saved'; // 流程状态 stageCompletionStatus = { materialAnalysis: false, requirementMapping: false, collaboration: false, progressReview: false }; // 自动保存定时器 private autoSaveTimer?: any; // 需求指标 colorIndicators = { mainColor: { r: 255, g: 230, b: 180 }, colorTemperature: 2700, colorRange: '暖色调', saturation: 70, brightness: 80, contrast: 60 }; spaceIndicators = { lineRatio: 60, blankRatio: 30, flowWidth: 0.9, aspectRatio: 1.6, ceilingHeight: 2.8, lightingLevel: 300 }; materialIndicators = { fabricRatio: 50, woodRatio: 30, metalRatio: 20, smoothness: 7, glossiness: 4, texture: 6 }; // 指标范围配置 indicatorRanges = { color: { r: { min: 0, max: 255 }, g: { min: 0, max: 255 }, b: { min: 0, max: 255 }, temperature: { min: 2000, max: 6500 }, saturation: { min: 0, max: 100 }, brightness: { min: 0, max: 100 }, contrast: { min: 0, max: 100 } }, space: { lineRatio: { min: 0, max: 100 }, blankRatio: { min: 10, max: 80 }, flowWidth: { min: 0.6, max: 1.5 }, aspectRatio: { min: 1.0, max: 3.0 }, ceilingHeight: { min: 2.4, max: 4.0 }, lightingLevel: { min: 100, max: 800 } }, material: { fabricRatio: { min: 0, max: 100 }, woodRatio: { min: 0, max: 100 }, metalRatio: { min: 0, max: 100 }, smoothness: { min: 1, max: 10 }, glossiness: { min: 1, max: 10 }, texture: { min: 1, max: 10 } } }; // 预设数据 presetAtmospheres = [ { name: '温馨暖调', rgb: '255,230,180', colorTemp: '2700-3000K', materials: ['木质', '布艺'] }, { name: '现代冷调', rgb: '200,220,240', colorTemp: '5000-6000K', materials: ['金属', '玻璃'] }, { name: '自然清新', rgb: '220,240,220', colorTemp: '4000-4500K', materials: ['竹木', '石材'] } ]; // 一致性检查 consistencyWarnings: string[] = []; historyStates: any[] = []; // 上传成功弹窗相关 showUploadSuccessModal = false; uploadedFiles: { id: string; name: string; url: string; size?: number; type?: 'image' | 'cad' | 'text'; preview?: string }[] = []; uploadType: 'image' | 'document' | 'mixed' = 'image'; colorAnalysisResult?: ColorAnalysisResult; cadAnalysisResult?: CadAnalysisResult; // 全局提示组件状态 showGlobalPrompt = false; promptMode: 'fullscreen' | 'corner' = 'corner'; promptPosition: 'top-right' | 'bottom-right' = 'bottom-right'; promptTitle = '上传成功!'; promptMessage = ''; // 全屏报告覆盖层 showFullReportOverlay = false; constructor( private fb: FormBuilder, private cdr: ChangeDetectorRef, private colorAnalysisService: ColorAnalysisService, private cadAnalysisService: CadAnalysisService ) {} ngOnInit() { this.initializeForms(); this.initializeRequirements(); this.loadPresetMetrics(); // 启用自动保存 this.autoSaveEnabled = true; // 定期检查阶段完成状态 setInterval(() => { this.checkStageCompletion(); }, 5000); } ngOnDestroy(): void { // 清理自动保存定时器 if (this.autoSaveTimer) { clearTimeout(this.autoSaveTimer); } } private initializeForms() { this.materialForm = this.fb.group({ textContent: ['', Validators.required], atmosphereDescription: [''], residentInfo: [''], scenePreferences: [''], title: [''], content: [''] }); this.commentForm = this.fb.group({ content: ['', Validators.required], requirementId: [''] }); this.materialUploadForm = this.fb.group({ file: [''], textContent: ['', Validators.required] }); } private initializeRequirements() { this.requirementItems = [ { id: 'color-atmosphere', title: '色彩氛围', description: '整体色调和氛围营造', priority: 'high', status: 'pending', lastUpdated: new Date() }, { id: 'space-layout', title: '空间布局', description: '功能分区和动线设计', priority: 'high', status: 'pending', lastUpdated: new Date() }, { id: 'material-selection', title: '材质选择', description: '主要材质和质感要求', priority: 'medium', status: 'pending', lastUpdated: new Date() } ]; } private loadPresetMetrics() { this.requirementMetrics = [ { id: 'main-color', category: 'color', name: '主色调', value: { r: 255, g: 230, b: 180 }, description: '空间主要色彩' }, { id: 'color-temp', category: 'color', name: '色温', value: 3000, unit: 'K', range: { min: 2700, max: 6500 }, description: '照明色温范围' }, { id: 'wood-ratio', category: 'material', name: '木质占比', value: 60, unit: '%', range: { min: 0, max: 100 }, description: '木质材料使用比例' } ]; } // 素材上传功能 onFileSelected(event: Event, type: 'text' | 'image' | 'cad') { const input = event.target as HTMLInputElement; if (input.files) { Array.from(input.files).forEach(file => { this.uploadFile(file, type); }); } } onFileDrop(event: DragEvent, type: 'text' | 'image' | 'cad') { event.preventDefault(); this.dragOver = false; if (event.dataTransfer?.files) { Array.from(event.dataTransfer.files).forEach(file => { this.uploadFile(file, type); }); } } onDragOver(event: DragEvent) { event.preventDefault(); this.dragOver = true; } onDragLeave(event: DragEvent) { event.preventDefault(); this.dragOver = false; } private uploadFile(file: File, type: 'text' | 'image' | 'cad') { this.isUploading = true; // 模拟文件上传 setTimeout(() => { const materialFile: MaterialFile = { id: this.generateId(), name: file.name, type: type, url: URL.createObjectURL(file), size: this.formatFileSize(file.size), uploadTime: new Date() }; this.materialFiles.push(materialFile); this.materials.push(materialFile); this.isUploading = false; // 显示全局提示(角落模式,无遮罩,不遮挡操作) this.promptTitle = '上传成功!'; this.promptMessage = `已上传文件:${file.name}`; this.promptMode = 'corner'; this.promptPosition = 'bottom-right'; this.showGlobalPrompt = true; // 显示上传成功弹窗 this.showUploadSuccessModal = true; this.uploadedFiles = [{ id: materialFile.id, name: file.name, url: materialFile.url || '', size: file.size, type: type === 'text' ? 'text' : type === 'image' ? 'image' : 'cad' }]; this.uploadType = type === 'text' ? 'document' : type === 'image' ? 'image' : 'mixed'; // 自动解析 this.analyzeMaterial(materialFile); }, 1000); } // 文本提交 onTextSubmit(): void { if (this.materialUploadForm.valid) { const formValue = this.materialUploadForm.value; const textMaterial: MaterialFile = { id: this.generateId(), name: '文本需求', type: 'text', content: formValue.textContent, uploadTime: new Date(), size: this.formatFileSize(formValue.textContent?.length || 0) }; this.materialFiles.push(textMaterial); this.materials.push(textMaterial); this.analyzeMaterial(textMaterial); this.materialUploadForm.reset(); } } // 素材解析功能 private analyzeMaterial(material: MaterialFile) { this.isAnalyzing = true; // CAD采用服务分析,其它类型保持模拟 if (material.type === 'cad') { const fileUrl = material.url || ''; this.cadAnalysisService.analyzeByUrl(fileUrl, material.name).subscribe({ next: (result) => { this.cadAnalysisResult = result; const analysis: MaterialAnalysis = { structuralElements: result.structuralElements, spaceMetrics: result.spaceMetrics, flowMetrics: result.flowMetrics }; material.analysis = analysis; this.isAnalyzing = false; this.updateRequirementsFromAnalysis(analysis); }, error: () => { this.cadAnalysisService.simulateCadAnalysis(material.name).subscribe({ next: (mock) => { this.cadAnalysisResult = mock; const analysis: MaterialAnalysis = { structuralElements: mock.structuralElements, spaceMetrics: mock.spaceMetrics, flowMetrics: mock.flowMetrics }; material.analysis = analysis; this.isAnalyzing = false; this.updateRequirementsFromAnalysis(analysis); }, error: () => { this.isAnalyzing = false; console.error('CAD分析失败'); } }); } }); return; } // 非CAD类型模拟 setTimeout(() => { let analysis: MaterialAnalysis = {}; switch (material.type) { case 'text': analysis = this.analyzeTextMaterial(material); break; case 'image': analysis = this.analyzeImageMaterial(material); break; } material.analysis = analysis; this.isAnalyzing = false; this.updateRequirementsFromAnalysis(analysis); }, 1200); } private analyzeTextMaterial(material: MaterialFile): MaterialAnalysis { return { atmosphere: { description: '温馨暖调', rgb: '255,230,180', colorTemp: '2700-3000K' }, residents: { type: '亲子家庭', details: '孩子年龄3-6岁' }, scenes: { preference: '瑜伽爱好者', requirements: '需要瑜伽区收纳' }, keywords: { positive: ['科技感', '温馨', '实用'], negative: ['复杂线条', '冷色调'] } }; } private analyzeImageMaterial(material: MaterialFile): MaterialAnalysis { return { mainColors: [ { rgb: '255,230,180', percentage: 45 }, { rgb: '200,180,160', percentage: 30 }, { rgb: '180,160,140', percentage: 25 } ], colorTemperature: '3000K', materialRatio: [ { material: '木质', percentage: 60 }, { material: '布艺', percentage: 25 }, { material: '金属', percentage: 15 } ], spaceRatio: 35 }; } private analyzeCADMaterial(material: MaterialFile): MaterialAnalysis { return { structuralElements: [ { type: '承重柱', position: '客厅中央', changeable: false }, { type: '门窗', position: '南墙', changeable: false } ], spaceMetrics: [ { room: '客厅', ratio: '16:9', width: '4.2m' }, { room: '卧室', ratio: '4:3', width: '3.6m' } ], flowMetrics: [ { area: '主通道', width: '1.2m', compliance: true }, { area: '次通道', width: '0.8m', compliance: false } ] }; } private updateRequirementsFromAnalysis(analysis: MaterialAnalysis) { if (analysis.atmosphere?.rgb) { const colorMetric = this.requirementMetrics.find(m => m.id === 'main-color'); if (colorMetric) { const [r, g, b] = analysis.atmosphere.rgb.split(',').map(Number); colorMetric.value = { r, g, b }; } } if (analysis.materialRatio) { const woodRatio = analysis.materialRatio.find(m => m.material === '木质'); if (woodRatio) { const woodMetric = this.requirementMetrics.find(m => m.id === 'wood-ratio'); if (woodMetric) { woodMetric.value = woodRatio.percentage; } } } } // 需求确认功能 confirmRequirement(requirementId: string) { // 检查是否可以进行需求确认操作 if (!this.canProceedToNextStage('materialAnalysis')) { alert('请先完成素材分析阶段的所有必要操作'); return; } const requirement = this.requirementItems.find(r => r.id === requirementId); if (requirement) { requirement.status = 'confirmed'; requirement.lastUpdated = new Date(); this.requirementConfirmed.emit(requirement); this.updateProgress(); this.triggerAutoSave(); // 检查阶段完成状态 this.checkStageCompletion(); } } rejectRequirement(requirementId: string, reason?: string) { // 检查是否可以进行需求拒绝操作 if (!this.canProceedToNextStage('materialAnalysis')) { alert('请先完成素材分析阶段的所有必要操作'); return; } const requirement = this.requirementItems.find(r => r.id === requirementId); if (requirement) { requirement.status = 'rejected'; requirement.lastUpdated = new Date(); if (reason) { this.addCommentToRequirement(requirementId, reason, 'system'); } this.updateProgress(); this.triggerAutoSave(); // 检查阶段完成状态 this.checkStageCompletion(); } } // 协作功能 addComment() { if (this.commentForm.valid) { const comment: CollaborationComment = { id: this.generateId(), author: '当前用户', role: 'designer', content: this.commentForm.value.content, timestamp: new Date(), requirementId: this.commentForm.value.requirementId, status: 'pending' }; this.collaborationComments.push(comment); this.commentForm.reset(); this.triggerAutoSave(); // 检查阶段完成状态 this.checkStageCompletion(); } } resolveComment(commentId: string) { const comment = this.collaborationComments.find(c => c.id === commentId); if (comment) { comment.status = 'resolved'; this.triggerAutoSave(); // 检查阶段完成状态 this.checkStageCompletion(); } } // 进度管理 private updateProgress() { const totalRequirements = this.requirementItems.length; const confirmedRequirements = this.requirementItems.filter(r => r.status === 'confirmed').length; const progress = totalRequirements > 0 ? (confirmedRequirements / totalRequirements) * 100 : 0; // 实时更新进度条 this.updateProgressBar(progress); this.progressUpdated.emit(progress); // 检查是否完成所有需求 if (progress === 100) { this.onRequirementCommunicationComplete(); } } private updateProgressBar(progress: number): void { // 更新进度条显示 const progressBar = document.querySelector('.progress-bar-fill') as HTMLElement; if (progressBar) { progressBar.style.width = `${progress}%`; } // 更新进度文本 const progressText = document.querySelector('.progress-text') as HTMLElement; if (progressText) { progressText.textContent = `${Math.round(progress)}%`; } // 添加进度动画效果 this.animateProgressUpdate(progress); } private animateProgressUpdate(progress: number): void { // 添加进度更新的视觉反馈 const progressContainer = document.querySelector('.progress-container') as HTMLElement; if (progressContainer) { progressContainer.classList.add('progress-updated'); setTimeout(() => { progressContainer.classList.remove('progress-updated'); }, 500); } } // 需求沟通阶段完成处理 private onRequirementCommunicationComplete(): void { console.log('需求沟通阶段完成,开始同步关键信息'); this.syncKeyInfoToSolutionConfirmation(); } // 同步关键信息到方案确认阶段 private syncKeyInfoToSolutionConfirmation(): void { const keyInfo = { colorAtmosphere: { mainColor: this.colorIndicators.mainColor, colorTemperature: this.colorIndicators.colorTemperature, saturation: this.colorIndicators.saturation, brightness: this.colorIndicators.brightness }, spaceStructure: { lineRatio: this.spaceIndicators.lineRatio, blankRatio: this.spaceIndicators.blankRatio, flowWidth: this.spaceIndicators.flowWidth }, materialWeights: { fabricRatio: this.materialIndicators.fabricRatio, woodRatio: this.materialIndicators.woodRatio, metalRatio: this.materialIndicators.metalRatio, smoothness: this.materialIndicators.smoothness }, presetAtmosphere: this.getSelectedPresetAtmosphere() }; // 触发同步事件 this.requirementConfirmed.emit({ id: 'requirement-sync', title: '需求沟通完成', description: '关键信息已同步至方案确认阶段', priority: 'high', status: 'confirmed', lastUpdated: new Date(), metrics: this.convertToMetrics(keyInfo) }); // 启动交付执行流程 setTimeout(() => { this.startDeliveryExecution(); }, 1000); } private getSelectedPresetAtmosphere(): any { // 根据当前颜色指标找到最匹配的预设氛围 const currentRgb = `${this.colorIndicators.mainColor.r},${this.colorIndicators.mainColor.g},${this.colorIndicators.mainColor.b}`; return this.presetAtmospheres.find(preset => preset.rgb === currentRgb) || this.presetAtmospheres[0]; } private convertToMetrics(keyInfo: any): RequirementMetric[] { return [ { id: 'color-sync', category: 'color', name: '色彩氛围', value: keyInfo.colorAtmosphere }, { id: 'space-sync', category: 'space', name: '空间结构', value: keyInfo.spaceStructure }, { id: 'material-sync', category: 'material', name: '材质权重', value: keyInfo.materialWeights } ]; } // 启动交付执行流程 private startDeliveryExecution(): void { console.log('启动交付执行流程'); // 显示执行启动提示 this.showExecutionStartNotification(); // 这里可以触发路由跳转或其他业务逻辑 // 例如:this.router.navigate(['/project', this.projectId, 'execution']); } private showExecutionStartNotification(): void { // 显示执行流程启动的通知 const notification = document.createElement('div'); notification.className = 'execution-notification'; notification.innerHTML = `
交付执行流程已启动
`; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); } getProgressPercentage(): number { if (this.requirementItems.length === 0) return 0; const confirmedCount = this.requirementItems.filter(r => r.status === 'confirmed').length; return Math.round((confirmedCount / this.requirementItems.length) * 100); } // 处理优先级更新 onPriorityChange(requirementId: string, event: Event): void { const target = event.target as HTMLSelectElement; this.updateRequirementPriority(requirementId, target.value as 'high' | 'medium' | 'low'); } // 检查需求是否有待处理评论 hasUnreadComments(requirementId: string): boolean { return this.getCommentsForRequirement(requirementId).some(c => c.status === 'pending'); } // 处理历史状态选择 onHistoryStateChange(event: Event): void { const target = event.target as HTMLSelectElement; const index = +target.value; if (index >= 0) { this.restoreHistoryState(index); } } // 获取指定状态的需求数量 getRequirementCountByStatus(status: 'confirmed' | 'pending' | 'rejected'): number { return (this.requirementItems || []).filter(r => r.status === status).length; } // 获取状态百分比 getStatusPercentage(status: 'confirmed' | 'pending' | 'rejected'): number { const total = this.requirementItems.length; if (total === 0) return 0; const statusCount = this.getRequirementCountByStatus(status); return Math.round((statusCount / total) * 100); } // 指标映射功能 updateMetricValue(metricId: string, value: any) { const metric = this.requirementMetrics.find(m => m.id === metricId); if (metric) { metric.value = value; this.checkConsistency(); } } // 滑动条数值同步方法 onSliderChange(key: string, value: number): void { console.log(`滑动条变化: ${key} = ${value}`); console.log('变化前的mainColor:', JSON.stringify(this.colorIndicators.mainColor)); // 直接更新数据,不需要额外的updateIndicatorValue调用 // 因为使用了双向绑定,数据已经自动更新 if (key === 'r' || key === 'g' || key === 'b') { // 确保RGB值在有效范围内 const range = this.getIndicatorRange(key); if (range) { const clampedValue = Math.max(range.min, Math.min(range.max, value)); this.colorIndicators.mainColor[key as keyof typeof this.colorIndicators.mainColor] = clampedValue; console.log(`RGB值已更新: ${key} = ${clampedValue}`); console.log('变化后的mainColor:', JSON.stringify(this.colorIndicators.mainColor)); } // 触发颜色指标更新 this.updateColorIndicator('mainColor', this.colorIndicators.mainColor); } else { // 处理其他颜色指标 this.updateIndicatorValue(key, value); } this.triggerAutoSave(); // 触发自动保存 this.checkStageCompletion(); // 检查阶段完成状态 // 发射实时数据更新事件 this.emitDataUpdate(); // 强制触发变更检测 this.cdr.detectChanges(); console.log('滑动条变更检测已触发'); } onInputChange(key: string, value: number): void { console.log(`输入框变化: ${key} = ${value}`); console.log('变化前的mainColor:', JSON.stringify(this.colorIndicators.mainColor)); // 直接更新数据,不需要额外的updateIndicatorValue调用 // 因为使用了双向绑定,数据已经自动更新 if (key === 'r' || key === 'g' || key === 'b') { // 确保RGB值在有效范围内 const range = this.getIndicatorRange(key); if (range) { const clampedValue = Math.max(range.min, Math.min(range.max, value)); this.colorIndicators.mainColor[key as keyof typeof this.colorIndicators.mainColor] = clampedValue; console.log(`RGB值已更新: ${key} = ${clampedValue}`); console.log('变化后的mainColor:', JSON.stringify(this.colorIndicators.mainColor)); } // 触发颜色指标更新 this.updateColorIndicator('mainColor', this.colorIndicators.mainColor); } else { // 处理其他颜色指标 this.updateIndicatorValue(key, value); } this.triggerAutoSave(); // 发射实时数据更新事件 this.emitDataUpdate(); // 强制触发变更检测 this.cdr.detectChanges(); console.log('输入框变更检测已触发'); } validateInput(key: string, value: string): void { const numValue = parseFloat(value); if (isNaN(numValue)) { // 如果输入无效,恢复原值 this.restoreIndicatorValue(key); return; } const range = this.getIndicatorRange(key); if (range) { const clampedValue = Math.max(range.min, Math.min(range.max, numValue)); this.updateIndicatorValue(key, clampedValue); } } private updateIndicatorValue(key: string, value: number): void { console.log(`updateIndicatorValue调用: ${key} = ${value}`); // 更新颜色指标 if (key in this.colorIndicators) { if (key === 'r' || key === 'g' || key === 'b') { // 创建新的mainColor对象以确保变更检测 const oldColor = { ...this.colorIndicators.mainColor }; this.colorIndicators.mainColor = { ...this.colorIndicators.mainColor, [key]: value }; console.log(`RGB更新: ${key}从${oldColor[key as keyof typeof oldColor]}变为${value}`, '新mainColor:', this.colorIndicators.mainColor); this.updateColorIndicator('mainColor', this.colorIndicators.mainColor); } else { (this.colorIndicators as any)[key] = value; this.updateColorIndicator(key, value); } return; } // 更新空间指标 if (key in this.spaceIndicators) { (this.spaceIndicators as any)[key] = value; this.updateSpaceIndicator(key, value); return; } // 更新材质指标 if (key in this.materialIndicators) { (this.materialIndicators as any)[key] = value; this.updateMaterialIndicator(key, value); return; } } private restoreIndicatorValue(key: string): void { // 这里可以从历史状态或默认值恢复 console.log(`恢复指标值: ${key}`); } private getIndicatorRange(key: string): { min: number; max: number } | null { // 检查颜色范围 if (key === 'r' || key === 'g' || key === 'b') { return this.indicatorRanges.color[key as keyof typeof this.indicatorRanges.color]; } if (key in this.indicatorRanges.color) { return (this.indicatorRanges.color as any)[key]; } // 检查空间范围 if (key in this.indicatorRanges.space) { return (this.indicatorRanges.space as any)[key]; } // 检查材质范围 if (key in this.indicatorRanges.material) { return (this.indicatorRanges.material as any)[key]; } return null; } // 指标更新方法 updateColorIndicator(type: string, value?: any): void { switch (type) { case 'mainColor': if (value && typeof value === 'object' && 'r' in value) { this.colorIndicators.mainColor = { ...value }; // 创建新对象以触发变更检测 console.log('mainColor更新后:', this.colorIndicators.mainColor); } break; case 'colorTemperature': // 色温更新时不需要改变颜色值,只是触发一致性检查 break; case 'saturation': // 饱和度更新时不需要改变颜色值,只是触发一致性检查 break; } console.log(`更新颜色指示器 ${type}:`, value, '当前RGB:', this.getRgbString()); this.checkConsistency(); this.updatePreview(); // 强制触发变更检测 this.cdr.detectChanges(); console.log('变更检测已触发'); } updateSpaceIndicator(key: string, value: number) { if (key in this.spaceIndicators) { (this.spaceIndicators as any)[key] = value; this.checkConsistency(); } } updateMaterialIndicator(key: string, value: number) { if (key in this.materialIndicators) { (this.materialIndicators as any)[key] = value; this.checkConsistency(); this.normalizeMaterialRatios(); } } // 材质比例归一化 private normalizeMaterialRatios() { const total = this.materialIndicators.fabricRatio + this.materialIndicators.woodRatio + this.materialIndicators.metalRatio; if (total > 100) { const scale = 100 / total; this.materialIndicators.fabricRatio = Math.round(this.materialIndicators.fabricRatio * scale); this.materialIndicators.woodRatio = Math.round(this.materialIndicators.woodRatio * scale); this.materialIndicators.metalRatio = 100 - this.materialIndicators.fabricRatio - this.materialIndicators.woodRatio; } } // 预览更新 private updatePreview() { // 移除直接DOM操作,依赖Angular的属性绑定 console.log('updatePreview调用,当前RGB:', this.getRgbString()); // 不再直接操作DOM,让Angular的变更检测处理 } // 获取RGB字符串 getRgbString(): string { const { r, g, b } = this.colorIndicators.mainColor; const rgbString = `rgb(${r}, ${g}, ${b})`; console.log('getRgbString调用:', rgbString, '完整mainColor对象:', this.colorIndicators.mainColor); return rgbString; } // 获取色温描述 getColorTemperatureDescription(): string { const temp = this.colorIndicators.colorTemperature; if (temp < 3000) return '暖色调'; if (temp < 4000) return '中性色调'; if (temp < 5000) return '自然色调'; return '冷色调'; } applyPresetAtmosphere(preset: any) { const rgbMatch = preset.rgb.match(/(\d+),(\d+),(\d+)/); if (rgbMatch) { this.colorIndicators.mainColor = { r: parseInt(rgbMatch[1]), g: parseInt(rgbMatch[2]), b: parseInt(rgbMatch[3]) }; } const tempMatch = preset.colorTemp.match(/(\d+)/); if (tempMatch) { this.colorIndicators.colorTemperature = parseInt(tempMatch[1]); } if (preset.materials.includes('木质')) { this.materialIndicators.woodRatio = 60; this.materialIndicators.fabricRatio = 30; this.materialIndicators.metalRatio = 10; } else if (preset.materials.includes('金属')) { this.materialIndicators.metalRatio = 50; this.materialIndicators.woodRatio = 20; this.materialIndicators.fabricRatio = 30; } this.updatePreview(); this.checkConsistency(); } // 一致性检查 private checkConsistency() { this.consistencyWarnings = []; if (this.colorIndicators.colorTemperature < 3000 && this.colorIndicators.mainColor.r > 200 && this.colorIndicators.mainColor.g < 150) { this.consistencyWarnings.push('暖调氛围与冷色调RGB值存在矛盾'); } if (this.materialIndicators.fabricRatio > 70 && this.colorIndicators.colorTemperature > 5000) { this.consistencyWarnings.push('高布艺占比与冷色调可能不协调'); } if (this.spaceIndicators.lineRatio > 80 && this.materialIndicators.woodRatio > 60) { this.consistencyWarnings.push('高直线条占比与高木质占比可能过于刚硬'); } } // 协作批注功能 addCommentToRequirement(requirementId: string, content: string, author: string = '当前用户'): void { const comment: CollaborationComment = { id: this.generateId(), author, role: 'designer', content, timestamp: new Date(), requirementId, status: 'pending' }; this.collaborationComments.push(comment); this.updateProgress(); } // 优先级排序功能 updateRequirementPriority(requirementId: string, priority: 'high' | 'medium' | 'low'): void { const requirement = this.requirementItems.find(r => r.id === requirementId); if (requirement) { requirement.priority = priority; requirement.lastUpdated = new Date(); this.sortRequirementsByPriority(); } } sortRequirementsByPriority(): void { const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 }; this.requirementItems.sort((a, b) => { return priorityOrder[b.priority] - priorityOrder[a.priority]; }); } // 历史记录功能 saveCurrentState(): void { const currentState = { timestamp: new Date(), colorIndicators: { ...this.colorIndicators }, spaceIndicators: { ...this.spaceIndicators }, materialIndicators: { ...this.materialIndicators }, requirementItems: this.requirementItems.map((r: RequirementItem) => ({ ...r })), author: '当前用户' }; this.historyStates.push(currentState); if (this.historyStates.length > 10) { this.historyStates.shift(); } } restoreHistoryState(index: number): void { if (index >= 0 && index < this.historyStates.length) { const state = this.historyStates[index]; this.colorIndicators = { ...state.colorIndicators }; this.spaceIndicators = { ...state.spaceIndicators }; this.materialIndicators = { ...state.materialIndicators }; this.requirementItems = state.requirementItems.map((r: RequirementItem) => ({ ...r })); this.updatePreview(); this.checkConsistency(); } } // 获取未解决的评论数量 getUnresolvedCommentsCount(): number { return this.collaborationComments.filter(c => c.status === 'pending').length; } // 获取需求的评论 getCommentsForRequirement(requirementId: string): CollaborationComment[] { return this.collaborationComments.filter(comment => comment.requirementId === requirementId) || []; } // 获取状态样式类 getStatusClass(status: string): string { return status; } // 工具方法 private generateId(): string { return Math.random().toString(36).substr(2, 9); } private formatFileSize(bytes: number): string { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // 删除素材 removeMaterial(materialId: string) { const index = this.materialFiles.findIndex(m => m.id === materialId); if (index > -1) { const material = this.materialFiles[index]; if (material.url) { URL.revokeObjectURL(material.url); } this.materialFiles.splice(index, 1); } const materialsIndex = this.materials.findIndex(m => m.id === materialId); if (materialsIndex > -1) { this.materials.splice(materialsIndex, 1); } } // 切换标签页 switchTab(tab: 'materials' | 'mapping' | 'collaboration' | 'progress') { this.activeTab = tab; } // 获取需求状态文本 getRequirementStatusText(status: string): string { const statusMap: { [key: string]: string } = { 'pending': '待确认', 'confirmed': '已确认', 'rejected': '已拒绝' }; return statusMap[status] || status; } // 获取优先级文本 getPriorityText(priority: string): string { const priorityMap: { [key: string]: string } = { 'high': '高', 'medium': '中', 'low': '低' }; return priorityMap[priority] || priority; } // 刷新进度(保留用于手动刷新,但不再强制要求) refreshProgress(): void { // 重新计算进度 this.updateProgress(); // 更新进度条显示 const progress = this.getProgressPercentage(); this.updateProgressBar(progress); // 检查阶段完成状态 this.checkStageCompletion(); // 触发进度更新事件 this.progressUpdated.emit(progress); console.log('进度已手动刷新:', progress + '%'); } // 自动保存功能 private triggerAutoSave(): void { if (!this.autoSaveEnabled) return; this.hasUnsavedChanges = true; this.saveStatus = 'unsaved'; // 清除之前的定时器 if (this.autoSaveTimer) { clearTimeout(this.autoSaveTimer); } // 设置新的自动保存定时器(延迟1秒) this.autoSaveTimer = setTimeout(() => { this.performAutoSave(); }, 1000); } private async performAutoSave(): Promise { if (this.isSaving) return; this.isSaving = true; this.saveStatus = 'saving'; try { // 收集所有需要保存的数据 const saveData = this.collectSaveData(); // 模拟保存操作(实际项目中这里会调用API) await this.saveToServer(saveData); this.hasUnsavedChanges = false; this.saveStatus = 'saved'; this.lastSaveTime = new Date(); console.log('自动保存成功:', new Date().toLocaleTimeString()); } catch (error) { this.saveStatus = 'error'; console.error('自动保存失败:', error); } finally { this.isSaving = false; } } // 手动保存 async manualSave(): Promise { if (this.isSaving) return; // 清除自动保存定时器 if (this.autoSaveTimer) { clearTimeout(this.autoSaveTimer); } await this.performAutoSave(); } private collectSaveData(): any { return { projectId: this.projectId, colorIndicators: this.colorIndicators, spaceIndicators: this.spaceIndicators, materialIndicators: this.materialIndicators, requirementItems: this.requirementItems, requirementMetrics: this.requirementMetrics, materialFiles: this.materialFiles, collaborationComments: this.collaborationComments, stageCompletionStatus: this.stageCompletionStatus, lastUpdated: new Date() }; } private async saveToServer(data: any): Promise { // 模拟API调用延迟 return new Promise((resolve, reject) => { setTimeout(() => { // 模拟90%成功率 if (Math.random() > 0.1) { resolve(); } else { reject(new Error('网络错误')); } }, 500); }); } // 检查阶段完成状态 private checkStageCompletion(): void { // 检查素材分析阶段 - 需要有素材文件且都已分析 this.stageCompletionStatus.materialAnalysis = this.materialFiles.length > 0 && this.materialFiles.every(m => m.analysis); // 检查需求映射阶段 - 需要有确认的需求项,或者滑动条数据已调整且保存 const hasConfirmedRequirements = this.requirementItems.length > 0 && this.requirementItems.some(r => r.status === 'confirmed'); const hasAdjustedIndicators = this.hasIndicatorChanges(); this.stageCompletionStatus.requirementMapping = hasConfirmedRequirements || (hasAdjustedIndicators && this.saveStatus === 'saved'); // 检查协作验证阶段 - 需要有协作评论或需求评论 this.stageCompletionStatus.collaboration = this.collaborationComments.length > 0 || this.requirementItems.some(r => r.comments && r.comments.length > 0); // 检查进度审查阶段 - 需要进度达到80%以上 this.stageCompletionStatus.progressReview = this.getProgressPercentage() >= 80; console.log('阶段完成状态更新:', this.stageCompletionStatus); // 检查是否所有阶段都已完成 const allStagesCompleted = Object.values(this.stageCompletionStatus).every(status => status); // 发射阶段完成事件 if (allStagesCompleted) { this.stageCompleted.emit({ stage: 'requirements-communication', allStagesCompleted: true }); } } // 检查指示器是否有变化 private hasIndicatorChanges(): boolean { // 检查颜色指示器是否有变化(与默认值比较) const defaultColorIndicators = { mainColor: { r: 255, g: 230, b: 180 }, colorTemperature: 2700, colorRange: '暖色调', saturation: 70, brightness: 80, contrast: 60 }; const defaultSpaceIndicators = { lineRatio: 60, blankRatio: 30, flowWidth: 0.9, aspectRatio: 1.6, ceilingHeight: 2.8, lightingLevel: 300 }; const defaultMaterialIndicators = { fabricRatio: 50, woodRatio: 30, metalRatio: 20, smoothness: 7, glossiness: 4, texture: 6 }; // 检查颜色指示器变化 const colorChanged = JSON.stringify(this.colorIndicators) !== JSON.stringify(defaultColorIndicators); // 检查空间指示器变化 const spaceChanged = JSON.stringify(this.spaceIndicators) !== JSON.stringify(defaultSpaceIndicators); // 检查材质指示器变化 const materialChanged = JSON.stringify(this.materialIndicators) !== JSON.stringify(defaultMaterialIndicators); return colorChanged || spaceChanged || materialChanged; } // 获取阶段状态文本 getStageStatusText(stage: keyof typeof this.stageCompletionStatus): string { if (this.stageCompletionStatus[stage]) { return '已完成'; } else { // 检查是否为未开始状态 const stages = ['materialAnalysis', 'requirementMapping', 'collaboration', 'progressReview']; const currentIndex = stages.indexOf(stage); // 检查前面的阶段是否都已完成 let allPreviousCompleted = true; for (let i = 0; i < currentIndex; i++) { if (!this.stageCompletionStatus[stages[i] as keyof typeof this.stageCompletionStatus]) { allPreviousCompleted = false; break; } } return allPreviousCompleted ? '进行中' : '未进行'; } } // 获取阶段状态类名 getStageStatusClass(stage: keyof typeof this.stageCompletionStatus): string { if (this.stageCompletionStatus[stage]) { return 'stage-completed'; } else { // 检查是否为未开始状态 const stages = ['materialAnalysis', 'requirementMapping', 'collaboration', 'progressReview']; const currentIndex = stages.indexOf(stage); // 检查前面的阶段是否都已完成 let allPreviousCompleted = true; for (let i = 0; i < currentIndex; i++) { if (!this.stageCompletionStatus[stages[i] as keyof typeof this.stageCompletionStatus]) { allPreviousCompleted = false; break; } } return allPreviousCompleted ? 'stage-in-progress' : 'stage-pending'; } } // 检查是否可以进入下一阶段 canProceedToNextStage(currentStage: keyof typeof this.stageCompletionStatus): boolean { const stages = Object.keys(this.stageCompletionStatus) as (keyof typeof this.stageCompletionStatus)[]; const currentIndex = stages.indexOf(currentStage); // 检查当前阶段之前的所有阶段是否都已完成 for (let i = 0; i <= currentIndex; i++) { if (!this.stageCompletionStatus[stages[i]]) { return false; } } return true; } // 获取保存状态文本 getSaveStatusText(): string { switch (this.saveStatus) { case 'saved': return this.lastSaveTime ? `已保存 ${this.lastSaveTime.toLocaleTimeString()}` : '已保存'; case 'saving': return '保存中...'; case 'error': return '保存失败'; case 'unsaved': return '有未保存的更改'; default: return ''; } } // 获取保存状态图标 getSaveStatusIcon(): string { switch (this.saveStatus) { case 'saved': return '✓'; case 'saving': return '⏳'; case 'error': return '⚠'; case 'unsaved': return '●'; default: return ''; } } // 上传成功弹窗相关方法 onModalClose(): void { this.showUploadSuccessModal = false; this.uploadedFiles = []; this.colorAnalysisResult = undefined; } // 全局提示关闭 onPromptClose(): void { this.showGlobalPrompt = false; } onAnalyzeColors(): void { if (this.uploadedFiles.length > 0 && this.uploadType === 'image') { const imageFile = this.uploadedFiles[0]; // 创建一个File对象用于分析 const file = new File([], imageFile.name, { type: imageFile.type || 'image/jpeg' }); // 使用模拟分析(在实际应用中应该调用真实的API) this.colorAnalysisService.simulateAnalysis(file).subscribe({ next: (result) => { this.colorAnalysisResult = result; // 可以在这里更新颜色指标 if (result.colors.length > 0) { const mainColor = result.colors[0]; this.updateColorIndicator('mainColor', mainColor.rgb); } // 新增:分析完成后向父级发射数据更新,包含色彩分析结果 this.emitDataUpdate(); }, error: (error) => { console.error('颜色分析失败:', error); } }); } } onViewReport(): void { // 打开全屏报告覆盖层 this.showFullReportOverlay = true; } onCloseFullReport(): void { this.showFullReportOverlay = false; } // 新增:发射实时数据更新事件的方法 private emitDataUpdate(): void { const currentData = { colorIndicators: this.colorIndicators || [], spaceIndicators: this.spaceIndicators || [], materialIndicators: this.materialIndicators || [], requirementItems: this.requirementItems || [], materials: this.materials || [], collaborationComments: this.collaborationComments || '', stageCompletionStatus: this.stageCompletionStatus || {}, // 新增:传递色彩分析结果到父级用于右侧展示 colorAnalysisResult: this.colorAnalysisResult }; this.dataUpdated.emit(currentData); console.log('实时数据更新事件已发射:', currentData); } // 新增:图片与CAD文件预览方法 previewImage(url?: string): void { if (!url) { return; } try { window.open(url, '_blank'); } catch (e) { console.error('预览图片失败:', e); } } previewCad(url?: string): void { if (!url) { return; } try { window.open(url, '_blank'); } catch (e) { console.error('预览CAD失败:', e); } } }