# 项目管理 - 需求确认阶段 PRD ## 1. 功能概述 ### 1.1 阶段定位 需求确认阶段包含"需求沟通"和"方案确认"两个子环节,是连接订单分配与交付执行的关键桥梁。该阶段通过AI辅助分析工具深入理解客户需求,并将抽象需求转化为可执行的设计方案。 ### 1.2 核心目标 - **需求沟通环节**:深度挖掘客户的色彩、空间、材质、照明等多维度需求 - **方案确认环节**:基于需求分析生成初步设计方案,并获得客户确认 - 建立需求与设计方案之间的映射关系 - 为后续建模、软装、渲染阶段提供标准化输入 ### 1.3 涉及角色 - **客服人员**:协助收集客户需求材料、沟通确认需求细节 - **设计师**:主导需求分析、方案设计、与客户沟通确认 - **组长**:审核方案可行性、协调资源、把控质量 ### 1.4 阶段划分 ```mermaid graph LR A[订单分配] --> B[需求沟通] B --> C[方案确认] C --> D[建模] style B fill:#e3f2fd style C fill:#e8f5e9 ``` ## 2. 需求沟通环节 ### 2.1 需求沟通卡片组件 #### 2.1.1 组件集成 **组件标签**: ```html ``` #### 2.1.2 核心功能模块 **四大需求采集流程**: 1. **色彩氛围需求** → AI色彩分析 2. **空间结构需求** → AI空间布局分析 3. **材质权重需求** → AI材质识别分析 4. **照明需求** → AI光照场景分析 ### 2.2 色彩氛围需求采集 #### 2.2.1 数据结构 ```typescript interface ColorAtmosphereRequirement { // 用户描述 description: string; // 客户对色彩氛围的文字描述 referenceImages: Array<{ // 参考图片 id: string; url: string; name: string; uploadTime: Date; }>; // AI分析结果 colorAnalysisResult?: { originalImage: string; // 原始参考图URL colors: Array<{ hex: string; // 十六进制颜色值 rgb: string; // RGB值 percentage: number; // 占比 0-100 name: string; // 颜色名称 }>; dominantColor: { // 主色 hex: string; rgb: string; name: string; }; colorHarmony: { // 色彩调和 type: string; // 调和类型:monochromatic/analogous/complementary temperature: 'warm' | 'neutral' | 'cool'; // 色温 contrast: number; // 对比度 0-100 }; mood: string; // 氛围:温馨/冷静/活力/优雅 }; // 映射到设计指标 colorIndicators?: { mainColor: { r: number; g: number; b: number }; colorRange: string; // 色彩范围描述 colorTemperature: number; // 色温值(K) }; } ``` #### 2.2.2 参考图上传与分析流程 **上传触发**: ```typescript // 用户点击"上传参考图"按钮 onUploadReferenceImages(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files); // 1. 验证文件类型 const validFiles = files.filter(file => /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name) ); if (validFiles.length === 0) { alert('请上传有效的图片文件(JPG/PNG/GIF/BMP/WEBP)'); return; } // 2. 显示上传进度 this.isUploadingFiles = true; this.uploadProgress = 0; // 3. 上传文件到服务器 this.uploadFiles(validFiles).subscribe({ next: (uploadedFiles) => { // 4. 添加到参考图列表 this.referenceImages.push(...uploadedFiles); // 5. 触发AI色彩分析 this.triggerColorAnalysis(uploadedFiles[0].url); }, error: (error) => { console.error('上传失败:', error); alert('图片上传失败,请重试'); this.isUploadingFiles = false; } }); } ``` **AI色彩分析**: ```typescript // ColorAnalysisService 调用 triggerColorAnalysis(imageUrl: string): void { this.isAnalyzingColors = true; this.colorAnalysisService.analyzeImage(imageUrl).subscribe({ next: (result: ColorAnalysisResult) => { // 保存分析结果 this.colorAnalysisResult = result; // 计算主色 const colors = result.colors || []; if (colors.length > 0) { const dominant = colors.reduce((max, cur) => cur.percentage > max.percentage ? cur : max, colors[0] ); this.dominantColorHex = dominant.hex; } // 映射到需求指标 this.mapColorResultToIndicators(result); this.isAnalyzingColors = false; // 通知父组件更新 this.requirementDataUpdated.emit({ colorAnalysisResult: result, colorIndicators: this.colorIndicators }); }, error: (error) => { console.error('色彩分析失败:', error); alert('AI色彩分析失败,请重试'); this.isAnalyzingColors = false; } }); } ``` **色彩结果映射**: ```typescript mapColorResultToIndicators(result: ColorAnalysisResult): void { if (!result.dominantColor) return; // 将十六进制颜色转换为RGB const rgb = this.hexToRgb(result.dominantColor.hex); this.colorIndicators = { mainColor: { r: rgb.r, g: rgb.g, b: rgb.b }, colorRange: this.describeColorRange(result.colors), colorTemperature: this.calculateColorTemperature(rgb) }; } // 计算色温(简化算法) calculateColorTemperature(rgb: {r: number; g: number; b: number}): number { // 基于RGB值估算色温 // 暖色调:2700K-3500K,中性:4000K-5000K,冷色调:5500K-6500K const warmth = (rgb.r - rgb.b) / 255; if (warmth > 0.3) return 2700 + warmth * 800; // 暖色调 if (warmth < -0.3) return 5500 - warmth * 1000; // 冷色调 return 4500; // 中性 } ``` #### 2.2.3 色彩分析可视化 **右侧面板展示**(project-detail.html lines 1826-1900): ```html

色彩分析结果

@if (colorAnalysisResult) {
{{ colorAnalysisResult.dominantColor.name }} {{ colorAnalysisResult.dominantColor.hex }}
@for (color of colorAnalysisResult.colors; track color.hex) {
{{ color.percentage }}%
}
调和类型: {{ getColorHarmonyName(colorAnalysisResult.colorHarmony?.type) }}
色温: {{ getTemperatureName(colorAnalysisResult.colorHarmony?.temperature) }}
对比度: {{ colorAnalysisResult.colorHarmony?.contrast }}%
{{ colorAnalysisResult.mood }}
} @else {

上传参考图后将显示AI色彩分析结果

}
``` **色彩轮盘可视化组件**: ```html ``` ### 2.3 空间结构需求采集 #### 2.3.1 数据结构 ```typescript interface SpaceStructureRequirement { // CAD文件上传 cadFiles: Array<{ id: string; name: string; url: string; uploadTime: Date; fileSize: number; }>; // 手动输入 dimensions?: { length: number; // 长度(米) width: number; // 宽度(米) height: number; // 层高(米) area: number; // 面积(平方米) }; // AI分析结果 spaceAnalysis?: { dimensions: { length: number; width: number; height: number; area: number; volume: number; }; functionalZones: Array<{ zone: string; // 功能区名称 area: number; percentage: number; requirements: string[]; furniture: string[]; }>; circulation: { mainPaths: string[]; // 主要动线 pathWidth: number; // 动线宽度 efficiency: number; // 动线效率 0-100 }; layoutType: string; // 布局类型:open/enclosed/semi-open }; // 映射到设计指标 spaceIndicators?: { lineRatio: number; // 线条占比 0-1 blankRatio: number; // 留白占比 0-1 flowWidth: number; // 流线宽度 aspectRatio: number; // 空间比例 ceilingHeight: number; // 层高 }; } ``` #### 2.3.2 CAD文件上传与解析 **文件上传**: ```typescript onCADFilesSelected(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files); // 1. 验证文件类型(支持DWG/DXF/PDF等) const validFiles = files.filter(file => /\.(dwg|dxf|pdf)$/i.test(file.name) ); if (validFiles.length === 0) { alert('请上传有效的CAD文件(DWG/DXF/PDF)'); return; } // 2. 上传并解析CAD文件 this.uploadAndParseCAD(validFiles).subscribe({ next: (parsedData) => { this.cadFiles.push(...parsedData.files); this.spaceAnalysis = parsedData.analysis; // 映射到设计指标 this.mapSpaceAnalysisToIndicators(parsedData.analysis); // 通知父组件 this.requirementDataUpdated.emit({ spaceAnalysis: this.spaceAnalysis, spaceIndicators: this.spaceIndicators }); }, error: (error) => { console.error('CAD解析失败:', error); alert('CAD文件解析失败,请检查文件格式'); } }); } ``` **空间指标映射**: ```typescript mapSpaceAnalysisToIndicators(analysis: SpaceAnalysis): void { if (!analysis) return; const { dimensions, functionalZones, circulation } = analysis; this.spaceIndicators = { // 线条占比:基于功能区划分密度 lineRatio: functionalZones.length / 10, // 简化计算 // 留白占比:基于功能区总占比 blankRatio: 1 - functionalZones.reduce((sum, zone) => sum + zone.percentage / 100, 0 ), // 流线宽度 flowWidth: circulation.pathWidth, // 空间比例(长宽比) aspectRatio: dimensions.length / dimensions.width, // 层高 ceilingHeight: dimensions.height }; } ``` #### 2.3.3 空间结构可视化 **空间分区图表**: ```html

功能区分布

@if (spaceAnalysis?.functionalZones) {
@for (zone of spaceAnalysis.functionalZones; track zone.zone) {
{{ zone.zone }} {{ zone.percentage }}%
面积:{{ zone.area }}m²
需求:
@for (req of zone.requirements; track req) { {{ req }} }
家具:
@for (furn of zone.furniture; track furn) { {{ furn }} }
}
}
``` **动线效率雷达图**: ```html

动线分析

@if (spaceAnalysis?.circulation) {
主要动线: {{ spaceAnalysis.circulation.mainPaths.join(' → ') }}
动线宽度: {{ spaceAnalysis.circulation.pathWidth }}m
效率评分:
{{ spaceAnalysis.circulation.efficiency }}分
}
``` ### 2.4 材质权重需求采集 #### 2.4.1 数据结构 ```typescript interface MaterialRequirement { // 参考图片 materialImages: Array<{ id: string; url: string; name: string; }>; // AI材质识别结果 materialAnalysis?: Array<{ id: string; name: string; // 材质名称 category: string; // 类别:wood/metal/fabric/leather/plastic/glass/ceramic/stone confidence: number; // 识别置信度 0-1 properties: { texture: string; // 纹理:smooth/rough/woven/carved color: string; finish: string; // 表面处理:matte/glossy/satin hardness: number; // 硬度 0-10 }; usage: { suitableAreas: string[]; // 适用区域 priority: 'primary' | 'secondary' | 'accent'; }; }>; // 映射到设计指标 materialIndicators?: { fabricRatio: number; // 布艺占比 0-100 woodRatio: number; // 木质占比 0-100 metalRatio: number; // 金属占比 0-100 smoothness: number; // 平滑度 0-10 glossiness: number; // 光泽度 0-10 }; } ``` #### 2.4.2 材质识别流程 **图片上传触发识别**: ```typescript onMaterialImagesSelected(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files); this.isAnalyzingMaterials = true; // 1. 上传图片 this.uploadFiles(files).subscribe({ next: (uploadedFiles) => { this.materialImages.push(...uploadedFiles); // 2. 触发AI材质识别 this.analyzeMaterials(uploadedFiles.map(f => f.url)); } }); } analyzeMaterials(imageUrls: string[]): void { // 调用材质识别服务(可以是本地模型或云端API) this.materialAnalysisService.analyzeImages(imageUrls).subscribe({ next: (results) => { this.materialAnalysisData = results; // 计算材质权重 this.calculateMaterialWeights(results); this.isAnalyzingMaterials = false; // 通知父组件 this.requirementDataUpdated.emit({ materialAnalysisData: results, materialIndicators: this.materialIndicators }); }, error: (error) => { console.error('材质识别失败:', error); this.isAnalyzingMaterials = false; } }); } ``` **材质权重计算**: ```typescript calculateMaterialWeights(materials: MaterialAnalysis[]): void { if (!materials || materials.length === 0) return; // 按类别分组统计 const categoryCount: Record = {}; const categoryConfidence: Record = {}; materials.forEach(mat => { categoryCount[mat.category] = (categoryCount[mat.category] || 0) + 1; categoryConfidence[mat.category] = (categoryConfidence[mat.category] || 0) + mat.confidence; }); const total = materials.length; // 计算加权占比 this.materialIndicators = { fabricRatio: Math.round( (categoryCount['fabric'] || 0) / total * (categoryConfidence['fabric'] || 0) / (categoryCount['fabric'] || 1) * 100 ), woodRatio: Math.round( (categoryCount['wood'] || 0) / total * (categoryConfidence['wood'] || 0) / (categoryCount['wood'] || 1) * 100 ), metalRatio: Math.round( (categoryCount['metal'] || 0) / total * (categoryConfidence['metal'] || 0) / (categoryCount['metal'] || 1) * 100 ), // 根据材质属性计算平滑度和光泽度 smoothness: this.calculateAverageSmoothness(materials), glossiness: this.calculateAverageGlossiness(materials) }; } calculateAverageSmoothness(materials: MaterialAnalysis[]): number { const textureScores: Record = { 'smooth': 10, 'satin': 7, 'rough': 3, 'woven': 5, 'carved': 2 }; const scores = materials .map(m => textureScores[m.properties.texture] || 5) .filter(s => s > 0); return scores.length > 0 ? Math.round(scores.reduce((sum, s) => sum + s, 0) / scores.length) : 5; } ``` #### 2.4.3 材质分析可视化 **材质卡片网格**: ```html

识别的材质

@if (materialAnalysisData && materialAnalysisData.length > 0) {
@for (material of materialAnalysisData; track material.id) {
{{ material.name }} {{ (material.confidence * 100).toFixed(0) }}%
{{ getMaterialName(material.category) }}
纹理: {{ material.properties.texture }}
表面: {{ material.properties.finish }}
硬度:
适用区域:
@for (area of material.usage.suitableAreas; track area) { {{ area }} }
}
材质分布
木质 {{ materialIndicators?.woodRatio }}%
金属 {{ materialIndicators?.metalRatio }}%
布艺 {{ materialIndicators?.fabricRatio }}%
} @else {
上传材质参考图后将显示AI识别结果
}
``` **纹理对比可视化组件**: ```html ``` ### 2.5 照明需求采集 #### 2.5.1 数据结构 ```typescript interface LightingRequirement { // 照明场景图片 lightingImages: Array<{ id: string; url: string; name: string; }>; // AI光照分析结果 lightingAnalysis?: { naturalLight: { direction: string[]; // 采光方向:north/south/east/west intensity: string; // 光照强度:strong/moderate/weak duration: string; // 日照时长 quality: number; // 光照质量 0-100 }; artificialLight: { mainLighting: { type: string; // 主照明类型:ceiling/chandelier/downlight distribution: string; // 分布方式:uniform/concentrated/layered brightness: number; // 亮度 0-100 }; accentLighting: { type: string; // 重点照明类型:spotlight/wallwash/uplighting locations: string[]; intensity: number; }; ambientLighting: { type: string; // 环境照明类型:cove/indirect/decorative mood: string; // 氛围:warm/cool/neutral colorTemperature: number; // 色温(K) }; }; lightingMood: string; // 整体照明氛围:dramatic/romantic/energetic/calm }; // 映射到设计指标 lightingIndicators?: { naturalLightRatio: number; // 自然光占比 0-1 artificialLightRatio: number; // 人工光占比 0-1 mainLightIntensity: number; // 主光强度 0-100 accentLightIntensity: number; // 辅助光强度 0-100 ambientColorTemp: number; // 环境色温(K) }; } ``` #### 2.5.2 光照分析流程 **图片上传触发分析**: ```typescript onLightingImagesSelected(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const files = Array.from(input.files); this.isAnalyzingLighting = true; // 1. 上传图片 this.uploadFiles(files).subscribe({ next: (uploadedFiles) => { this.lightingImages.push(...uploadedFiles); // 2. 触发AI光照分析 this.analyzeLighting(uploadedFiles.map(f => f.url)); } }); } analyzeLighting(imageUrls: string[]): void { this.lightingAnalysisService.analyzeImages(imageUrls).subscribe({ next: (result) => { this.lightingAnalysis = result; // 映射到设计指标 this.mapLightingToIndicators(result); this.isAnalyzingLighting = false; // 通知父组件 this.requirementDataUpdated.emit({ lightingAnalysis: result, lightingIndicators: this.lightingIndicators }); }, error: (error) => { console.error('光照分析失败:', error); this.isAnalyzingLighting = false; } }); } ``` **光照指标映射**: ```typescript mapLightingToIndicators(analysis: LightingAnalysis): void { if (!analysis) return; // 根据自然光质量和人工光配置计算占比 const naturalQuality = analysis.naturalLight.quality || 50; const artificialBrightness = analysis.artificialLight.mainLighting.brightness || 50; const totalLight = naturalQuality + artificialBrightness; this.lightingIndicators = { naturalLightRatio: naturalQuality / totalLight, artificialLightRatio: artificialBrightness / totalLight, mainLightIntensity: analysis.artificialLight.mainLighting.brightness, accentLightIntensity: analysis.artificialLight.accentLighting.intensity, ambientColorTemp: analysis.artificialLight.ambientLighting.colorTemperature }; } ``` #### 2.5.3 光照分析可视化 **光照信息面板**: ```html

光照分析

@if (lightingAnalysis) {
自然光
采光方向
@for (dir of lightingAnalysis.naturalLight.direction; track dir) { {{ dir }} }
光照强度 {{ lightingAnalysis.naturalLight.intensity }}
日照时长 {{ lightingAnalysis.naturalLight.duration }}
光照质量
{{ lightingAnalysis.naturalLight.quality }}分
人工光
主照明
类型: {{ lightingAnalysis.artificialLight.mainLighting.type }}
分布: {{ lightingAnalysis.artificialLight.mainLighting.distribution }}
亮度:
重点照明
类型: {{ lightingAnalysis.artificialLight.accentLighting.type }}
位置:
@for (loc of lightingAnalysis.artificialLight.accentLighting.locations; track loc) { {{ loc }} }
环境照明
氛围: {{ lightingAnalysis.artificialLight.ambientLighting.mood }}
色温: {{ lightingAnalysis.artificialLight.ambientLighting.colorTemperature }}K
照明氛围
{{ getLightingMoodName(lightingAnalysis.lightingMood) }}
} @else {
上传照明场景图后将显示AI光照分析
}
``` ### 2.6 需求映射总览 #### 2.6.1 需求完成度检查 ```typescript // 检查四大需求是否全部完成 areAllRequirementsCompleted(): boolean { const hasColorData = !!this.colorAnalysisResult || !!this.colorIndicators; const hasSpaceData = !!this.spaceAnalysis || !!this.spaceIndicators; const hasMaterialData = !!this.materialAnalysisData?.length || !!this.materialIndicators; const hasLightingData = !!this.lightingAnalysis || !!this.lightingIndicators; return hasColorData && hasSpaceData && hasMaterialData && hasLightingData; } ``` #### 2.6.2 需求数据汇总 ```typescript // 汇总所有需求数据 getRequirementSummary(): RequirementSummary { return { colorRequirement: { description: this.colorDescription, referenceImages: this.referenceImages, analysisResult: this.colorAnalysisResult, indicators: this.colorIndicators }, spaceRequirement: { cadFiles: this.cadFiles, dimensions: this.manualDimensions, analysisResult: this.spaceAnalysis, indicators: this.spaceIndicators }, materialRequirement: { materialImages: this.materialImages, analysisResult: this.materialAnalysisData, indicators: this.materialIndicators }, lightingRequirement: { lightingImages: this.lightingImages, analysisResult: this.lightingAnalysis, indicators: this.lightingIndicators }, completionRate: this.calculateCompletionRate() }; } calculateCompletionRate(): number { let completed = 0; const total = 4; if (this.colorAnalysisResult) completed++; if (this.spaceAnalysis) completed++; if (this.materialAnalysisData?.length) completed++; if (this.lightingAnalysis) completed++; return Math.round((completed / total) * 100); } ``` #### 2.6.3 需求沟通完成触发 ```typescript // 当所有需求完成后触发 completeRequirementsCommunication(): void { if (!this.areAllRequirementsCompleted()) { alert('请完成所有需求采集项:色彩氛围、空间结构、材质权重、照明需求'); return; } // 1. 保存需求数据 const summary = this.getRequirementSummary(); // 2. 通知父组件推进到方案确认阶段 this.stageCompleted.emit({ stage: 'requirements-communication', allStagesCompleted: true, data: summary }); // 3. 显示成功提示 alert('需求沟通完成!即将进入方案确认阶段'); } ``` ## 3. 方案确认环节 ### 3.1 方案生成逻辑 #### 3.1.1 AI方案生成触发 ```typescript // 基于需求数据生成初步设计方案 generateDesignProposal(): void { if (!this.areRequiredStagesCompleted()) { alert('请先完成需求沟通的所有采集项'); return; } this.isAnalyzing = true; this.analysisProgress = 0; // 模拟方案生成进度 const progressInterval = setInterval(() => { this.analysisProgress += Math.random() * 15; if (this.analysisProgress >= 100) { this.analysisProgress = 100; clearInterval(progressInterval); this.completeProposalGeneration(); } }, 500); } ``` #### 3.1.2 方案数据结构 ```typescript interface ProposalAnalysis { id: string; name: string; version: string; createdAt: Date; status: 'analyzing' | 'completed' | 'approved' | 'rejected'; // 材质方案 materials: MaterialAnalysis[]; // 设计风格 designStyle: { primaryStyle: string; styleElements: Array<{ element: string; description: string; influence: number; // 影响程度 0-100 }>; characteristics: Array<{ feature: string; value: string; importance: 'high' | 'medium' | 'low'; }>; compatibility: { withMaterials: string[]; withColors: string[]; score: number; // 兼容性评分 0-100 }; }; // 色彩方案 colorScheme: { palette: Array<{ color: string; hex: string; rgb: string; percentage: number; role: 'dominant' | 'secondary' | 'accent' | 'neutral'; }>; harmony: { type: string; temperature: 'warm' | 'cool' | 'neutral'; contrast: number; }; psychology: { mood: string; atmosphere: string; suitability: string[]; }; }; // 空间布局 spaceLayout: { dimensions: { length: number; width: number; height: number; area: number; volume: number; }; functionalZones: Array<{ zone: string; area: number; percentage: number; requirements: string[]; furniture: string[]; }>; circulation: { mainPaths: string[]; pathWidth: number; efficiency: number; }; lighting: { natural: { direction: string[]; intensity: string; duration: string; }; artificial: { zones: string[]; requirements: string[]; }; }; }; // 预算方案 budget: { total: number; breakdown: Array<{ category: string; amount: number; percentage: number; }>; }; // 时间规划 timeline: Array<{ phase: string; duration: number; dependencies: string[]; }>; // 可行性评估 feasibility: { technical: number; // 技术可行性 0-100 budget: number; // 预算可行性 0-100 timeline: number; // 时间可行性 0-100 overall: number; // 综合可行性 0-100 }; } ``` #### 3.1.3 方案生成实现(简化示例) ```typescript // project-detail.ts lines 3237-3512 private completeProposalGeneration(): void { this.isAnalyzing = false; // 基于需求指标生成方案 this.proposalAnalysis = { id: 'proposal-' + Date.now(), name: '现代简约风格方案', version: 'v1.0', createdAt: new Date(), status: 'completed', // 材质方案:基于materialIndicators materials: this.generateMaterialProposal(), // 设计风格:基于整体需求 designStyle: this.generateStyleProposal(), // 色彩方案:基于colorIndicators colorScheme: this.generateColorSchemeProposal(), // 空间布局:基于spaceIndicators spaceLayout: this.generateSpaceLayoutProposal(), // 预算方案:基于quotationData budget: this.generateBudgetProposal(), // 时间规划 timeline: this.generateTimelineProposal(), // 可行性评估 feasibility: this.assessFeasibility() }; console.log('方案生成完成:', this.proposalAnalysis); } ``` ### 3.2 方案展示与确认 #### 3.2.1 方案概览面板 ```html

设计方案概览

@if (proposalAnalysis && proposalAnalysis.status === 'completed') {
{{ proposalAnalysis.name }}
{{ proposalAnalysis.version }} {{ formatDate(proposalAnalysis.createdAt) }}
技术可行性
{{ proposalAnalysis.feasibility.technical }}
预算可行性
{{ proposalAnalysis.feasibility.budget }}
时间可行性
{{ proposalAnalysis.feasibility.timeline }}
综合可行性
{{ proposalAnalysis.feasibility.overall }}

材质方案

{{ getMaterialCategories() }}

设计风格

{{ getStyleSummary() }}

色彩方案

{{ getColorSummary() }}

空间效率

{{ getSpaceEfficiency() }}%

} @else if (isAnalyzing) {

AI正在分析需求并生成设计方案...

{{ analysisProgress.toFixed(0) }}%
} @else {

完成需求沟通后可生成设计方案

}
``` #### 3.2.2 方案详情弹窗 ```html
``` ### 3.3 方案确认流程 #### 3.3.1 确认操作 ```typescript // project-detail.ts lines 2577-2585 confirmProposal(): void { console.log('确认方案按钮被点击'); if (!this.proposalAnalysis || this.proposalAnalysis.status !== 'completed') { alert('请先生成设计方案'); return; } // 标记方案为已确认 this.proposalAnalysis.status = 'approved'; // 保存方案数据到项目 this.saveProposalToProject(); // 使用统一的阶段推进方法 this.advanceToNextStage('方案确认'); console.log('已跳转到建模阶段'); } ``` #### 3.3.2 方案数据持久化 ```typescript saveProposalToProject(): void { if (!this.proposalAnalysis) return; const proposalData = { projectId: this.projectId, proposalId: this.proposalAnalysis.id, proposal: this.proposalAnalysis, approvedAt: new Date(), approvedBy: this.getCurrentDesignerName() }; this.projectService.saveProposal(proposalData).subscribe({ next: (result) => { console.log('方案已保存:', result); }, error: (error) => { console.error('方案保存失败:', error); alert('方案保存失败,请重试'); } }); } ``` ## 4. 数据流转与同步 ### 4.1 需求数据流转图 ```mermaid sequenceDiagram participant User as 用户 participant UI as 需求沟通UI participant Service as AnalysisService participant AI as AI引擎 participant Parent as 项目详情页 User->>UI: 上传参考图/CAD UI->>Service: 调用分析接口 Service->>AI: 发送分析请求 AI-->>Service: 返回分析结果 Service-->>UI: 返回结构化数据 UI->>UI: 映射到设计指标 UI->>Parent: emit requirementDataUpdated Parent->>Parent: 更新 requirementKeyInfo Parent->>Parent: 同步到左侧信息面板 ``` ### 4.2 父子组件数据同步 **子组件向父组件传递需求数据**: ```typescript // requirements-confirm-card.component.ts @Output() requirementDataUpdated = new EventEmitter(); // 当任一需求数据更新时触发 onDataUpdated(): void { const data = { colorAnalysisResult: this.colorAnalysisResult, colorIndicators: this.colorIndicators, spaceAnalysis: this.spaceAnalysis, spaceIndicators: this.spaceIndicators, materialAnalysisData: this.materialAnalysisData, materialIndicators: this.materialIndicators, lightingAnalysis: this.lightingAnalysis, lightingIndicators: this.lightingIndicators, detailedAnalysis: { enhancedColorAnalysis: this.enhancedColorAnalysis, formAnalysis: this.formAnalysis, textureAnalysis: this.textureAnalysis, patternAnalysis: this.patternAnalysis, lightingAnalysis: this.lightingAnalysis }, materials: [ ...this.referenceImages.map(img => ({ ...img, type: 'image' })), ...this.cadFiles.map(file => ({ ...file, type: 'cad' })) ] }; this.requirementDataUpdated.emit(data); } ``` **父组件接收并处理**: ```typescript // project-detail.ts lines 3071-3187 onRequirementDataUpdated(data: any): void { console.log('收到需求数据更新:', data); // 1. 同步关键信息到左侧面板 this.syncRequirementKeyInfo(data); // 2. 更新项目信息显示 this.updateProjectInfoFromRequirementData(data); } private syncRequirementKeyInfo(requirementData: any): void { if (requirementData) { // 同步色彩氛围信息 if (requirementData.colorIndicators) { this.requirementKeyInfo.colorAtmosphere = { description: requirementData.colorIndicators.colorRange || '', mainColor: `rgb(${requirementData.colorIndicators.mainColor?.r || 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.detailedAnalysis) { this.enhancedColorAnalysis = requirementData.detailedAnalysis.enhancedColorAnalysis; this.formAnalysis = requirementData.detailedAnalysis.formAnalysis; this.textureAnalysis = requirementData.detailedAnalysis.textureAnalysis; this.patternAnalysis = requirementData.detailedAnalysis.patternAnalysis; this.lightingAnalysis = requirementData.detailedAnalysis.lightingAnalysis; } // 拆分参考图片和CAD文件 const materials = Array.isArray(requirementData?.materials) ? requirementData.materials : []; this.referenceImages = materials.filter((m: any) => m?.type === 'image'); this.cadFiles = materials.filter((m: any) => m?.type === 'cad'); // 触发变更检测 this.cdr.detectChanges(); } } ``` ### 4.3 左侧信息面板实时更新 **需求关键信息展示**(project-detail.html lines 350-450): ```html

需求关键信息

色彩氛围
@if (requirementKeyInfo.colorAtmosphere.description) {

{{ requirementKeyInfo.colorAtmosphere.description }}

主色 {{ requirementKeyInfo.colorAtmosphere.colorTemp }}
} @else {

待采集

}
空间结构
@if (requirementKeyInfo.spaceStructure.aspectRatio > 0) {
空间比例: {{ requirementKeyInfo.spaceStructure.aspectRatio.toFixed(1) }}
层高: {{ requirementKeyInfo.spaceStructure.ceilingHeight }}m
线条占比: {{ (requirementKeyInfo.spaceStructure.lineRatio * 100).toFixed(0) }}%
留白占比: {{ (requirementKeyInfo.spaceStructure.blankRatio * 100).toFixed(0) }}%
} @else {

待采集

}
材质权重
@if (requirementKeyInfo.materialWeights.woodRatio > 0 || requirementKeyInfo.materialWeights.fabricRatio > 0 || requirementKeyInfo.materialWeights.metalRatio > 0) {
木质
{{ requirementKeyInfo.materialWeights.woodRatio }}%
布艺
{{ requirementKeyInfo.materialWeights.fabricRatio }}%
金属
{{ requirementKeyInfo.materialWeights.metalRatio }}%
} @else {

待采集

}
预设氛围
@if (requirementKeyInfo.presetAtmosphere.name) {

{{ requirementKeyInfo.presetAtmosphere.name }}

色温:{{ requirementKeyInfo.presetAtmosphere.colorTemp }} 主材:{{ requirementKeyInfo.presetAtmosphere.materials.join('、') }}
} @else {

待采集

}
``` ## 5. 权限控制 ### 5.1 需求确认阶段权限矩阵 | 操作 | 客服 | 设计师 | 组长 | 技术 | |-----|------|--------|------|------| | 查看需求沟通 | ✅ | ✅ | ✅ | ✅ | | 上传参考图 | ✅ | ✅ | ✅ | ❌ | | 上传CAD文件 | ✅ | ✅ | ✅ | ❌ | | 触发AI分析 | ✅ | ✅ | ✅ | ❌ | | 手动编辑指标 | ❌ | ✅ | ✅ | ❌ | | 生成设计方案 | ❌ | ✅ | ✅ | ❌ | | 确认方案 | ❌ | ✅ | ✅ | ❌ | | 推进到建模阶段 | ❌ | ✅ | ✅ | ❌ | ### 5.2 权限控制实现 **组件级别**: ```html ``` **操作级别**: ```typescript generateDesignProposal(): void { // 检查权限 if (!this.canEditStage('方案确认')) { alert('您没有权限生成设计方案'); return; } // 检查前置条件 if (!this.areRequiredStagesCompleted()) { alert('请先完成需求沟通的所有采集项'); return; } // 执行方案生成 this.isAnalyzing = true; // ... } ``` ## 6. 异常处理 ### 6.1 文件上传失败 ```typescript uploadFiles(files: File[]): Observable { const formData = new FormData(); files.forEach(file => formData.append('files', file)); return this.http.post('/api/upload', formData).pipe( catchError(error => { let errorMessage = '文件上传失败'; if (error.status === 413) { errorMessage = '文件过大,请上传小于10MB的文件'; } else if (error.status === 415) { errorMessage = '文件格式不支持'; } else if (error.status === 500) { errorMessage = '服务器错误,请稍后重试'; } return throwError(() => new Error(errorMessage)); }) ); } ``` ### 6.2 AI分析失败 ```typescript triggerColorAnalysis(imageUrl: string): void { this.isAnalyzingColors = true; this.colorAnalysisService.analyzeImage(imageUrl).pipe( retry(2), // 失败后重试2次 timeout(30000), // 30秒超时 catchError(error => { this.isAnalyzingColors = false; let errorMessage = 'AI色彩分析失败'; if (error.name === 'TimeoutError') { errorMessage = '分析超时,请稍后重试'; } else if (error.status === 400) { errorMessage = '图片格式不符合要求'; } alert(errorMessage); return of(null); }) ).subscribe({ next: (result) => { if (result) { this.colorAnalysisResult = result; // ... } } }); } ``` ### 6.3 方案生成失败 ```typescript generateDesignProposal(): void { this.isAnalyzing = true; this.analysisProgress = 0; // 设置超时保护 const timeout = setTimeout(() => { if (this.isAnalyzing) { this.isAnalyzing = false; alert('方案生成超时,请重试'); } }, 60000); // 60秒超时 // 模拟生成进度 const progressInterval = setInterval(() => { this.analysisProgress += Math.random() * 15; if (this.analysisProgress >= 100) { this.analysisProgress = 100; clearInterval(progressInterval); clearTimeout(timeout); try { this.completeProposalGeneration(); } catch (error) { console.error('方案生成失败:', error); alert('方案生成失败,请重试'); this.isAnalyzing = false; } } }, 500); } ``` ## 7. 性能优化 ### 7.1 图片懒加载 ```typescript // 使用Intersection Observer实现图片懒加载 @ViewChild('imageContainer') imageContainer?: ElementRef; ngAfterViewInit(): void { if ('IntersectionObserver' in window) { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target as HTMLImageElement; const src = img.dataset['src']; if (src) { img.src = src; observer.unobserve(img); } } }); }); const images = this.imageContainer?.nativeElement.querySelectorAll('img[data-src]'); images?.forEach((img: HTMLImageElement) => observer.observe(img)); } } ``` ### 7.2 分析结果缓存 ```typescript // ColorAnalysisService with caching private analysisCache = new Map(); analyzeImage(imageUrl: string): Observable { // 检查缓存 const cached = this.analysisCache.get(imageUrl); if (cached) { console.log('使用缓存的分析结果'); return of(cached); } // 调用API分析 return this.http.post('/api/analyze/color', { imageUrl }).pipe( tap(result => { // 缓存结果(限制缓存大小) if (this.analysisCache.size >= 50) { const firstKey = this.analysisCache.keys().next().value; this.analysisCache.delete(firstKey); } this.analysisCache.set(imageUrl, result); }) ); } ``` ### 7.3 大文件分片上传 ```typescript uploadLargeFile(file: File): Observable { const chunkSize = 1024 * 1024; // 1MB per chunk const chunks = Math.ceil(file.size / chunkSize); const uploadProgress$ = new Subject(); let uploadedChunks = 0; for (let i = 0; i < chunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('chunk', chunk); formData.append('chunkIndex', i.toString()); formData.append('totalChunks', chunks.toString()); formData.append('fileName', file.name); this.http.post('/api/upload/chunk', formData).subscribe({ next: () => { uploadedChunks++; uploadProgress$.next({ progress: (uploadedChunks / chunks) * 100, status: 'uploading' }); if (uploadedChunks === chunks) { uploadProgress$.next({ progress: 100, status: 'completed' }); uploadProgress$.complete(); } }, error: (error) => { uploadProgress$.error(error); } }); } return uploadProgress$.asObservable(); } ``` ## 8. 测试用例 ### 8.1 需求采集流程测试 ```typescript describe('Requirements Collection Flow', () => { it('should complete all four requirement types', async () => { // 1. 上传色彩参考图 await uploadFile('color-reference.jpg', 'colorInput'); expect(component.colorAnalysisResult).toBeTruthy(); // 2. 上传CAD文件 await uploadFile('floor-plan.dwg', 'cadInput'); expect(component.spaceAnalysis).toBeTruthy(); // 3. 上传材质图片 await uploadFile('material-ref.jpg', 'materialInput'); expect(component.materialAnalysisData.length).toBeGreaterThan(0); // 4. 上传照明场景图 await uploadFile('lighting-scene.jpg', 'lightingInput'); expect(component.lightingAnalysis).toBeTruthy(); // 5. 验证完成度 expect(component.areAllRequirementsCompleted()).toBeTruthy(); }); }); ``` ### 8.2 方案生成测试 ```typescript describe('Proposal Generation', () => { it('should generate design proposal based on requirements', async () => { // 准备需求数据 component.colorIndicators = mockColorIndicators; component.spaceIndicators = mockSpaceIndicators; component.materialIndicators = mockMaterialIndicators; component.lightingIndicators = mockLightingIndicators; // 触发方案生成 component.generateDesignProposal(); // 等待生成完成 await waitForCondition(() => component.proposalAnalysis !== null); // 验证方案数据 expect(component.proposalAnalysis.status).toBe('completed'); expect(component.proposalAnalysis.materials.length).toBeGreaterThan(0); expect(component.proposalAnalysis.designStyle).toBeTruthy(); expect(component.proposalAnalysis.colorScheme).toBeTruthy(); expect(component.proposalAnalysis.spaceLayout).toBeTruthy(); expect(component.proposalAnalysis.feasibility.overall).toBeGreaterThan(70); }); }); ``` ### 8.3 阶段推进测试 ```typescript describe('Stage Progression', () => { it('should advance from requirements to proposal to modeling', async () => { // 1. 完成需求沟通 component.completeRequirementsCommunication(); expect(component.currentStage).toBe('方案确认'); // 2. 生成并确认方案 component.generateDesignProposal(); await waitForCondition(() => component.proposalAnalysis !== null); component.confirmProposal(); expect(component.currentStage).toBe('建模'); expect(component.expandedStages['建模']).toBeTruthy(); }); }); ``` --- **文档版本**:v1.0.0 **创建日期**:2025-10-16 **最后更新**:2025-10-16 **维护人**:产品团队