Browse Source

feat(project-detail): 实现需求映射功能及相关界面更新

- 在项目详情页面中新增需求映射面板,支持上传图片并实时分析需求映射结果。
- 更新需求确认卡片,添加需求映射进度显示和文件上传功能。
- 优化样式以提升用户体验,包括需求映射面板的视觉效果和交互反馈。
- 增强数据处理逻辑,支持多图片上传和分析,确保用户能够顺畅进行需求映射。

此更新旨在提升项目管理的效率和用户交互的流畅性。
0235711 1 week ago
parent
commit
f763ef49f2

+ 798 - 0
copy/color-analysis.service.ts

@@ -0,0 +1,798 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, BehaviorSubject, throwError, forkJoin, of } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+import { FormAnalysisService } from './form-analysis.service';
+import { TextureAnalysisService } from './texture-analysis.service';
+import { PatternAnalysisService } from './pattern-analysis.service';
+import { LightingAnalysisService } from './lighting-analysis.service';
+
+export interface UploadedFile {
+  id: string;
+  name: string;
+  url: string;
+  size?: number;
+  type?: string;
+  preview?: string;
+}
+
+export interface ColorAnalysisResult {
+  colors: Array<{
+    hex: string;
+    rgb: { r: number; g: number; b: number };
+    percentage: number;
+    name?: string; // 添加可选的name属性
+  }>;
+  originalImage: string;
+  mosaicImage: string;
+  reportPath: string;
+  // 新增增强分析结果
+  enhancedAnalysis?: EnhancedColorAnalysis;
+  // 新增其他分析结果
+  formAnalysis?: any;
+  textureAnalysis?: any;
+  patternAnalysis?: any;
+  lightingAnalysis?: any;
+}
+
+// 新增:增强色彩分析结果接口
+export interface EnhancedColorAnalysis {
+  colorWheel: ColorWheelData;
+  colorHarmony: ColorHarmonyAnalysis;
+  colorTemperature: ColorTemperatureAnalysis;
+  colorPsychology: ColorPsychologyAnalysis;
+}
+
+// 色轮数据
+export interface ColorWheelData {
+  dominantHue: number; // 主色调角度 (0-360)
+  saturationRange: { min: number; max: number }; // 饱和度范围
+  brightnessRange: { min: number; max: number }; // 亮度范围
+  colorDistribution: Array<{
+    hue: number;
+    saturation: number;
+    brightness: number;
+    percentage: number;
+  }>;
+}
+
+// 色彩和谐度分析
+export interface ColorHarmonyAnalysis {
+  harmonyType: 'monochromatic' | 'analogous' | 'complementary' | 'triadic' | 'tetradic' | 'split-complementary';
+  harmonyScore: number; // 0-100
+  suggestions: string[];
+  relationships: Array<{
+    color1: string;
+    color2: string;
+    relationship: string;
+    strength: number;
+  }>;
+}
+
+// 色温详细分析
+export interface ColorTemperatureAnalysis {
+  averageTemperature: number; // 开尔文值
+  temperatureRange: { min: number; max: number };
+  warmCoolBalance: number; // -100(冷) 到 100(暖)
+  temperatureDescription: string;
+  lightingRecommendations: string[];
+}
+
+// 色彩心理学分析
+export interface ColorPsychologyAnalysis {
+  mood: string; // 整体情绪
+  atmosphere: string; // 氛围描述
+  psychologicalEffects: string[];
+  suitableSpaces: string[]; // 适合的空间类型
+  emotionalImpact: {
+    energy: number; // 0-100
+    warmth: number; // 0-100
+    sophistication: number; // 0-100
+    comfort: number; // 0-100
+  };
+}
+
+export interface AnalysisProgress {
+  stage: 'preparing' | 'processing' | 'extracting' | 'generating' | 'completed' | 'error';
+  message: string;
+  progress: number;
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ColorAnalysisService {
+  private readonly API_BASE = '/api/color-analysis';
+  private analysisProgress$ = new BehaviorSubject<AnalysisProgress>({
+    stage: 'preparing',
+    message: '准备分析...',
+    progress: 0
+  });
+
+  constructor(
+    private http: HttpClient,
+    private formAnalysisService: FormAnalysisService,
+    private textureAnalysisService: TextureAnalysisService,
+    private patternAnalysisService: PatternAnalysisService,
+    private lightingAnalysisService: LightingAnalysisService
+  ) {}
+
+  /**
+   * 获取分析进度
+   */
+  getAnalysisProgress(): Observable<AnalysisProgress> {
+    return this.analysisProgress$.asObservable();
+  }
+
+  /**
+   * 分析图片颜色
+   * @param imageFile 图片文件
+   * @param options 分析选项
+   */
+  analyzeImageColors(imageFile: File, options?: {
+    mosaicSize?: number;
+    maxColors?: number;
+  }): Observable<ColorAnalysisResult> {
+    const formData = new FormData();
+    formData.append('image', imageFile);
+    
+    if (options?.mosaicSize) {
+      formData.append('mosaicSize', options.mosaicSize.toString());
+    }
+    if (options?.maxColors) {
+      formData.append('maxColors', options.maxColors.toString());
+    }
+
+    return this.http.post<any>(`${this.API_BASE}/analyze`, formData).pipe(
+      map((response: any) => {
+        if (response && response.success) {
+          return this.parseAnalysisResult(response.data);
+        }
+        throw new Error('分析结果无效');
+      }),
+      catchError(error => {
+        console.error('颜色分析失败:', error);
+        return throwError(() => new Error('颜色分析服务暂时不可用'));
+      })
+    );
+  }
+
+  /**
+   * 分析上传的图片文件
+   * @param file 上传的文件信息
+   */
+  analyzeImage(file: UploadedFile): Observable<ColorAnalysisResult> {
+    // 暂时使用模拟分析,避免API 404错误
+    console.log('使用模拟分析处理文件:', file.name);
+    
+    // 创建一个File对象用于模拟分析
+    return new Observable(observer => {
+      // 如果有预览URL,尝试获取文件
+      if (file.preview || file.url) {
+        fetch(file.preview || file.url)
+          .then(response => response.blob())
+          .then(blob => {
+            const mockFile = new File([blob], file.name, { type: file.type || 'image/jpeg' });
+            this.simulateAnalysis(mockFile).subscribe({
+              next: (result) => observer.next(result),
+              error: (error) => observer.error(error),
+              complete: () => observer.complete()
+            });
+          })
+          .catch(error => {
+            console.warn('无法获取文件内容,使用默认模拟数据:', error);
+            // 如果无法获取文件,直接返回模拟结果
+            this.getDefaultMockResult(file).subscribe({
+              next: (result) => observer.next(result),
+              error: (error) => observer.error(error),
+              complete: () => observer.complete()
+            });
+          });
+      } else {
+        // 没有文件URL,直接返回模拟结果
+        this.getDefaultMockResult(file).subscribe({
+          next: (result) => observer.next(result),
+          error: (error) => observer.error(error),
+          complete: () => observer.complete()
+        });
+      }
+    });
+  }
+
+  /**
+   * 获取默认模拟结果
+   */
+  private getDefaultMockResult(file: UploadedFile): Observable<ColorAnalysisResult> {
+    return new Observable(observer => {
+      this.updateProgress('processing', '开始分析...', 10);
+      
+      setTimeout(() => {
+        this.updateProgress('extracting', '提取颜色信息...', 50);
+        
+        setTimeout(() => {
+          this.updateProgress('generating', '生成分析报告...', 80);
+          
+          setTimeout(() => {
+            const mockResult: ColorAnalysisResult = {
+              colors: [
+                { hex: '#FF6B6B', rgb: { r: 255, g: 107, b: 107 }, percentage: 25.5, name: '珊瑚红' },
+                { hex: '#4ECDC4', rgb: { r: 78, g: 205, b: 196 }, percentage: 18.3, name: '青绿色' },
+                { hex: '#45B7D1', rgb: { r: 69, g: 183, b: 209 }, percentage: 15.7, name: '天蓝色' },
+                { hex: '#96CEB4', rgb: { r: 150, g: 206, b: 180 }, percentage: 12.1, name: '薄荷绿' },
+                { hex: '#FFEAA7', rgb: { r: 255, g: 234, b: 167 }, percentage: 10.8, name: '柠檬黄' },
+                { hex: '#DDA0DD', rgb: { r: 221, g: 160, b: 221 }, percentage: 8.9, name: '紫罗兰' },
+                { hex: '#98D8C8', rgb: { r: 152, g: 216, b: 200 }, percentage: 8.7, name: '海泡石绿' }
+              ],
+              originalImage: file.preview || file.url || '/assets/images/placeholder.jpg',
+              mosaicImage: file.preview || file.url || '/assets/images/placeholder.jpg',
+              reportPath: '/mock-report.html',
+              enhancedAnalysis: this.performEnhancedColorAnalysis([
+                { hex: '#FF6B6B', rgb: { r: 255, g: 107, b: 107 }, percentage: 25.5 },
+                { hex: '#4ECDC4', rgb: { r: 78, g: 205, b: 196 }, percentage: 18.3 },
+                { hex: '#45B7D1', rgb: { r: 69, g: 183, b: 209 }, percentage: 15.7 },
+                { hex: '#96CEB4', rgb: { r: 150, g: 206, b: 180 }, percentage: 12.1 },
+                { hex: '#FFEAA7', rgb: { r: 255, g: 234, b: 167 }, percentage: 10.8 }
+              ])
+            };
+
+            this.updateProgress('completed', '分析完成', 100);
+            observer.next(mockResult);
+            observer.complete();
+          }, 300);
+        }, 400);
+      }, 300);
+    });
+  }
+
+  /**
+   * 获取分析报告
+   * @param reportId 报告ID
+   */
+  getAnalysisReport(reportId: string): Observable<string> {
+    return this.http.get(`${this.API_BASE}/report/${reportId}`, {
+      responseType: 'text'
+    });
+  }
+
+  /**
+   * 批量分析多个图片
+   * @param imageFiles 图片文件数组
+   */
+  analyzeBatchImages(imageFiles: File[]): Observable<ColorAnalysisResult[]> {
+    this.updateProgress('preparing', '准备批量分析...', 0);
+
+    const formData = new FormData();
+    imageFiles.forEach((file, index) => {
+      formData.append(`images`, file);
+    });
+
+    return this.http.post<any>(`${this.API_BASE}/analyze-batch`, formData).pipe(
+      map(response => {
+        this.updateProgress('completed', '批量分析完成', 100);
+        return response.results.map((result: any) => this.parseAnalysisResult(result));
+      }),
+      catchError(error => {
+        this.updateProgress('error', '批量分析失败: ' + (error.message || '未知错误'), 0);
+        return throwError(() => error);
+      })
+    );
+  }
+
+  /**
+   * 检查color-get服务状态
+   */
+  checkServiceStatus(): Observable<boolean> {
+    return this.http.get<{ status: string }>(`${this.API_BASE}/status`).pipe(
+      map(response => response.status === 'ready'),
+      catchError(() => throwError(() => new Error('颜色分析服务不可用')))
+    );
+  }
+
+  /**
+   * 增强色彩分析 - 包含色轮、和谐度、色温、心理学分析
+   * @param colors 基础颜色分析结果
+   */
+  performEnhancedColorAnalysis(colors: Array<{hex: string; rgb: {r: number; g: number; b: number}; percentage: number}>): EnhancedColorAnalysis {
+    return {
+      colorWheel: this.analyzeColorWheel(colors),
+      colorHarmony: this.analyzeColorHarmony(colors),
+      colorTemperature: this.analyzeColorTemperature(colors),
+      colorPsychology: this.analyzeColorPsychology(colors)
+    };
+  }
+
+  /**
+   * 色轮分析
+   */
+  private analyzeColorWheel(colors: Array<{hex: string; rgb: {r: number; g: number; b: number}; percentage: number}>): ColorWheelData {
+    const colorDistribution = colors.map(color => {
+      const hsl = this.rgbToHsl(color.rgb.r, color.rgb.g, color.rgb.b);
+      return {
+        hue: hsl.h,
+        saturation: hsl.s,
+        brightness: hsl.l,
+        percentage: color.percentage
+      };
+    });
+
+    const dominantColor = colorDistribution.reduce((prev, current) => 
+      prev.percentage > current.percentage ? prev : current
+    );
+
+    const saturationValues = colorDistribution.map(c => c.saturation);
+    const brightnessValues = colorDistribution.map(c => c.brightness);
+
+    return {
+      dominantHue: dominantColor.hue,
+      saturationRange: {
+        min: Math.min(...saturationValues),
+        max: Math.max(...saturationValues)
+      },
+      brightnessRange: {
+        min: Math.min(...brightnessValues),
+        max: Math.max(...brightnessValues)
+      },
+      colorDistribution
+    };
+  }
+
+  /**
+   * 色彩和谐度分析
+   */
+  private analyzeColorHarmony(colors: Array<{hex: string; rgb: {r: number; g: number; b: number}; percentage: number}>): ColorHarmonyAnalysis {
+    const hues = colors.map(color => {
+      const hsl = this.rgbToHsl(color.rgb.r, color.rgb.g, color.rgb.b);
+      return { hue: hsl.h, hex: color.hex, percentage: color.percentage };
+    });
+
+    // 分析色彩关系
+    const relationships = [];
+    for (let i = 0; i < hues.length; i++) {
+      for (let j = i + 1; j < hues.length; j++) {
+        const hueDiff = Math.abs(hues[i].hue - hues[j].hue);
+        const minDiff = Math.min(hueDiff, 360 - hueDiff);
+        
+        let relationship = '';
+        let strength = 0;
+        
+        if (minDiff < 30) {
+          relationship = '相似色';
+          strength = 90 - minDiff;
+        } else if (minDiff > 150 && minDiff < 210) {
+          relationship = '互补色';
+          strength = 100 - Math.abs(minDiff - 180);
+        } else if (minDiff > 110 && minDiff < 130) {
+          relationship = '三角色';
+          strength = 100 - Math.abs(minDiff - 120);
+        }
+
+        if (relationship) {
+          relationships.push({
+            color1: hues[i].hex,
+            color2: hues[j].hex,
+            relationship,
+            strength
+          });
+        }
+      }
+    }
+
+    // 确定和谐类型
+    let harmonyType: ColorHarmonyAnalysis['harmonyType'] = 'monochromatic';
+    let harmonyScore = 60;
+
+    if (relationships.some(r => r.relationship === '互补色' && r.strength > 80)) {
+      harmonyType = 'complementary';
+      harmonyScore = 85;
+    } else if (relationships.filter(r => r.relationship === '相似色').length >= 2) {
+      harmonyType = 'analogous';
+      harmonyScore = 75;
+    } else if (relationships.some(r => r.relationship === '三角色')) {
+      harmonyType = 'triadic';
+      harmonyScore = 80;
+    }
+
+    return {
+      harmonyType,
+      harmonyScore,
+      relationships,
+      suggestions: this.generateHarmonySuggestions(harmonyType, harmonyScore)
+    };
+  }
+
+  /**
+   * 色温分析
+   */
+  private analyzeColorTemperature(colors: Array<{hex: string; rgb: {r: number; g: number; b: number}; percentage: number}>): ColorTemperatureAnalysis {
+    const temperatures = colors.map(color => {
+      // 简化的色温计算
+      const temp = this.calculateColorTemperature(color.rgb);
+      return { temperature: temp, percentage: color.percentage };
+    });
+
+    const weightedAverage = temperatures.reduce((sum, t) => sum + t.temperature * t.percentage, 0) / 100;
+    const tempValues = temperatures.map(t => t.temperature);
+    
+    const warmCoolBalance = this.calculateWarmCoolBalance(colors);
+    
+    return {
+      averageTemperature: Math.round(weightedAverage),
+      temperatureRange: {
+        min: Math.min(...tempValues),
+        max: Math.max(...tempValues)
+      },
+      warmCoolBalance,
+      temperatureDescription: this.getTemperatureDescription(weightedAverage),
+      lightingRecommendations: this.generateLightingRecommendations(weightedAverage, warmCoolBalance)
+    };
+  }
+
+  /**
+   * 色彩心理学分析
+   */
+  private analyzeColorPsychology(colors: Array<{hex: string; rgb: {r: number; g: number; b: number}; percentage: number}>): ColorPsychologyAnalysis {
+    const psychologyData = colors.map(color => {
+      const psychology = this.getColorPsychology(color.hex);
+      return { ...psychology, percentage: color.percentage };
+    });
+
+    // 计算加权平均情感影响
+    const emotionalImpact = {
+      energy: Math.round(psychologyData.reduce((sum, p) => sum + p.energy * p.percentage, 0) / 100),
+      warmth: Math.round(psychologyData.reduce((sum, p) => sum + p.warmth * p.percentage, 0) / 100),
+      sophistication: Math.round(psychologyData.reduce((sum, p) => sum + p.sophistication * p.percentage, 0) / 100),
+      comfort: Math.round(psychologyData.reduce((sum, p) => sum + p.comfort * p.percentage, 0) / 100)
+    };
+
+    return {
+      mood: this.determineMood(emotionalImpact),
+      atmosphere: this.determineAtmosphere(emotionalImpact),
+      psychologicalEffects: this.aggregatePsychologicalEffects(psychologyData),
+      suitableSpaces: this.determineSuitableSpaces(emotionalImpact),
+      emotionalImpact
+    };
+  }
+
+  // 辅助方法
+  private rgbToHsl(r: number, g: number, b: number): {h: number, s: number, l: number} {
+    r /= 255; g /= 255; b /= 255;
+    const max = Math.max(r, g, b), min = Math.min(r, g, b);
+    let h = 0, s = 0, l = (max + min) / 2;
+
+    if (max !== min) {
+      const d = max - min;
+      s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+      switch (max) {
+        case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+        case g: h = (b - r) / d + 2; break;
+        case b: h = (r - g) / d + 4; break;
+      }
+      h /= 6;
+    }
+
+    return { h: h * 360, s: s * 100, l: l * 100 };
+  }
+
+  private calculateColorTemperature(rgb: {r: number, g: number, b: number}): number {
+    // 简化的色温计算公式
+    const ratio = rgb.b / (rgb.r + rgb.g + rgb.b);
+    return Math.round(2000 + ratio * 4000); // 2000K-6000K范围
+  }
+
+  private calculateWarmCoolBalance(colors: Array<{hex: string; rgb: {r: number; g: number; b: number}; percentage: number}>): number {
+    let warmScore = 0;
+    colors.forEach(color => {
+      const { r, g, b } = color.rgb;
+      const warmness = (r - b) / 255 * 100; // 红色减蓝色的比例
+      warmScore += warmness * color.percentage / 100;
+    });
+    return Math.max(-100, Math.min(100, warmScore));
+  }
+
+  private getTemperatureDescription(temp: number): string {
+    if (temp < 3000) return '暖色调';
+    if (temp < 4000) return '中性偏暖';
+    if (temp < 5000) return '中性色调';
+    if (temp < 6000) return '中性偏冷';
+    return '冷色调';
+  }
+
+  private generateLightingRecommendations(temp: number, balance: number): string[] {
+    const recommendations = [];
+    if (temp < 3500) {
+      recommendations.push('适合使用暖白光照明(2700K-3000K)');
+      recommendations.push('营造温馨舒适氛围');
+    } else if (temp > 5000) {
+      recommendations.push('适合使用冷白光照明(5000K-6500K)');
+      recommendations.push('营造清爽现代的感觉');
+    } else {
+      recommendations.push('适合使用中性白光照明(4000K-4500K)');
+      recommendations.push('平衡温暖与清爽的感觉');
+    }
+    return recommendations;
+  }
+
+  private generateHarmonySuggestions(harmonyType: string, score: number): string[] {
+    const suggestions = [];
+    if (score < 70) {
+      suggestions.push('考虑调整色彩比例以提高和谐度');
+      suggestions.push('可以添加中性色作为过渡');
+    }
+    
+    switch (harmonyType) {
+      case 'complementary':
+        suggestions.push('互补色搭配,建议一主一辅的比例');
+        break;
+      case 'analogous':
+        suggestions.push('相似色搭配,可以添加少量对比色增加活力');
+        break;
+      case 'triadic':
+        suggestions.push('三角色搭配,注意控制各色彩的饱和度');
+        break;
+    }
+    
+    return suggestions;
+  }
+
+  private getColorPsychology(hex: string): {energy: number, warmth: number, sophistication: number, comfort: number} {
+    // 基于色相的心理学属性(简化版)
+    const rgb = this.hexToRgb(hex);
+    if (!rgb) return { energy: 50, warmth: 50, sophistication: 50, comfort: 50 };
+    
+    const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
+    const hue = hsl.h;
+    
+    // 根据色相确定心理属性
+    if (hue >= 0 && hue < 60) { // 红-橙
+      return { energy: 85, warmth: 90, sophistication: 60, comfort: 70 };
+    } else if (hue >= 60 && hue < 120) { // 黄-绿
+      return { energy: 75, warmth: 70, sophistication: 50, comfort: 80 };
+    } else if (hue >= 120 && hue < 180) { // 绿-青
+      return { energy: 45, warmth: 30, sophistication: 70, comfort: 85 };
+    } else if (hue >= 180 && hue < 240) { // 青-蓝
+      return { energy: 35, warmth: 20, sophistication: 85, comfort: 75 };
+    } else if (hue >= 240 && hue < 300) { // 蓝-紫
+      return { energy: 55, warmth: 40, sophistication: 90, comfort: 65 };
+    } else { // 紫-红
+      return { energy: 70, warmth: 60, sophistication: 80, comfort: 60 };
+    }
+  }
+
+  private hexToRgb(hex: string): {r: number, g: number, b: number} | null {
+    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+    return result ? {
+      r: parseInt(result[1], 16),
+      g: parseInt(result[2], 16),
+      b: parseInt(result[3], 16)
+    } : null;
+  }
+
+  private determineMood(impact: {energy: number, warmth: number, sophistication: number, comfort: number}): string {
+    if (impact.energy > 70 && impact.warmth > 70) return '活力温暖';
+    if (impact.sophistication > 80) return '优雅精致';
+    if (impact.comfort > 80) return '舒适宁静';
+    if (impact.energy > 70) return '充满活力';
+    return '平和稳定';
+  }
+
+  private determineAtmosphere(impact: {energy: number, warmth: number, sophistication: number, comfort: number}): string {
+    if (impact.warmth > 70 && impact.comfort > 70) return '温馨舒适的家居氛围';
+    if (impact.sophistication > 80 && impact.energy < 50) return '高雅静谧的商务氛围';
+    if (impact.energy > 70) return '活跃动感的现代氛围';
+    return '平衡和谐的中性氛围';
+  }
+
+  private aggregatePsychologicalEffects(data: any[]): string[] {
+    const effects = new Set<string>();
+    data.forEach(d => {
+      if (d.energy > 70) effects.add('提升活力和注意力');
+      if (d.warmth > 70) effects.add('营造温暖亲切感');
+      if (d.sophistication > 80) effects.add('增强空间品质感');
+      if (d.comfort > 80) effects.add('促进放松和舒适感');
+    });
+    return Array.from(effects);
+  }
+
+  private determineSuitableSpaces(impact: {energy: number, warmth: number, sophistication: number, comfort: number}): string[] {
+    const spaces = [];
+    if (impact.warmth > 70 && impact.comfort > 70) {
+      spaces.push('客厅', '卧室', '餐厅');
+    }
+    if (impact.sophistication > 80) {
+      spaces.push('办公室', '会议室', '接待区');
+    }
+    if (impact.energy > 70) {
+      spaces.push('工作区', '健身房', '娱乐区');
+    }
+    if (impact.comfort > 80 && impact.energy < 50) {
+      spaces.push('休息区', '阅读角', '冥想室');
+    }
+    return spaces.length > 0 ? spaces : ['通用空间'];
+  }
+
+  /**
+   * 更新分析进度
+   */
+  private updateProgress(stage: AnalysisProgress['stage'], message: string, progress: number): void {
+    this.analysisProgress$.next({ stage, message, progress });
+  }
+
+  /**
+   * 解析分析结果
+   */
+  private parseAnalysisResult(data: any): ColorAnalysisResult {
+    return {
+      colors: data.colors || [],
+      originalImage: data.originalImage || '',
+      mosaicImage: data.mosaicImage || '',
+      reportPath: data.reportPath || ''
+    };
+  }
+
+  /**
+   * 模拟color-get分析过程(用于开发测试)
+   */
+  simulateAnalysis(imageFile: File): Observable<ColorAnalysisResult> {
+    return new Observable(observer => {
+      this.updateProgress('processing', '开始分析...', 10);
+      
+      setTimeout(() => {
+        this.updateProgress('extracting', '提取颜色信息...', 30);
+        
+        setTimeout(() => {
+          this.updateProgress('generating', '生成分析报告...', 70);
+          
+          // 使用forkJoin来并行处理所有分析服务,添加错误处理
+          const analysisObservables = {
+            formAnalysis: this.formAnalysisService.analyzeImageForm(imageFile).pipe(
+              catchError(error => {
+                console.warn('形体分析失败,使用默认值:', error);
+                return of(null);
+              })
+            ),
+            textureAnalysis: this.textureAnalysisService.analyzeImageTexture(imageFile).pipe(
+              catchError(error => {
+                console.warn('质感分析失败,使用默认值:', error);
+                return of(null);
+              })
+            ),
+            patternAnalysis: this.patternAnalysisService.analyzeImagePattern(imageFile).pipe(
+              catchError(error => {
+                console.warn('纹理分析失败,使用默认值:', error);
+                return of(null);
+              })
+            ),
+            lightingAnalysis: this.lightingAnalysisService.analyzeImageLighting(imageFile).pipe(
+              catchError(error => {
+                console.warn('灯光分析失败,使用默认值:', error);
+                return of(null);
+              })
+            )
+          };
+
+          forkJoin(analysisObservables).subscribe({
+            next: (analysisResults) => {
+              const mockResult: ColorAnalysisResult = {
+                colors: [
+                  { hex: '#FF6B6B', rgb: { r: 255, g: 107, b: 107 }, percentage: 25.5 },
+                  { hex: '#4ECDC4', rgb: { r: 78, g: 205, b: 196 }, percentage: 18.3 },
+                  { hex: '#45B7D1', rgb: { r: 69, g: 183, b: 209 }, percentage: 15.7 },
+                  { hex: '#96CEB4', rgb: { r: 150, g: 206, b: 180 }, percentage: 12.1 },
+                  { hex: '#FFEAA7', rgb: { r: 255, g: 234, b: 167 }, percentage: 10.8 },
+                  { hex: '#DDA0DD', rgb: { r: 221, g: 160, b: 221 }, percentage: 8.9 },
+                  { hex: '#98D8C8', rgb: { r: 152, g: 216, b: 200 }, percentage: 8.7 }
+                ],
+                originalImage: URL.createObjectURL(imageFile),
+                mosaicImage: URL.createObjectURL(imageFile), // 在实际应用中这里应该是处理后的图片
+                reportPath: '/mock-report.html',
+                // 添加增强色彩分析
+                enhancedAnalysis: this.performEnhancedColorAnalysis([
+                  { hex: '#FF6B6B', rgb: { r: 255, g: 107, b: 107 }, percentage: 25.5 },
+                  { hex: '#4ECDC4', rgb: { r: 78, g: 205, b: 196 }, percentage: 18.3 },
+                  { hex: '#45B7D1', rgb: { r: 69, g: 183, b: 209 }, percentage: 15.7 },
+                  { hex: '#96CEB4', rgb: { r: 150, g: 206, b: 180 }, percentage: 12.1 },
+                  { hex: '#FFEAA7', rgb: { r: 255, g: 234, b: 167 }, percentage: 10.8 }
+                ]),
+                // 添加其他分析结果,如果分析失败则使用默认值
+                formAnalysis: analysisResults.formAnalysis || this.getDefaultFormAnalysis(),
+                textureAnalysis: analysisResults.textureAnalysis || this.getDefaultTextureAnalysis(),
+                patternAnalysis: analysisResults.patternAnalysis || this.getDefaultPatternAnalysis(),
+                lightingAnalysis: analysisResults.lightingAnalysis || this.getDefaultLightingAnalysis()
+              };
+
+              this.updateProgress('completed', '分析完成', 100);
+              observer.next(mockResult);
+              observer.complete();
+            },
+            error: (error) => {
+              console.error('分析服务出错:', error);
+              // 即使分析服务出错,也返回基本的颜色分析结果
+              const fallbackResult: ColorAnalysisResult = {
+                colors: [
+                  { hex: '#FF6B6B', rgb: { r: 255, g: 107, b: 107 }, percentage: 25.5, name: '珊瑚红' },
+                  { hex: '#4ECDC4', rgb: { r: 78, g: 205, b: 196 }, percentage: 18.3, name: '青绿色' },
+                  { hex: '#45B7D1', rgb: { r: 69, g: 183, b: 209 }, percentage: 15.7, name: '天蓝色' }
+                ],
+                originalImage: URL.createObjectURL(imageFile),
+                mosaicImage: URL.createObjectURL(imageFile),
+                reportPath: '/mock-report.html',
+                enhancedAnalysis: this.performEnhancedColorAnalysis([
+                  { hex: '#FF6B6B', rgb: { r: 255, g: 107, b: 107 }, percentage: 25.5 },
+                  { hex: '#4ECDC4', rgb: { r: 78, g: 205, b: 196 }, percentage: 18.3 },
+                  { hex: '#45B7D1', rgb: { r: 69, g: 183, b: 209 }, percentage: 15.7 }
+                ]),
+                formAnalysis: this.getDefaultFormAnalysis(),
+                textureAnalysis: this.getDefaultTextureAnalysis(),
+                patternAnalysis: this.getDefaultPatternAnalysis(),
+                lightingAnalysis: this.getDefaultLightingAnalysis()
+              };
+              
+              this.updateProgress('completed', '分析完成(部分功能降级)', 100);
+              observer.next(fallbackResult);
+              observer.complete();
+            }
+          });
+        }, 500);
+      }, 300);
+    });
+  }
+
+  /**
+   * 获取默认形体分析结果
+   */
+  private getDefaultFormAnalysis(): any {
+    return {
+      spaceAnalysis: {
+        spaceType: '现代简约'
+      },
+      lineAnalysis: {
+        dominantLineType: 'straight',
+        lineDirection: { horizontal: 60, vertical: 30, diagonal: 10 }
+      }
+    };
+  }
+
+  /**
+   * 获取默认质感分析结果
+   */
+  private getDefaultTextureAnalysis(): any {
+    return {
+      materialClassification: {
+        primary: '光滑表面'
+      },
+      surfaceProperties: {
+        roughness: { level: 'smooth', value: 30 },
+        glossiness: { level: 'satin', value: 50 }
+      }
+    };
+  }
+
+  /**
+   * 获取默认纹理分析结果
+   */
+  private getDefaultPatternAnalysis(): any {
+    return {
+      patternRecognition: {
+        primaryPatterns: [
+          { type: 'geometric', confidence: 70, coverage: 40 }
+        ],
+        patternComplexity: { level: 'moderate', score: 50 }
+      }
+    };
+  }
+
+  /**
+   * 获取默认灯光分析结果
+   */
+  private getDefaultLightingAnalysis(): any {
+    return {
+      ambientAnalysis: {
+        lightingMood: '温馨舒适'
+      },
+      illuminationAnalysis: {
+        brightness: { overall: 70 },
+        colorTemperature: { kelvin: 3000, warmth: 'warm' }
+      }
+    };
+  }
+}

+ 5283 - 0
copy/project-detail.ts

@@ -0,0 +1,5283 @@
+import { Component, OnInit, OnDestroy, ChangeDetectorRef } 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 { PaymentVoucherRecognitionService } from '../../../services/payment-voucher-recognition.service';
+import { ProjectReviewService, ReviewReportExportRequest, ReviewReportShareRequest } from '../../../services/project-review.service';
+import {
+  Project,
+  RenderProgress,
+  CustomerFeedback,
+  DesignerChange,
+  Settlement,
+  ProjectStage,
+  PanoramicSynthesis,
+  ModelCheckItem
+} from '../../../models/project.model';
+import { RequirementsConfirmCardComponent } from '../../../shared/components/requirements-confirm-card/requirements-confirm-card';
+import { SettlementCardComponent } from '../../../shared/components/settlement-card/settlement-card';
+import { CustomerReviewCardComponent, DetailedCustomerReview } from '../../../shared/components/customer-review-card/customer-review-card';
+import { CustomerReviewFormComponent } from '../../../shared/components/customer-review-form/customer-review-form';
+import { ComplaintCardComponent } from '../../../shared/components/complaint-card/complaint-card';
+import { PanoramicSynthesisCardComponent } from '../../../shared/components/panoramic-synthesis-card/panoramic-synthesis-card';
+import { QuotationDetailsComponent, QuotationData } from './components/quotation-details/quotation-details.component';
+import { DesignerAssignmentComponent, DesignerAssignmentData, Designer as AssignmentDesigner } from './components/designer-assignment/designer-assignment.component';
+// 引入客户服务模块的设计师日历组件
+import { DesignerCalendarComponent, Designer as CalendarDesigner, ProjectGroup as CalendarProjectGroup } from '../../customer-service/consultation-order/components/designer-calendar/designer-calendar.component';
+
+import { ColorAnalysisResult } from '../../../shared/services/color-analysis.service';
+
+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';
+
+// 素材解析后的方案数据结构
+interface MaterialAnalysis {
+  category: string;
+  specifications: {
+    type: string;
+    grade: string;
+    thickness?: string;
+    finish?: string;
+    durability: string;
+  };
+  usage: {
+    area: string;
+    percentage: number;
+    priority: 'primary' | 'secondary' | 'accent';
+  };
+  properties: {
+    texture: string;
+    color: string;
+    maintenance: string;
+  };
+}
+
+interface DesignStyleAnalysis {
+  primaryStyle: string;
+  styleElements: {
+    element: string;
+    description: string;
+    influence: number; // 影响程度 0-100
+  }[];
+  characteristics: {
+    feature: string;
+    value: string;
+    importance: 'high' | 'medium' | 'low';
+  }[];
+  compatibility: {
+    withMaterials: string[];
+    withColors: string[];
+    score: number; // 兼容性评分 0-100
+  };
+}
+
+interface ColorSchemeAnalysis {
+  palette: {
+    color: string;
+    hex: string;
+    rgb: string;
+    percentage: number;
+    role: 'dominant' | 'secondary' | 'accent' | 'neutral';
+  }[];
+  harmony: {
+    type: string; // 如:互补色、类似色、三角色等
+    temperature: 'warm' | 'cool' | 'neutral';
+    contrast: number; // 对比度 0-100
+  };
+  psychology: {
+    mood: string;
+    atmosphere: string;
+    suitability: string[];
+  };
+}
+
+interface SpaceAnalysis {
+  dimensions: {
+    length: number;
+    width: number;
+    height: number;
+    area: number;
+    volume: number;
+  };
+  functionalZones: {
+    zone: string;
+    area: number;
+    percentage: number;
+    requirements: string[];
+    furniture: string[];
+  }[];
+  circulation: {
+    mainPaths: string[];
+    pathWidth: number;
+    efficiency: number; // 动线效率 0-100
+  };
+  lighting: {
+    natural: {
+      direction: string[];
+      intensity: string;
+      duration: string;
+    };
+    artificial: {
+      zones: string[];
+      requirements: string[];
+    };
+  };
+}
+
+// 新增:项目复盘数据结构
+interface ProjectReview {
+  id: string;
+  projectId: string;
+  generatedAt: Date;
+  overallScore: number; // 项目总评分 0-100
+  sopAnalysis: {
+    stageName: string;
+    plannedDuration: number; // 计划天数
+    actualDuration: number; // 实际天数
+    score: number; // 阶段评分 0-100
+    executionStatus: 'excellent' | 'good' | 'average' | 'poor';
+    issues?: string[]; // 问题列表
+  }[];
+  keyHighlights: string[]; // 项目亮点
+  improvementSuggestions: string[]; // 改进建议
+  customerSatisfaction: {
+    overallRating: number; // 1-5星
+    feedback?: string; // 客户反馈
+    responseTime: number; // 响应时间(小时)
+    completionTime: number; // 完成时间(天)
+  };
+  teamPerformance: {
+    designerScore: number;
+    communicationScore: number;
+    timelinessScore: number;
+    qualityScore: number;
+  };
+  budgetAnalysis: {
+    plannedBudget: number;
+    actualBudget: number;
+    variance: number; // 预算偏差百分比
+    costBreakdown: {
+      category: string;
+      planned: number;
+      actual: number;
+    }[];
+  };
+  lessonsLearned: string[]; // 经验教训
+  recommendations: string[]; // 后续项目建议
+}
+
+interface ProposalAnalysis {
+  id: string;
+  name: string;
+  version: string;
+  createdAt: Date;
+  status: 'analyzing' | 'completed' | 'approved' | 'rejected';
+  materials: MaterialAnalysis[];
+  designStyle: DesignStyleAnalysis;
+  colorScheme: ColorSchemeAnalysis;
+  spaceLayout: SpaceAnalysis;
+  budget: {
+    total: number;
+    breakdown: {
+      category: string;
+      amount: number;
+      percentage: number;
+    }[];
+  };
+  timeline: {
+    phase: string;
+    duration: number;
+    dependencies: string[];
+  }[];
+  feasibility: {
+    technical: number; // 技术可行性 0-100
+    budget: number; // 预算可行性 0-100
+    timeline: number; // 时间可行性 0-100
+    overall: number; // 综合可行性 0-100
+  };
+}
+
+// 交付执行板块数据结构
+interface DeliverySpace {
+  id: string;
+  name: string; // 空间名称:卧室、餐厅、厨房等
+  isExpanded: boolean; // 是否展开
+  order: number; // 排序
+}
+
+interface DeliveryProcess {
+  id: string;
+  name: string; // 流程名称:建模、软装、渲染、后期
+  type: 'modeling' | 'softDecor' | 'rendering' | 'postProcess';
+  spaces: DeliverySpace[]; // 该流程下的空间列表
+  isExpanded: boolean; // 是否展开
+  content: {
+    [spaceId: string]: {
+      // 每个空间的具体内容
+      images: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected' }>;
+      progress: number; // 进度百分比
+      status: 'pending' | 'in_progress' | 'completed' | 'approved';
+      notes: string; // 备注
+      lastUpdated: Date;
+    };
+  };
+}
+
+@Component({
+  selector: 'app-project-detail',
+  standalone: true,
+  imports: [CommonModule, FormsModule, ReactiveFormsModule, RequirementsConfirmCardComponent, SettlementCardComponent, CustomerReviewCardComponent, CustomerReviewFormComponent, ComplaintCardComponent, PanoramicSynthesisCardComponent, QuotationDetailsComponent, DesignerAssignmentComponent, DesignerCalendarComponent],
+  templateUrl: './project-detail.html',
+  styleUrls: ['./project-detail.scss', './debug-styles.scss', './horizontal-panel.scss']
+})
+export class ProjectDetail implements OnInit, OnDestroy {
+  // 项目基本数据
+  projectId: string = '';
+  project: Project | undefined;
+  renderProgress: RenderProgress | undefined;
+  feedbacks: CustomerFeedback[] = [];
+  detailedReviews: DetailedCustomerReview[] = [];
+  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 = '';
+  
+  // 新增:尾款结算完成状态
+  isSettlementCompleted: boolean = false;
+  isConfirmingSettlement: boolean = false;
+  isSettlementInitiated: boolean = false;
+
+  // 新增:自动化功能状态跟踪
+  miniprogramPaymentStatus: 'active' | 'inactive' = 'inactive';
+  voucherRecognitionCount: number = 0;
+  notificationsSent: number = 0;
+  isAutoSettling: boolean = false;
+  isPaymentVerified: boolean = false;
+  
+  // 客户信息卡片展开/收起状态
+  isCustomerInfoExpanded: boolean = false;
+  
+  // 新增:订单创建表单相关
+  orderCreationForm!: FormGroup;
+  optionalForm!: FormGroup;
+  isOptionalFormExpanded: boolean = false;
+  orderCreationData: any = null;
+  
+  // 新增:方案分析相关数据
+  proposalAnalysis: ProposalAnalysis | null = null;
+  isAnalyzing: boolean = false;
+  analysisProgress: number = 0;
+  // 新增:右侧色彩分析结果展示
+  colorAnalysisResult: ColorAnalysisResult | null = null;
+  dominantColorHex: string | null = null;
+  // 新增:区分展示的参考图片与CAD文件(来源于需求确认子组件)
+  referenceImages: any[] = [];
+  cadFiles: any[] = [];
+  
+  // 新增:详细分析数据属性
+  enhancedColorAnalysis: any = null;
+  formAnalysis: any = null;
+  textureAnalysis: any = null;
+  patternAnalysis: any = null;
+  lightingAnalysis: any = null;
+  materialAnalysisData: any[] = [];
+  
+  // 新增:9阶段顺序(串式流程)- 包含后期阶段
+  stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价', '投诉处理'];
+  // 新增:阶段展开状态(默认全部收起,当前阶段在数据加载后自动展开)
+  expandedStages: Partial<Record<ProjectStage, boolean>> = {
+    '订单创建': 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: Array<{ id: 'progress' | 'members' | 'files'; name: string }> = [
+    { 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[] = [];
+
+  // 团队分配弹窗相关
+  selectedDesigner: any = null;
+  projectData: any = null;
+
+  // ============ 阶段图片上传状态(新增) ============
+  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; locked?: boolean; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
+  postProcessImages: 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 }> = [];
+
+  // 视图上下文:根据路由前缀识别角色视角(客服/设计师/组长)
+  roleContext: 'customer-service' | 'designer' | 'team-leader' | 'technical' = 'designer';
+
+  // ============ 模型检查项数据 ============
+  modelCheckItems: ModelCheckItem[] = [
+    { id: 'check-1', name: '户型匹配度检查', isPassed: false, notes: '' },
+    { id: 'check-2', name: '尺寸精度验证', isPassed: false, notes: '' },
+    { id: 'check-3', name: '材质贴图检查', isPassed: false, notes: '' },
+    { id: 'check-4', name: '光影效果验证', isPassed: false, notes: '' },
+    { id: 'check-5', name: '细节完整性检查', isPassed: false, notes: '' }
+  ];
+
+  // ============ 交付执行板块数据 ============
+  deliveryProcesses: DeliveryProcess[] = [
+    {
+      id: 'modeling',
+      name: '建模',
+      type: 'modeling',
+      isExpanded: true, // 默认展开第一个
+      spaces: [
+        { id: 'bedroom', name: '卧室', isExpanded: false, order: 1 },
+        { id: 'living', name: '客厅', isExpanded: false, order: 2 },
+        { id: 'kitchen', name: '厨房', isExpanded: false, order: 3 }
+      ],
+      content: {
+        'bedroom': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'living': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'kitchen': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() }
+      }
+    },
+    {
+      id: 'softDecor',
+      name: '软装',
+      type: 'softDecor',
+      isExpanded: false,
+      spaces: [
+        { id: 'bedroom', name: '卧室', isExpanded: false, order: 1 },
+        { id: 'living', name: '客厅', isExpanded: false, order: 2 },
+        { id: 'kitchen', name: '厨房', isExpanded: false, order: 3 }
+      ],
+      content: {
+        'bedroom': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'living': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'kitchen': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() }
+      }
+    },
+    {
+      id: 'rendering',
+      name: '渲染',
+      type: 'rendering',
+      isExpanded: false,
+      spaces: [
+        { id: 'bedroom', name: '卧室', isExpanded: false, order: 1 },
+        { id: 'living', name: '客厅', isExpanded: false, order: 2 },
+        { id: 'kitchen', name: '厨房', isExpanded: false, order: 3 }
+      ],
+      content: {
+        'bedroom': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'living': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'kitchen': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() }
+      }
+    },
+    {
+      id: 'postProcess',
+      name: '后期',
+      type: 'postProcess',
+      isExpanded: false,
+      spaces: [
+        { id: 'bedroom', name: '卧室', isExpanded: false, order: 1 },
+        { id: 'living', name: '客厅', isExpanded: false, order: 2 },
+        { id: 'kitchen', name: '厨房', isExpanded: false, order: 3 }
+      ],
+      content: {
+        'bedroom': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'living': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() },
+        'kitchen': { images: [], progress: 0, status: 'pending', notes: '', lastUpdated: new Date() }
+      }
+    }
+  ];
+
+  // 新增空间输入框状态
+  newSpaceName: { [processId: string]: string } = {};
+  showAddSpaceInput: { [processId: string]: boolean } = {};
+
+  constructor(
+    private route: ActivatedRoute,
+    private projectService: ProjectService,
+    private router: Router,
+    private fb: FormBuilder,
+    private cdr: ChangeDetectorRef,
+    private paymentVoucherService: PaymentVoucherRecognitionService,
+    private projectReviewService: ProjectReviewService
+  ) {}
+
+  // 切换标签页
+  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 } });
+  }
+
+  // 检查是否处于订单创建阶段,用于红色高亮显示
+  isCurrentOrderCreation(): boolean {
+    // 只有当订单创建阶段是当前活跃阶段时才标红显示
+    // 修复逻辑:完成前序环节后才标红当前阶段
+    const currentStage = this.project?.currentStage;
+    if (!currentStage) return false;
+    
+    // 如果当前阶段就是订单创建阶段,说明需要标红显示
+    if (currentStage === '订单创建') return true;
+    
+    // 如果当前阶段在订单创建之后,说明订单创建已完成,不应标红
+    const stageOrder = [
+      '订单创建', '需求沟通', '方案确认', '建模', '软装', 
+      '渲染', '尾款结算', '客户评价', '投诉处理'
+    ];
+    
+    const currentIndex = stageOrder.indexOf(currentStage);
+    const orderCreationIndex = stageOrder.indexOf('订单创建');
+    
+    // 如果当前阶段在订单创建之后,说明订单创建已完成
+    return currentIndex <= orderCreationIndex;
+  }
+
+  // 返回工作台
+  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;
+    // 优先使用 currentStage 属性,如果没有则使用 project.currentStage
+    const current = (this.currentStage as ProjectStage) || (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.initializeForms();
+    
+    // 初始化需求关键信息数据
+    this.ensureRequirementData();
+    
+    // 重置方案分析状态,确保需求信息展示区域能够显示
+    this.resetProposalAnalysis();
+    
+    // 初始化售后模块示例数据
+    this.initializeAftercareData();
+    
+    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();
+      
+      // 启动客户信息自动同步
+      this.startAutoSync();
+    });
+
+    // 新增:监听查询参数,支持通过 activeTab 设置初始标签页和 currentStage 设置当前阶段
+    this.route.queryParamMap.subscribe(qp => {
+      const raw = qp.get('activeTab');
+      const alias: Record<string, 'progress' | 'members' | 'files'> = {
+        requirements: 'progress',
+        overview: 'progress'
+      };
+      const tab = raw && (raw in alias ? alias[raw] : raw);
+      if (tab === 'progress' || tab === 'members' || tab === 'files') {
+        this.activeTab = tab;
+      }
+      
+      // 处理 currentStage 查询参数
+      const currentStageParam = qp.get('currentStage');
+      if (currentStageParam) {
+        this.currentStage = currentStageParam;
+        
+        // 根据当前阶段设置项目状态和展开相应区域
+        this.initializeStageFromRoute(currentStageParam as ProjectStage);
+        
+        // 根据当前阶段自动展开对应的区域
+        const sectionKey = this.getSectionKeyForStage(currentStageParam as ProjectStage);
+        if (sectionKey) {
+          this.expandedSection = sectionKey;
+        }
+        
+        // 延迟滚动到对应阶段
+        setTimeout(() => {
+          this.scrollToStage(currentStageParam as ProjectStage);
+        }, 500);
+      }
+
+      // 处理从客服项目列表传递的同步数据
+      const syncDataParam = qp.get('syncData');
+      if (syncDataParam) {
+        try {
+          const syncData = JSON.parse(syncDataParam);
+          console.log('接收到客服同步数据:', syncData);
+          
+          // 设置同步状态
+          this.isSyncingCustomerInfo = true;
+          
+          // 存储订单创建数据用于显示
+          this.orderCreationData = syncData;
+          
+          // 更新projectData以传递给子组件
+          this.projectData = {
+            customerInfo: syncData.customerInfo,
+            requirementInfo: syncData.requirementInfo,
+            preferenceTags: syncData.preferenceTags
+          };
+          
+          // 同步客户信息到表单
+          if (syncData.customerInfo) {
+            this.customerForm.patchValue({
+              name: syncData.customerInfo.name || '',
+              phone: syncData.customerInfo.phone || '',
+              wechat: syncData.customerInfo.wechat || '',
+              customerType: syncData.customerInfo.customerType || '新客户',
+              source: syncData.customerInfo.source || '小程序',
+              remark: syncData.customerInfo.remark || '',
+              demandType: syncData.customerInfo.demandType || '',
+              followUpStatus: syncData.customerInfo.followUpStatus || '待分配'
+            });
+
+            // 设置选中的客户
+            this.selectedOrderCustomer = {
+              id: syncData.customerInfo.id || 'temp-' + Date.now(),
+              name: syncData.customerInfo.name,
+              phone: syncData.customerInfo.phone,
+              wechat: syncData.customerInfo.wechat,
+              customerType: syncData.customerInfo.customerType,
+              source: syncData.customerInfo.source,
+              remark: syncData.customerInfo.remark
+            };
+          }
+
+          // 同步需求信息
+          if (syncData.requirementInfo) {
+            this.syncRequirementKeyInfo(syncData.requirementInfo);
+          }
+
+          // 同步偏好标签到项目数据
+          if (syncData.preferenceTags && this.project) {
+            this.project.customerTags = syncData.preferenceTags.map((tag: string) => ({
+              source: '客服填写',
+              needType: tag,
+              preference: tag,
+              colorAtmosphere: tag
+            }));
+          }
+
+          // 模拟同步完成
+          setTimeout(() => {
+            this.isSyncingCustomerInfo = false;
+            this.lastSyncTime = new Date();
+            this.cdr.detectChanges();
+            console.log('客户信息同步完成');
+          }, 1500);
+
+          // 触发界面更新
+          this.cdr.detectChanges();
+          
+          console.log('客服数据同步完成,orderCreationData:', this.orderCreationData);
+        } catch (error) {
+          console.error('解析同步数据失败:', error);
+          this.isSyncingCustomerInfo = false;
+        }
+      }
+    });
+    
+    // 添加点击事件监听器,当点击页面其他位置时关闭下拉菜单
+    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'
+    });
+  }
+
+  // 新增:根据路由参数初始化阶段状态
+  private initializeStageFromRoute(targetStage: ProjectStage): void {
+    // 设置当前阶段
+    this.currentStage = targetStage;
+    
+    // 根据目标阶段设置之前的阶段为已完成
+    const targetIndex = this.stageOrder.indexOf(targetStage);
+    if (targetIndex > 0) {
+      // 将目标阶段之前的所有阶段设置为已完成
+      for (let i = 0; i < targetIndex; i++) {
+        const stage = this.stageOrder[i];
+        this.expandedStages[stage] = false; // 已完成的阶段收起
+      }
+    }
+    
+    // 展开当前阶段
+    this.expandedStages[targetStage] = true;
+    
+    // 如果项目对象存在,更新项目的当前阶段
+    if (this.project) {
+      this.project.currentStage = targetStage;
+    }
+    
+    // 触发变更检测以更新UI
+    this.cdr.detectChanges();
+  }
+  ngOnDestroy(): void {
+    if (this.countdownInterval) {
+      clearInterval(this.countdownInterval);
+    }
+    
+    // 停止自动同步
+    this.stopAutoSync();
+    
+    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' | 'technical' {
+    const url = this.router.url || '';
+    
+    // 首先检查查询参数中的role
+    const queryParams = this.route.snapshot.queryParamMap;
+    const roleParam = queryParams.get('role');
+    if (roleParam === 'customer-service') {
+      return 'customer-service';
+    }
+    if (roleParam === 'technical') {
+      return 'technical';
+    }
+    
+    // 如果没有role查询参数,则根据URL路径判断
+    if (url.includes('/customer-service/')) return 'customer-service';
+    if (url.includes('/team-leader/')) return 'team-leader';
+    if (url.includes('/technical/')) return 'technical';
+    return 'designer';
+  }
+
+  isDesignerView(): boolean { return this.roleContext === 'designer'; }
+  isTeamLeaderView(): boolean { return this.roleContext === 'team-leader'; }
+  isCustomerServiceView(): boolean { return this.roleContext === 'customer-service'; }
+  isTechnicalView(): boolean { return this.roleContext === 'technical'; }
+  // 只读规则:客服视角为只读
+  isReadOnly(): boolean { return this.isCustomerServiceView(); }
+
+  // 权限控制:客服只能编辑订单创建、确认需求、售后板块
+  canEditSection(sectionKey: SectionKey): boolean {
+    if (this.isCustomerServiceView()) {
+      return sectionKey === 'order' || sectionKey === 'requirements' || sectionKey === 'aftercare';
+    }
+    return true; // 设计师和组长可以编辑所有板块
+  }
+
+  // 权限控制:客服只能编辑特定阶段
+  canEditStage(stage: ProjectStage): boolean {
+    if (this.isCustomerServiceView()) {
+      const editableStages: ProjectStage[] = [
+        '订单创建', '需求沟通', '方案确认', // 订单创建和确认需求板块
+        '尾款结算', '客户评价', '投诉处理' // 售后板块
+      ];
+      return editableStages.includes(stage);
+    }
+    return true; // 设计师和组长可以编辑所有阶段
+  }
+
+  // 计算当前激活板块:优先用户点击的 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' | 'postProcess'): 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);
+    if (phase === 'postProcess') markSynced(this.postProcessImages);
+    alert('已同步该阶段的图片信息(模拟)');
+  }
+
+  reviewImage(imageId: string, phase: 'white' | 'soft' | 'render' | 'postProcess', 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);
+    if (phase === 'postProcess') setStatus(this.postProcessImages);
+  }
+
+  getImageReviewStatusText(img: { reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }): string {
+    const synced = img.synced ? '已同步' : '未同步';
+    const map: Record<string, string> = {
+      '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.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 {
+    console.log('=== loadProjectDetails 开始加载项目数据 ===');
+    console.log('当前项目ID:', this.projectId);
+    this.projectService.getProjectById(this.projectId).subscribe(project => {
+      console.log('获取到的项目数据:', project);
+      if (!project) {
+        console.error('未找到项目数据,项目ID:', this.projectId);
+        // 如果找不到项目,尝试使用默认项目ID
+        console.log('尝试使用默认项目ID: proj-001');
+        this.projectService.getProjectById('proj-001').subscribe(defaultProject => {
+          console.log('默认项目数据:', defaultProject);
+          if (defaultProject) {
+            this.project = defaultProject;
+            this.currentStage = defaultProject.currentStage || '';
+            console.log('使用默认项目,设置当前阶段:', this.currentStage);
+            this.setupStageExpansion(defaultProject);
+          }
+        });
+        return;
+      }
+      
+      this.project = project;
+      // 设置当前阶段
+      if (project) {
+        this.currentStage = project.currentStage || '';
+        console.log('设置当前阶段:', this.currentStage);
+        this.setupStageExpansion(project);
+      }
+      // 检查技能匹配度 - 已注释掉以取消弹窗警告
+      // this.checkSkillMismatch();
+    });
+  }
+
+  private setupStageExpansion(project: any): void {
+    // 重置展开状态并默认展开当前阶段
+    this.stageOrder.forEach(s => this.expandedStages[s] = false);
+    const currentStage = project.currentStage as ProjectStage;
+    if (this.stageOrder.includes(currentStage)) {
+      this.expandedStages[currentStage] = true;
+      console.log('展开当前阶段:', currentStage);
+    }
+    // 新增:根据当前阶段默认展开所属板块
+    const currentSec = this.getSectionKeyForStage(currentStage);
+    this.expandedSection = currentSec;
+    console.log('展开板块:', currentSec);
+    
+    // 新增:如果当前阶段是建模、软装或渲染,自动展开对应的折叠面板
+    if (currentStage === '建模' || currentStage === '软装' || currentStage === '渲染') {
+      const processTypeMap = {
+        '建模': 'modeling',
+        '软装': 'softDecor', 
+        '渲染': 'rendering'
+      };
+      const processType = processTypeMap[currentStage] as 'modeling' | 'softDecor' | 'rendering';
+      const targetProcess = this.deliveryProcesses.find(p => p.type === processType);
+      if (targetProcess) {
+        // 展开对应的流程面板
+        targetProcess.isExpanded = true;
+        // 展开第一个空间以便用户操作
+        if (targetProcess.spaces.length > 0) {
+          targetProcess.spaces[0].isExpanded = true;
+          // 关闭其他空间
+          targetProcess.spaces.slice(1).forEach(space => space.isExpanded = false);
+        }
+        console.log('自动展开折叠面板:', currentStage, processType);
+      }
+    }
+  }
+  
+  // 整理项目详情
+  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);
+  }
+
+  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;
+    });
+  }
+
+  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.currentStage = stage; // 同步更新本地状态
+        this.project!.currentStage = stage; // 同步更新project对象的currentStage
+        this.loadProjectDetails(); // 重新加载项目详情
+        this.cdr.detectChanges(); // 触发变更检测以更新导航栏颜色
+      });
+    }
+  }
+
+  // 新增:根据给定阶段跳转到下一阶段
+  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;
+      // 更新板块展开状态
+      const nextSection = this.getSectionKeyForStage(next);
+      this.expandedSection = nextSection;
+      
+      // 新增:自动展开对应阶段的折叠面板
+      if (next === '软装' || next === '渲染') {
+        const processType = next === '软装' ? 'softDecor' : 'rendering';
+        const targetProcess = this.deliveryProcesses.find(p => p.type === processType);
+        if (targetProcess) {
+          // 展开对应的流程面板
+          targetProcess.isExpanded = true;
+          // 展开第一个空间以便用户操作
+          if (targetProcess.spaces.length > 0) {
+            targetProcess.spaces[0].isExpanded = true;
+            // 关闭其他空间
+            targetProcess.spaces.slice(1).forEach(space => space.isExpanded = false);
+          }
+        }
+      }
+      
+      // 触发变更检测以更新导航栏颜色
+      this.cdr.detectChanges();
+    }
+  }
+  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<ProjectStage, '待分配' | '需求方案' | '项目执行' | '收尾验收' | '归档'> = {
+      '订单创建': '待分配',
+      '需求沟通': '需求方案',
+      '方案确认': '需求方案',
+      '建模': '项目执行',
+      '软装': '项目执行',
+      '渲染': '项目执行',
+      '后期': '项目执行',
+      '尾款结算': '收尾验收',
+      '客户评价': '收尾验收',
+      '投诉处理': '收尾验收'
+    };
+    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<void> {
+    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<boolean> {
+    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();
+  }
+
+  // 检查是否所有模型检查项都已通过
+
+
+  // 获取技能匹配度警告
+  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<string, string> = {
+      '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 {
+    // 检查建模阶段的图片数据
+    const modelingProcess = this.deliveryProcesses.find(p => p.id === 'modeling');
+    if (!modelingProcess) return;
+    
+    // 检查是否有任何空间上传了图片
+    const hasImages = modelingProcess.spaces.some(space => {
+      const content = modelingProcess.content[space.id];
+      return content && content.images && content.images.length > 0;
+    });
+    
+    if (!hasImages) 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 {
+    const isRenderLarge = !!this.renderLargeImages.find(i => i.id === img?.id);
+    if (isRenderLarge && img?.locked) {
+      alert('该渲染大图已加锁,需完成尾款结算并上传/识别支付凭证后方可预览。');
+      return;
+    }
+    this.previewImageData = img;
+    this.showImagePreview = true;
+  }
+
+  closeImagePreview(): void {
+    this.showImagePreview = false;
+    this.previewImageData = null;
+  }
+
+  downloadImage(img: any): void {
+    const isRenderLarge = !!this.renderLargeImages.find(i => i.id === img?.id);
+    if (isRenderLarge && img?.locked) {
+      alert('该渲染大图已加锁,需完成尾款结算并上传/识别支付凭证后方可下载。');
+      return;
+    }
+    if (img) {
+      const link = document.createElement('a');
+      link.href = img.url;
+      link.download = img.name;
+      link.click();
+    }
+  }
+
+  removeImageFromPreview(): void {
+    if (this.previewImageData) {
+      // 首先检查新的 deliveryProcesses 结构
+      let imageFound = false;
+      
+      for (const process of this.deliveryProcesses) {
+        for (const space of process.spaces) {
+          if (process.content[space.id]?.images) {
+            const imageIndex = process.content[space.id].images.findIndex(img => img.id === this.previewImageData.id);
+            if (imageIndex > -1) {
+              this.removeSpaceImage(process.id, space.id, this.previewImageData.id);
+              imageFound = true;
+              break;
+            }
+          }
+        }
+        if (imageFound) break;
+      }
+      
+      // 如果在新结构中没找到,检查旧的图片数组
+      if (!imageFound) {
+        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);
+        } else if (this.postProcessImages.find(i => i.id === this.previewImageData.id)) {
+          this.removePostProcessImage(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' | 'postProcess'): 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;
+      case 'postProcess':
+        this.onPostProcessPicsSelected(mockEvent);
+        break;
+    }
+  }
+
+  // 触发文件输入框
+  triggerFileInput(type: 'whiteModel' | 'softDecor' | 'render' | 'postProcess'): void {
+    let inputId: string;
+    switch (type) {
+      case 'whiteModel':
+        inputId = 'whiteModelFileInput';
+        break;
+      case 'softDecor':
+        inputId = 'softDecorFileInput';
+        break;
+      case 'render':
+        inputId = 'renderFileInput';
+        break;
+      case 'postProcess':
+        inputId = 'postProcessFileInput';
+        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);
+  }
+
+  // 新增:后期阶段图片上传处理
+  async onPostProcessPicsSelected(event: Event): Promise<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));
+
+    for (const f of files) {
+      const item = this.makeImageItem(f);
+      this.postProcessImages.unshift({ 
+        id: item.id, 
+        name: item.name, 
+        url: item.url, 
+        size: this.formatFileSize(f.size) 
+      });
+    }
+    input.value = '';
+  }
+
+  removePostProcessImage(id: string): void {
+    const target = this.postProcessImages.find(i => i.id === id);
+    if (target) this.revokeUrl(target.url);
+    this.postProcessImages = this.postProcessImages.filter(i => i.id !== id);
+  }
+
+  // 新增:后期阶段 确认上传并自动进入下一阶段(尾款结算)
+  confirmPostProcessUpload(): void {
+    // 检查后期阶段的图片数据
+    const postProcessProcess = this.deliveryProcesses.find(p => p.id === 'post-processing');
+    if (!postProcessProcess) return;
+    
+    // 检查是否有任何空间上传了图片
+    const hasImages = postProcessProcess.spaces.some(space => {
+      const content = postProcessProcess.content[space.id];
+      return content && content.images && content.images.length > 0;
+    });
+    
+    if (!hasImages) return;
+    this.advanceToNextStage('后期');
+  }
+
+  // 新增:尾款结算阶段确认并自动进入下一阶段(客户评价)
+  confirmSettlement(): void {
+    if (this.isConfirmingSettlement) return;
+    
+    this.isConfirmingSettlement = true;
+    
+    // 模拟API调用延迟
+    setTimeout(() => {
+      this.isSettlementCompleted = true;
+      this.isConfirmingSettlement = false;
+      
+      // 显示成功提示
+      alert('尾款结算已确认完成!');
+      
+      // 进入下一阶段
+      this.advanceToNextStage('尾款结算');
+    }, 1500);
+  }
+
+  // 新增:投诉处理阶段确认并完成项目(基础版本,详细版本在售后模块中)
+  confirmComplaintBasic(): void {
+    console.log('确认投诉处理完成');
+    // 可以在这里添加更多逻辑,比如标记项目完成等
+    // 调用服务更新后端数据
+    // this.projectService.confirmComplaintResolution(this.projectId);
+    this.advanceToNextStage('投诉处理');
+  }
+
+  // 新增:软装阶段 确认上传并自动进入下一阶段(渲染)
+  confirmSoftDecorUpload(): void {
+    // 检查软装阶段的图片数据
+    const softDecorProcess = this.deliveryProcesses.find(p => p.id === 'soft-decoration');
+    if (!softDecorProcess) return;
+    
+    // 检查是否有任何空间上传了图片
+    const hasImages = softDecorProcess.spaces.some(space => {
+      const content = softDecorProcess.content[space.id];
+      return content && content.images && content.images.length > 0;
+    });
+    
+    if (!hasImages) return;
+    this.advanceToNextStage('软装');
+  }
+
+  // 新增:渲染阶段 确认上传并自动进入下一阶段(后期)
+  confirmRenderUpload(): void {
+    // 检查渲染阶段的图片数据
+    const renderProcess = this.deliveryProcesses.find(p => p.id === 'rendering');
+    if (!renderProcess) return;
+    
+    // 检查是否有任何空间上传了图片
+    const hasImages = renderProcess.spaces.some(space => {
+      const content = renderProcess.content[space.id];
+      return content && content.images && content.images.length > 0;
+    });
+    
+    if (!hasImages) 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<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));
+
+    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),
+        locked: true 
+      });
+    }
+    input.value = '';
+  }
+  
+  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 '渲染':
+      case '后期':
+        return 'delivery';
+      case '尾款结算':
+      case '客户评价':
+      case '投诉处理':
+        return 'aftercare';
+      default:
+        return 'order';
+    }
+  }
+
+  // 获取板块状态:completed | 'active' | 'pending'
+  getSectionStatus(key: SectionKey): 'completed' | 'active' | 'pending' {
+    // 优先使用本地的currentStage,如果没有则使用project.currentStage
+    const current = (this.currentStage || this.project?.currentStage) as ProjectStage | undefined;
+    
+    // 如果没有当前阶段(新创建的项目),默认订单创建板块为active(红色)
+    if (!current || current === '订单创建') {
+      return key === 'order' ? 'active' : '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<ProjectStage, string> = {
+      '订单创建': 'order',
+      '需求沟通': 'requirements-talk',
+      '方案确认': 'proposal-confirm',
+      '建模': 'modeling',
+      '软装': 'softdecor',
+      '渲染': 'render',
+      '后期': 'postprocess',
+      '尾款结算': '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 = '';
+
+  // 客户信息实时同步相关变量
+  isSyncingCustomerInfo: boolean = false;
+  lastSyncTime: Date | null = null;
+  syncInterval: any = null;
+
+  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;
+      
+      // 触发客户信息同步显示
+      this.syncCustomerInfoDisplay();
+    }, 1000);
+  }
+
+  // 同步客户信息显示
+  syncCustomerInfoDisplay(): void {
+    this.isSyncingCustomerInfo = true;
+    
+    // 模拟同步过程
+    setTimeout(() => {
+      this.lastSyncTime = new Date();
+      this.isSyncingCustomerInfo = false;
+      
+      // 触发界面更新
+      this.cdr.detectChanges();
+      
+      console.log('客户信息显示已同步:', this.lastSyncTime);
+    }, 800);
+  }
+
+  // 启动自动同步
+  startAutoSync(): void {
+    if (this.syncInterval) {
+      clearInterval(this.syncInterval);
+    }
+    
+    // 每30秒自动同步一次
+    this.syncInterval = setInterval(() => {
+      this.syncCustomerInfoDisplay();
+    }, 30000);
+  }
+
+  // 停止自动同步
+  stopAutoSync(): void {
+    if (this.syncInterval) {
+      clearInterval(this.syncInterval);
+      this.syncInterval = null;
+    }
+  }
+
+  // 格式化时间显示
+  formatTime(date: Date): string {
+    const now = new Date();
+    const diff = now.getTime() - date.getTime();
+    const minutes = Math.floor(diff / 60000);
+    
+    if (minutes < 1) {
+      return '刚刚';
+    } else if (minutes < 60) {
+      return `${minutes}分钟前`;
+    } else {
+      const hours = Math.floor(minutes / 60);
+      return `${hours}小时前`;
+    }
+  }
+
+  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 || []
+        };
+      }
+
+      // 新增:实时更新左侧客户信息显示
+      if (this.project) {
+        // 更新项目的客户信息
+        if (requirementData.colorIndicators && requirementData.colorIndicators.length > 0) {
+          this.project.customerInfo = {
+            ...this.project.customerInfo,
+            colorPreference: requirementData.colorIndicators.map((indicator: any) => indicator.name).join(', ')
+          };
+        }
+        
+        // 更新空间结构信息
+        if (requirementData.spaceIndicators && requirementData.spaceIndicators.length > 0) {
+          this.project.customerInfo = {
+            ...this.project.customerInfo,
+            spaceRequirements: requirementData.spaceIndicators.map((indicator: any) => `${indicator.name}: ${indicator.value}`).join(', ')
+          };
+        }
+        
+        // 更新材质偏好
+        if (requirementData.materialIndicators && requirementData.materialIndicators.length > 0) {
+          this.project.customerInfo = {
+            ...this.project.customerInfo,
+            materialPreference: requirementData.materialIndicators.map((indicator: any) => `${indicator.name}: ${indicator.value}%`).join(', ')
+          };
+        }
+        
+        // 触发变更检测以更新UI
+        this.cdr.detectChanges();
+      }
+
+      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: ['金属', '玻璃']
+        }
+      };
+    }
+  }
+
+  // 新增:处理需求阶段完成事件
+  onRequirementsStageCompleted(event: { stage: string; allStagesCompleted: boolean }): void {
+    console.log('需求阶段完成事件:', event);
+    
+    if (event.allStagesCompleted && event.stage === 'requirements-communication') {
+      // 自动推进到方案确认阶段
+      this.currentStage = '方案确认';
+      this.expandedStages['方案确认'] = true;
+      this.expandedStages['需求沟通'] = false;
+      
+      // 更新项目状态
+      this.updateProjectStage('方案确认');
+      
+      console.log('自动推进到方案确认阶段');
+    }
+  }
+
+  // 新增:确认方案方法
+  confirmProposal(): void {
+    console.log('确认方案按钮被点击');
+    
+    // 使用统一的阶段推进方法
+    this.advanceToNextStage('方案确认');
+    
+    console.log('已跳转到建模阶段');
+  }
+
+  // 获取同步的关键信息摘要
+  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;
+  }
+
+  // 检查必需阶段是否全部完成(流程进度 > 确认需求 > 需求沟通四个流程)
+  areRequiredStagesCompleted(): boolean {
+    // 检查项目是否已经进入方案确认阶段或更后的阶段
+    if (!this.project) {
+      return false;
+    }
+    
+    const stageOrder = [
+      '订单创建', '需求沟通', '方案确认', '建模', '软装', 
+      '渲染', '尾款结算', '客户评价', '投诉处理'
+    ];
+    
+    const currentStageIndex = stageOrder.indexOf(this.project.currentStage);
+    const proposalStageIndex = stageOrder.indexOf('方案确认');
+    const requirementStageIndex = stageOrder.indexOf('需求沟通');
+    
+    // 如果当前阶段是方案确认或之后的阶段,则认为需求阶段已完成
+    if (currentStageIndex >= proposalStageIndex) {
+      // 确保有基本的需求信息数据,如果没有则初始化模拟数据
+      this.ensureRequirementData();
+      return true;
+    }
+    
+    // 如果当前阶段是需求沟通,检查需求沟通是否已完成
+    if (currentStageIndex === requirementStageIndex) {
+      // 检查需求关键信息是否有数据,或者检查需求沟通组件的完成状态
+      const hasRequirementData = this.getRequirementSummary().length > 0 && 
+             (!!this.requirementKeyInfo.colorAtmosphere.description || 
+              this.requirementKeyInfo.spaceStructure.aspectRatio > 0 ||
+              this.requirementKeyInfo.materialWeights.woodRatio > 0 ||
+              !!this.requirementKeyInfo.presetAtmosphere.name);
+      
+      // 只有在真正有需求数据时才返回true,不再自动初始化模拟数据
+      if (hasRequirementData) {
+        return true;
+      }
+      
+      // 如果没有需求数据,返回false,不允许进入下一阶段
+      return false;
+    }
+    
+    // 其他情况返回false
+    return false;
+  }
+
+  // 确保有需求数据用于方案确认显示
+  private ensureRequirementData(): void {
+    // console.log('=== ensureRequirementData 开始确保需求数据 ===');
+    // console.log('当前requirementKeyInfo:', this.requirementKeyInfo);
+    
+    // 修复条件判断:检查是否需要初始化数据
+    const needsInitialization = 
+      !this.requirementKeyInfo.colorAtmosphere.description || 
+      this.requirementKeyInfo.spaceStructure.aspectRatio === 0 ||
+      this.requirementKeyInfo.materialWeights.woodRatio === 0 ||
+      !this.requirementKeyInfo.presetAtmosphere.name;
+    
+    console.log('是否需要初始化数据:', needsInitialization);
+    
+    if (needsInitialization) {
+      console.log('需求关键信息为空,初始化默认数据');
+      // 初始化模拟的需求数据
+      this.requirementKeyInfo = {
+        colorAtmosphere: {
+          description: '现代简约风格,以白色和灰色为主调',
+          mainColor: '#F5F5F5',
+          colorTemp: '冷色调',
+          materials: ['木质', '金属', '玻璃']
+        },
+        spaceStructure: {
+          lineRatio: 0.6,
+          blankRatio: 0.4,
+          flowWidth: 1.2,
+          aspectRatio: 1.8,
+          ceilingHeight: 2.8
+        },
+        materialWeights: {
+          fabricRatio: 20,
+          woodRatio: 45,
+          metalRatio: 25,
+          smoothness: 0.7,
+          glossiness: 0.3
+        },
+        presetAtmosphere: {
+          name: '现代简约',
+          rgb: '#F5F5F5',
+          colorTemp: '5000K',
+          materials: ['木质', '金属']
+        }
+      };
+      // console.log('初始化后的requirementKeyInfo:', this.requirementKeyInfo);
+    } else {
+      // console.log('需求关键信息已存在,无需初始化');
+    }
+  }
+
+  // 获取项目状态文本
+  getProjectStatusText(): string {
+    const current = (this.currentStage || this.project?.currentStage) as ProjectStage | undefined;
+    
+    if (!current || current === '订单创建') {
+      return '待开始';
+    }
+    
+    // 检查是否已完成所有阶段
+    const allStages: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '尾款结算', '客户评价', '投诉处理'];
+    const currentIndex = allStages.indexOf(current);
+    
+    if (currentIndex === allStages.length - 1) {
+      return '已完成';
+    }
+    
+    return '进行中';
+  }
+
+  // 获取当前阶段文本
+  getCurrentStageText(): string {
+    const current = (this.currentStage || this.project?.currentStage) as ProjectStage | undefined;
+    
+    if (!current || current === '订单创建') {
+      return '订单创建阶段';
+    }
+    
+    return `${current}阶段`;
+  }
+
+  // 获取整体进度百分比
+  getOverallProgress(): number {
+    const current = (this.currentStage || this.project?.currentStage) as ProjectStage | undefined;
+    
+    if (!current) {
+      return 0;
+    }
+    
+    // 定义所有阶段及其权重
+    const stageWeights: Record<ProjectStage, number> = {
+      '订单创建': 5,
+      '需求沟通': 15,
+      '方案确认': 25,
+      '建模': 40,
+      '软装': 55,
+      '渲染': 70,
+      '后期': 80,
+      '尾款结算': 90,
+      '客户评价': 95,
+      '投诉处理': 100
+    };
+    
+    return stageWeights[current] || 0;
+  }
+
+  // 获取必需阶段的完成进度百分比
+  getRequiredStagesProgress(): number {
+    let completedCount = 0;
+    const totalCount = 4; // 四个必需流程
+
+    // 检查各个关键信息是否已确认
+    if (this.requirementKeyInfo.colorAtmosphere.description) completedCount++;
+    if (this.requirementKeyInfo.spaceStructure.aspectRatio > 0) completedCount++;
+    if (this.requirementKeyInfo.materialWeights.woodRatio > 0 || 
+        this.requirementKeyInfo.materialWeights.fabricRatio > 0 || 
+        this.requirementKeyInfo.materialWeights.metalRatio > 0) completedCount++;
+    if (this.requirementKeyInfo.presetAtmosphere.name) completedCount++;
+
+    return Math.round((completedCount / totalCount) * 100);
+  }
+
+  // 订单金额
+  orderAmount: number = 0;
+  
+  // 报价明细
+  quotationDetails: Array<{
+    id: string;
+    room: string;
+    amount: number;
+    description?: string;
+  }> = [];
+
+  // AI生成报价明细
+  generateQuotationDetails(): void {
+    // 基于项目信息生成报价明细
+    const rooms = ['客餐厅', '主卧', '次卧', '厨房', '卫生间'];
+    this.quotationDetails = rooms.map((room, index) => ({
+      id: `quote_${index + 1}`,
+      room: room,
+      amount: Math.floor(Math.random() * 1000) + 300, // 示例金额
+      description: `${room}装修设计费用`
+    }));
+    
+    // 更新总订单金额
+    this.orderAmount = this.quotationDetails.reduce((total, item) => total + item.amount, 0);
+  }
+
+  // 添加报价明细项
+  addQuotationItem(): void {
+    this.quotationDetails.push({
+      id: `quote_${Date.now()}`,
+      room: '',
+      amount: 0,
+      description: ''
+    });
+  }
+
+  // 删除报价明细项
+  removeQuotationItem(id: string): void {
+    this.quotationDetails = this.quotationDetails.filter(item => item.id !== id);
+    this.updateOrderAmount();
+  }
+
+  // 更新订单总金额
+  updateOrderAmount(): void {
+    this.orderAmount = this.quotationDetails.reduce((total, item) => total + item.amount, 0);
+  }
+
+  // 报价组件数据
+  quotationData: QuotationData = {
+    items: [],
+    totalAmount: 0,
+    materialCost: 0,
+    laborCost: 0,
+    designFee: 0,
+    managementFee: 0
+  };
+
+  // 设计师指派数据
+  designerAssignmentData?: DesignerAssignmentData;
+
+  // 设计师日历弹窗状态与数据
+  showDesignerCalendar: boolean = false;
+  selectedCalendarDate: Date = new Date();
+  calendarDesigners: CalendarDesigner[] = [];
+  calendarGroups: CalendarProjectGroup[] = [];
+
+  onQuotationDataChange(data: QuotationData): void {
+    this.quotationData = { ...data };
+    this.orderAmount = data.totalAmount || 0;
+  }
+
+  onDesignerAssignmentChange(data: DesignerAssignmentData): void {
+    this.designerAssignmentData = { ...data };
+  }
+
+  onDesignerClick(designer: AssignmentDesigner): void {
+    const mapped = this.mapAssignmentDesignerToCalendar(designer);
+    this.calendarDesigners = [mapped];
+    this.calendarGroups = [{ id: designer.teamId, name: designer.teamName, leaderId: designer.id, memberIds: [designer.id] }];
+    this.selectedCalendarDate = new Date();
+    this.showDesignerCalendar = true;
+  }
+
+  closeDesignerCalendar(): void {
+    this.showDesignerCalendar = false;
+  }
+
+  onCalendarDesignerSelected(designer: CalendarDesigner): void {
+    this.selectedDesigner = designer;
+    this.closeDesignerCalendar();
+  }
+
+  onCalendarAssignmentRequested(designer: CalendarDesigner): void {
+    this.selectedDesigner = designer;
+    this.closeDesignerCalendar();
+  }
+
+  private mapAssignmentDesignerToCalendar(d: AssignmentDesigner): CalendarDesigner {
+    return {
+      id: d.id,
+      name: d.name,
+      avatar: d.avatar,
+      groupId: d.teamId,
+      groupName: d.teamName,
+      isLeader: !!d.isTeamLeader,
+      status: d.status === 'idle' ? 'available' : 'busy',
+      currentProjects: Math.max(0, d.recentOrders || 0),
+      lastOrderDate: d.lastOrderDate,
+      idleDays: Math.max(0, d.idleDays || 0),
+      completedThisMonth: Math.max(0, d.recentOrders || 0),
+      averageCycle: 15,
+      upcomingEvents: (d.reviewDates || []).map((dateStr, idx) => ({
+        id: `${d.id}-review-${idx}`,
+        date: new Date(dateStr),
+        title: '对图评审',
+        type: 'review',
+        projectId: undefined,
+        duration: 2
+      })),
+      workload: Math.max(0, d.workload || 0),
+      nextAvailableDate: new Date()
+    };
+  }
+
+  // 处理咨询订单表单提交
+  // 存储订单创建时的客户信息和需求信息
+
+  onConsultationOrderSubmit(formData: any): void {
+    console.log('咨询订单表单提交:', formData);
+    
+    // 保存订单创建数据
+    this.orderCreationData = formData;
+    
+    // 更新projectData以便传递给子组件(集成报价与指派信息)
+    this.projectData = {
+      customerInfo: formData.customerInfo,
+      requirementInfo: formData.requirementInfo,
+      preferenceTags: formData.preferenceTags,
+      quotation: this.quotationData ? { ...this.quotationData } : undefined,
+      assignment: this.designerAssignmentData ? { ...this.designerAssignmentData } : undefined,
+      orderAmount: this.quotationData?.totalAmount ?? this.orderAmount ?? 0
+    };
+    
+    // 实时更新左侧客户信息显示
+    this.updateCustomerInfoDisplay(formData);
+    
+    // 根据角色上下文处理数据同步
+    if (formData.roleContext === 'customer-service') {
+      // 客服端创建的订单需要同步到设计师端
+      this.syncOrderToDesignerView(formData);
+    }
+
+    // 根据报价数据更新订单金额
+    this.orderAmount = this.quotationData?.totalAmount ?? this.orderAmount ?? 0;
+    this.updateOrderAmount();
+    
+    // 触发变更检测以更新UI
+    this.cdr.detectChanges();
+  }
+
+  // 新增:更新客户信息显示 - 优化后只更新实际存在的字段
+  private updateCustomerInfoDisplay(formData: any): void {
+    if (formData.customerInfo) {
+      // 更新项目对象中的客户信息 - 只更新实际存在的字段
+      if (this.project) {
+        // 由于已移除客户姓名和手机号字段,只更新微信和客户类型
+        if (formData.customerInfo.wechat) {
+          this.project.customerWechat = formData.customerInfo.wechat;
+        }
+        if (formData.customerInfo.customerType) {
+          this.project.customerType = formData.customerInfo.customerType;
+        }
+        if (formData.customerInfo.source) {
+          this.project.customerSource = formData.customerInfo.source;
+        }
+        if (formData.customerInfo.remark) {
+          this.project.customerRemark = formData.customerInfo.remark;
+        }
+      }
+      
+      // 更新客户标签
+      if (formData.preferenceTags) {
+        this.project = {
+          ...this.project,
+          customerTags: formData.preferenceTags
+        } as any;
+      }
+      
+      // 更新需求信息 - 只更新实际存在的字段
+      if (formData.requirementInfo) {
+        this.project = {
+          ...this.project,
+          // 移除已删除的字段:decorationType, firstDraftDate, style, budget, area, houseType
+          downPayment: formData.requirementInfo.downPayment,
+          smallImageTime: formData.requirementInfo.smallImageTime,
+          spaceRequirements: formData.requirementInfo.spaceRequirements,
+          designAngles: formData.requirementInfo.designAngles,
+          specialAreaHandling: formData.requirementInfo.specialAreaHandling,
+          materialRequirements: formData.requirementInfo.materialRequirements,
+          lightingRequirements: formData.requirementInfo.lightingRequirements
+        } as any;
+      }
+      
+      console.log('客户信息已实时更新:', this.project);
+    }
+  }
+
+  // 新增:同步订单数据到设计师视图
+  private syncOrderToDesignerView(formData: any): void {
+    // 创建项目数据
+    const projectData = {
+      customerId: formData.customerInfo.id || 'customer-' + Date.now(),
+      customerName: formData.customerInfo.name,
+      requirement: formData.requirementInfo,
+      referenceCases: [],
+      tags: {
+        demandType: formData.customerInfo.demandType,
+        preferenceTags: formData.preferenceTags,
+        followUpStatus: formData.customerInfo.followUpStatus
+      },
+      // 新增:报价与指派信息(可选)
+      quotation: this.quotationData ? { ...this.quotationData } : undefined,
+      assignment: this.designerAssignmentData ? { ...this.designerAssignmentData } : undefined,
+      orderAmount: this.quotationData?.totalAmount ?? this.orderAmount ?? 0
+    };
+
+    // 调用项目服务创建项目
+    this.projectService.createProject(projectData).subscribe(
+      result => {
+        if (result.success) {
+          console.log('订单数据已同步到设计师端,项目ID:', result.projectId);
+          // 可以在这里添加成功提示或其他处理逻辑
+        }
+      },
+      error => {
+        console.error('同步订单数据到设计师端失败:', error);
+      }
+    );
+  }
+
+  // 确认团队分配
+  confirmTeamAssignment(designer: any): void {
+    if (designer) {
+      this.selectedDesigner = designer;
+      console.log('团队分配确认:', designer);
+      
+      // 这里可以添加实际的团队分配逻辑
+      // 例如调用服务来分配设计师到项目
+      
+      // 进入下一个阶段:需求沟通
+      this.updateProjectStage('需求沟通');
+      this.expandedStages['需求沟通'] = true;
+      this.expandedStages['订单创建'] = false;
+      
+      // 显示成功消息
+      alert('团队分配成功!已进入需求沟通阶段');
+      
+      console.log('团队分配完成,已跳转到需求沟通阶段');
+    }
+  }
+
+  // 项目创建完成事件处理
+  onProjectCreated(projectData: any): void {
+    console.log('项目创建完成:', projectData);
+    this.projectData = projectData;
+
+    // 团队分配已在子组件中完成并触发该事件:推进到需求沟通阶段
+    this.updateProjectStage('需求沟通');
+    
+    // 更新项目对象的当前阶段,确保四大板块状态正确显示
+    if (this.project) {
+      this.project.currentStage = '需求沟通';
+    }
+    
+    // 展开需求沟通阶段,收起订单创建阶段
+    this.expandedStages['需求沟通'] = true;
+    this.expandedStages['订单创建'] = false;
+    
+    // 自动展开确认需求板块
+    this.expandedSection = 'requirements';
+
+    // 强制触发变更检测,确保UI更新
+    this.cdr.detectChanges();
+
+    // 延迟滚动到需求沟通阶段,确保DOM更新完成
+    setTimeout(() => {
+      this.scrollToStage('需求沟通');
+      // 再次触发变更检测,确保所有状态都已正确更新
+      this.cdr.detectChanges();
+    }, 100);
+
+    console.log('项目创建成功,已推进到需求沟通阶段,四大板块状态已更新');
+  }
+
+  // 新增:处理实时需求数据更新
+  onRequirementDataUpdated(data: any): void {
+    console.log('收到需求数据更新:', data);
+    
+    // 同步关键信息
+    this.syncRequirementKeyInfo(data);
+    
+    // 更新客户信息显示
+    if (data && this.project) {
+      // 更新项目的客户信息
+      if (data.colorIndicators && data.colorIndicators.length > 0) {
+        this.project.customerInfo = {
+          ...this.project.customerInfo,
+          colorPreference: data.colorIndicators.map((indicator: any) => indicator.name).join(', ')
+        };
+      }
+      
+      // 更新空间结构信息
+      if (data.spaceIndicators && data.spaceIndicators.length > 0) {
+        this.project.customerInfo = {
+          ...this.project.customerInfo,
+          spaceRequirements: data.spaceIndicators.map((indicator: any) => `${indicator.name}: ${indicator.value}`).join(', ')
+        };
+      }
+      
+      // 更新材质偏好
+      if (data.materialIndicators && data.materialIndicators.length > 0) {
+        this.project.customerInfo = {
+          ...this.project.customerInfo,
+          materialPreference: data.materialIndicators.map((indicator: any) => `${indicator.name}: ${indicator.value}%`).join(', ')
+        };
+      }
+      
+      // 更新需求项目
+      if (data.requirementItems && data.requirementItems.length > 0) {
+        this.project.requirements = data.requirementItems.map((item: any) => ({
+          id: item.id,
+          description: item.description,
+          status: item.status,
+          priority: item.priority || 'medium'
+        }));
+      }
+      
+      // 接收色彩分析结果并存储用于右侧展示
+      if (data.colorAnalysisResult) {
+        console.log('接收到色彩分析结果:', data.colorAnalysisResult);
+        this.colorAnalysisResult = data.colorAnalysisResult as ColorAnalysisResult;
+        console.log('设置colorAnalysisResult后:', this.colorAnalysisResult);
+        
+        // 计算主色:按占比最高的颜色
+        const colors = this.colorAnalysisResult?.colors || [];
+        if (colors.length > 0) {
+          const dominant = colors.reduce((max, cur) => cur.percentage > max.percentage ? cur : max, colors[0]);
+          this.dominantColorHex = dominant.hex;
+          console.log('计算出的主色:', this.dominantColorHex);
+        } else {
+          this.dominantColorHex = null;
+          console.log('没有颜色数据,主色设为null');
+        }
+      } else {
+        console.log('没有接收到色彩分析结果');
+      }
+
+      // 新增:处理详细的分析数据
+      if (data.detailedAnalysis) {
+        console.log('接收到详细分析数据:', data.detailedAnalysis);
+        
+        // 存储各类分析结果
+        this.enhancedColorAnalysis = data.detailedAnalysis.enhancedColorAnalysis;
+        this.formAnalysis = data.detailedAnalysis.formAnalysis;
+        this.textureAnalysis = data.detailedAnalysis.textureAnalysis;
+        this.patternAnalysis = data.detailedAnalysis.patternAnalysis;
+        this.lightingAnalysis = data.detailedAnalysis.lightingAnalysis;
+        
+        console.log('详细分析数据已存储:', {
+          enhancedColorAnalysis: this.enhancedColorAnalysis,
+          formAnalysis: this.formAnalysis,
+          textureAnalysis: this.textureAnalysis,
+          patternAnalysis: this.patternAnalysis,
+          lightingAnalysis: this.lightingAnalysis
+        });
+      }
+
+      // 新增:处理材料分析数据
+      if (data.materialAnalysisData && data.materialAnalysisData.length > 0) {
+        console.log('接收到材料分析数据:', data.materialAnalysisData);
+        this.materialAnalysisData = data.materialAnalysisData;
+      }
+
+      // 新增:根据上传来源拆分材料文件用于左右区展示
+      const materials = Array.isArray(data?.materials) ? data.materials : [];
+      this.referenceImages = materials.filter((m: any) => m?.type === 'image');
+      this.cadFiles = materials.filter((m: any) => m?.type === 'cad');
+
+      // 触发变更检测以更新UI
+      this.cdr.detectChanges();
+      
+      console.log('客户信息已实时更新,当前colorAnalysisResult状态:', !!this.colorAnalysisResult);
+    }
+  }
+
+  // 预览右侧色彩分析参考图
+  previewColorRefImage(): void {
+    const url = this.colorAnalysisResult?.originalImage;
+    if (url) {
+      window.open(url, '_blank');
+    }
+  }
+
+  // 新增:点击预览参考图片与CAD文件
+  previewImageFile(url?: string): void {
+    if (!url) return;
+    window.open(url, '_blank');
+  }
+
+  previewCadFile(url?: string): void {
+    if (!url) return;
+    window.open(url, '_blank');
+  }
+
+
+  // 切换客户信息卡片展开状态
+  toggleCustomerInfo(): void {
+    this.isCustomerInfoExpanded = !this.isCustomerInfoExpanded;
+  }
+
+  // 新增:重置方案分析状态的方法
+  resetProposalAnalysis(): void {
+    this.proposalAnalysis = null;
+    this.isAnalyzing = false;
+    this.analysisProgress = 0;
+    console.log('方案分析状态已重置');
+  }
+
+  // 新增:模拟素材解析方法
+  startMaterialAnalysis(): void {
+    this.isAnalyzing = true;
+    this.analysisProgress = 0;
+    const progressInterval = setInterval(() => {
+      this.analysisProgress += Math.random() * 15;
+      if (this.analysisProgress >= 100) {
+        this.analysisProgress = 100;
+        clearInterval(progressInterval);
+        this.completeMaterialAnalysis();
+      }
+    }, 500);
+  }
+
+  // 完成素材解析,生成方案数据
+  private completeMaterialAnalysis(): void {
+    this.isAnalyzing = false;
+    
+    // 生成模拟的方案分析数据
+    this.proposalAnalysis = {
+      id: 'proposal-' + Date.now(),
+      name: '现代简约风格方案',
+      version: 'v1.0',
+      createdAt: new Date(),
+      status: 'completed',
+      materials: [
+        {
+          category: '地面材料',
+          specifications: {
+            type: '复合木地板',
+            grade: 'E0级',
+            thickness: '12mm',
+            finish: '哑光面',
+            durability: '家用33级'
+          },
+          usage: {
+            area: '客厅、卧室',
+            percentage: 65,
+            priority: 'primary'
+          },
+          properties: {
+            texture: '木纹理',
+            color: '浅橡木色',
+            maintenance: '日常清洁'
+          }
+        },
+        {
+          category: '墙面材料',
+          specifications: {
+            type: '乳胶漆',
+            grade: '净味抗甲醛',
+            finish: '丝光面',
+            durability: '15年'
+          },
+          usage: {
+            area: '全屋墙面',
+            percentage: 80,
+            priority: 'primary'
+          },
+          properties: {
+            texture: '平滑',
+            color: '暖白色',
+            maintenance: '可擦洗'
+          }
+        },
+        {
+          category: '软装面料',
+          specifications: {
+            type: '亚麻混纺',
+            grade: 'A级',
+            finish: '防污处理',
+            durability: '5-8年'
+          },
+          usage: {
+            area: '沙发、窗帘',
+            percentage: 25,
+            priority: 'secondary'
+          },
+          properties: {
+            texture: '自然纹理',
+            color: '米灰色系',
+            maintenance: '干洗'
+          }
+        }
+      ],
+      designStyle: {
+        primaryStyle: '现代简约',
+        styleElements: [
+          {
+            element: '线条设计',
+            description: '简洁流畅的直线条,避免繁复装饰',
+            influence: 85
+          },
+          {
+            element: '色彩搭配',
+            description: '以中性色为主,局部点缀暖色',
+            influence: 75
+          },
+          {
+            element: '材质选择',
+            description: '天然材质与现代工艺结合',
+            influence: 70
+          }
+        ],
+        characteristics: [
+          {
+            feature: '空间感',
+            value: '开放通透',
+            importance: 'high'
+          },
+          {
+            feature: '功能性',
+            value: '实用至上',
+            importance: 'high'
+          },
+          {
+            feature: '装饰性',
+            value: '简约精致',
+            importance: 'medium'
+          }
+        ],
+        compatibility: {
+          withMaterials: ['木材', '金属', '玻璃', '石材'],
+          withColors: ['白色', '灰色', '米色', '原木色'],
+          score: 92
+        }
+      },
+      colorScheme: {
+        palette: [
+          {
+            color: '暖白色',
+            hex: '#F8F6F0',
+            rgb: '248, 246, 240',
+            percentage: 45,
+            role: 'dominant'
+          },
+          {
+            color: '浅灰色',
+            hex: '#E5E5E5',
+            rgb: '229, 229, 229',
+            percentage: 30,
+            role: 'secondary'
+          },
+          {
+            color: '原木色',
+            hex: '#D4A574',
+            rgb: '212, 165, 116',
+            percentage: 20,
+            role: 'accent'
+          },
+          {
+            color: '深灰色',
+            hex: '#4A4A4A',
+            rgb: '74, 74, 74',
+            percentage: 5,
+            role: 'neutral'
+          }
+        ],
+        harmony: {
+          type: '类似色调和',
+          temperature: 'warm',
+          contrast: 65
+        },
+        psychology: {
+          mood: '宁静舒适',
+          atmosphere: '温馨自然',
+          suitability: ['居住', '办公', '休闲']
+        }
+      },
+      spaceLayout: {
+        dimensions: {
+          length: 12.5,
+          width: 8.2,
+          height: 2.8,
+          area: 102.5,
+          volume: 287
+        },
+        functionalZones: [
+          {
+            zone: '客厅区域',
+            area: 35.2,
+            percentage: 34.3,
+            requirements: ['会客', '娱乐', '休息'],
+            furniture: ['沙发', '茶几', '电视柜', '边几']
+          },
+          {
+            zone: '餐厅区域',
+            area: 18.5,
+            percentage: 18.0,
+            requirements: ['用餐', '储物'],
+            furniture: ['餐桌', '餐椅', '餐边柜']
+          },
+          {
+            zone: '厨房区域',
+            area: 12.8,
+            percentage: 12.5,
+            requirements: ['烹饪', '储存', '清洁'],
+            furniture: ['橱柜', '岛台', '吧台椅']
+          },
+          {
+            zone: '主卧区域',
+            area: 25.6,
+            percentage: 25.0,
+            requirements: ['睡眠', '储衣', '梳妆'],
+            furniture: ['床', '衣柜', '梳妆台', '床头柜']
+          },
+          {
+            zone: '次卧区域',
+            area: 10.4,
+            percentage: 10.2,
+            requirements: ['睡眠', '学习'],
+            furniture: ['床', '书桌', '衣柜']
+          }
+        ],
+        circulation: {
+          mainPaths: ['入户-客厅', '客厅-餐厅', '餐厅-厨房', '客厅-卧室'],
+          pathWidth: 1.2,
+          efficiency: 88
+        },
+        lighting: {
+          natural: {
+            direction: ['南向', '东向'],
+            intensity: '充足',
+            duration: '8-10小时'
+          },
+          artificial: {
+            zones: ['主照明', '局部照明', '装饰照明'],
+            requirements: ['无主灯设计', '分层控制', '调光调色']
+          }
+        }
+      },
+      budget: {
+        total: 285000,
+        breakdown: [
+          {
+            category: '基础装修',
+            amount: 142500,
+            percentage: 50
+          },
+          {
+            category: '主材采购',
+            amount: 85500,
+            percentage: 30
+          },
+          {
+            category: '软装配饰',
+            amount: 42750,
+            percentage: 15
+          },
+          {
+            category: '设计费用',
+            amount: 14250,
+            percentage: 5
+          }
+        ]
+      },
+      timeline: [
+        {
+          phase: '设计深化',
+          duration: 7,
+          dependencies: ['需求确认']
+        },
+        {
+          phase: '材料采购',
+          duration: 5,
+          dependencies: ['设计深化']
+        },
+        {
+          phase: '基础施工',
+          duration: 30,
+          dependencies: ['材料采购']
+        },
+        {
+          phase: '软装进场',
+          duration: 7,
+          dependencies: ['基础施工']
+        },
+        {
+          phase: '验收交付',
+          duration: 3,
+          dependencies: ['软装进场']
+        }
+      ],
+      feasibility: {
+        technical: 95,
+        budget: 88,
+        timeline: 92,
+        overall: 92
+      }
+    };
+  }
+
+  // 获取材质分类统计
+  getMaterialCategories(): { category: string; count: number; percentage: number }[] {
+    if (!this.proposalAnalysis) return [];
+    
+    const categories = this.proposalAnalysis.materials.reduce((acc, material) => {
+      acc[material.category] = (acc[material.category] || 0) + 1;
+      return acc;
+    }, {} as Record<string, number>);
+    
+    const total = Object.values(categories).reduce((sum, count) => sum + count, 0);
+    
+    return Object.entries(categories).map(([category, count]) => ({
+      category,
+      count,
+      percentage: Math.round((count / total) * 100)
+    }));
+  }
+
+  // 获取设计风格特征摘要
+  getStyleSummary(): string {
+    if (!this.proposalAnalysis) return '';
+    
+    const style = this.proposalAnalysis.designStyle;
+    const topElements = style.styleElements
+      .sort((a, b) => b.influence - a.influence)
+      .slice(0, 2)
+      .map(el => el.element)
+      .join('、');
+    
+    return `${style.primaryStyle}风格,主要体现在${topElements}等方面`;
+  }
+
+  // 获取色彩方案摘要
+  getColorSummary(): string {
+    if (!this.proposalAnalysis) return '';
+    
+    const scheme = this.proposalAnalysis.colorScheme;
+    const dominantColor = scheme.palette.find(p => p.role === 'dominant')?.color || '';
+    const accentColor = scheme.palette.find(p => p.role === 'accent')?.color || '';
+    
+    return `以${dominantColor}为主调,${accentColor}作点缀,营造${scheme.psychology.mood}的氛围`;
+  }
+
+  // 获取空间效率评分
+  getSpaceEfficiency(): number {
+    if (!this.proposalAnalysis) return 0;
+    return this.proposalAnalysis.spaceLayout.circulation.efficiency;
+  }
+
+  private handlePaymentProofUpload(file: File): void {
+    // 显示上传进度
+    const uploadingMessage = `正在上传支付凭证:${file.name}...`;
+    console.log(uploadingMessage);
+    
+    // 使用支付凭证识别服务处理上传
+    const settlementId = this.project?.id || 'default_settlement';
+    
+    this.paymentVoucherService.processPaymentVoucherUpload(file, settlementId).subscribe({
+      next: (result) => {
+        if (result.success && result.recognitionResult) {
+          const recognition = result.recognitionResult;
+          
+          // 更新识别计数
+          this.voucherRecognitionCount++;
+          
+          // 显示识别结果
+          const successMessage = `
+            支付凭证识别成功!
+            支付方式:${recognition.paymentMethod}
+            支付金额:¥${recognition.amount}
+            交易号:${recognition.transactionNumber}
+            置信度:${(recognition.confidence * 100).toFixed(1)}%
+          `;
+          
+          alert(successMessage);
+          console.log('支付凭证识别完成', recognition);
+          
+          // 自动标记验证通过并解锁渲染大图
+          this.isPaymentVerified = true;
+          this.renderLargeImages = this.renderLargeImages.map(img => ({ ...img, locked: false }));
+          
+          // 触发自动通知流程
+          this.triggerPaymentCompletedNotification(recognition);
+          
+        } else {
+          const errorMessage = `支付凭证识别失败:${result.error || '未知错误'}`;
+          alert(errorMessage);
+          console.error('支付凭证识别失败', result.error);
+        }
+      },
+      error: (error) => {
+        const errorMessage = `支付凭证处理出错:${error.message || '网络错误'}`;
+        alert(errorMessage);
+        console.error('支付凭证处理出错', error);
+      }
+    });
+  }
+
+  /**
+   * 触发支付完成通知流程
+   */
+  private triggerPaymentCompletedNotification(recognition: any): void {
+    console.log('触发支付完成自动通知流程...');
+    
+    // 模拟调用通知服务发送多渠道通知
+    this.sendMultiChannelNotifications(recognition);
+    
+    // 模拟发送通知
+    setTimeout(() => {
+      const notificationMessage = `
+        🎉 尾款已到账,大图已解锁!
+        
+        支付信息:
+        • 支付方式:${recognition.paymentMethod}
+        • 支付金额:¥${recognition.amount}
+        • 处理时间:${new Date().toLocaleString()}
+        
+        📱 系统已自动发送通知至:
+        • 短信通知:138****8888
+        • 微信通知:已推送至微信
+        • 邮件通知:customer@example.com
+        
+        🖼️ 高清渲染图下载链接已发送
+        您现在可以下载4K高清渲染图了!
+      `;
+      
+      alert(notificationMessage);
+      console.log('自动通知发送完成');
+    }, 1000);
+  }
+
+  /**
+   * 发送多渠道通知
+   */
+  private sendMultiChannelNotifications(recognition: any): void {
+    console.log('开始发送多渠道通知...');
+    
+    // 更新通知发送计数
+    this.notificationsSent++;
+    
+    // 模拟发送短信通知
+    console.log('📱 发送短信通知: 尾款已到账,大图已解锁');
+    
+    // 模拟发送微信通知
+    console.log('💬 发送微信通知: 支付成功,高清图片已准备就绪');
+    
+    // 模拟发送邮件通知
+    console.log('📧 发送邮件通知: 包含下载链接的详细通知');
+    
+    // 模拟发送应用内通知
+    console.log('🔔 发送应用内通知: 实时推送支付状态更新');
+    
+    // 模拟通知发送结果
+    setTimeout(() => {
+      console.log('✅ 所有渠道通知发送完成');
+      console.log(`通知发送统计: 短信✅ 微信✅ 邮件✅ 应用内✅ (总计: ${this.notificationsSent} 次)`);
+    }, 500);
+  }
+
+  // 获取当前设计师名称
+  getCurrentDesignerName(): string {
+    // 这里应该从用户服务或认证信息中获取当前用户名称
+    // 暂时返回一个默认值
+    return '张设计师';
+  }
+
+  // ==================== 售后相关变量 ====================
+
+  // 售后标签页控制
+  activeAftercareTab: string = 'services'; // 当前激活的售后标签页
+  
+  // 售后状态管理
+  afterSalesStage: string = '未开始'; // 售后阶段:未开始、进行中、已完成
+  afterSalesStatus: 'pending' | 'in_progress' | 'completed' | 'cancelled' = 'pending'; // 售后状态
+  afterSalesProgress: number = 0; // 售后进度百分比
+  
+  // 售后服务数据
+  afterSalesServices: Array<{
+    id: string;
+    type: string; // 服务类型:维修、保养、咨询、投诉处理
+    description: string;
+    assignedTo: string; // 指派给的人员
+    scheduledDate?: Date; // 计划服务日期
+    completedDate?: Date; // 完成日期
+    status: 'pending' | 'scheduled' | 'in_progress' | 'completed' | 'cancelled';
+    priority: 'low' | 'medium' | 'high';
+  }> = [];
+
+  // 售后评价和反馈
+  afterSalesRating: number = 0; // 售后评分(0-5)
+  afterSalesFeedback: string = ''; // 售后反馈内容
+  afterSalesFeedbackDate?: Date; // 反馈日期
+  
+  // 售后时间跟踪
+  afterSalesResponseTime?: Date; // 首次响应时间
+  afterSalesStartTime?: Date; // 售后开始时间
+  afterSalesCompletionTime?: Date; // 售后完成时间
+  afterSalesTotalDuration: number = 0; // 总耗时(小时)
+
+  // 售后联系人信息
+  afterSalesContact: {
+    name: string;
+    phone: string;
+    wechat?: string;
+    email?: string;
+  } = {
+    name: '',
+    phone: ''
+  };
+
+  // 售后问题记录
+  afterSalesIssues: Array<{
+    id: string;
+    title: string;
+    description: string;
+    severity: 'low' | 'medium' | 'high' | 'critical';
+    reportedDate: Date;
+    resolvedDate?: Date;
+    status: 'reported' | 'investigating' | 'resolved' | 'closed';
+  }> = [];
+
+  // 售后文件记录
+  afterSalesDocuments: Array<{
+    id: string;
+    name: string;
+    type: string; // 文件类型:合同、报告、照片、视频
+    uploadDate: Date;
+    url: string;
+  }> = [];
+
+  // 售后费用记录
+  afterSalesCosts: Array<{
+    id: string;
+    description: string;
+    amount: number;
+    category: 'material' | 'labor' | 'transportation' | 'other';
+    date: Date;
+    status: 'pending' | 'approved' | 'paid' | 'rejected';
+  }> = [];
+
+  // 售后沟通记录
+  afterSalesCommunications: Array<{
+    id: string;
+    type: 'phone' | 'wechat' | 'email' | 'visit';
+    date: Date;
+    summary: string;
+    participants: string[];
+  }> = [];
+
+  // 新增:项目复盘相关属性
+  projectReview: ProjectReview | null = null;
+  isGeneratingReview: boolean = false;
+  
+  // 项目复盘 Tab 切换
+  activeReviewTab: 'sop' | 'experience' | 'suggestions' = 'sop';
+  
+  // SOP执行数据
+  sopMetrics: any = {
+    communicationCount: 6,
+    avgCommunication: 5,
+    revisionCount: 3,
+    avgRevision: 2,
+    deliveryCycle: 18,
+    standardCycle: 15,
+    customerSatisfaction: 4.5
+  };
+  
+  sopStagesData: any[] = [
+    { name: '订单创建', plannedDuration: 1, actualDuration: 1, score: 95, status: 'completed', statusText: '已完成', isDelayed: false, issues: [] },
+    { name: '需求沟通', plannedDuration: 3, actualDuration: 4, score: 75, status: 'completed', statusText: '已完成', isDelayed: true, issues: ['沟通次数超标'] },
+    { name: '方案确认', plannedDuration: 2, actualDuration: 2, score: 90, status: 'completed', statusText: '已完成', isDelayed: false, issues: [] },
+    { name: '建模', plannedDuration: 4, actualDuration: 5, score: 80, status: 'completed', statusText: '已完成', isDelayed: true, issues: ['细节调整耗时'] },
+    { name: '软装', plannedDuration: 2, actualDuration: 2, score: 92, status: 'completed', statusText: '已完成', isDelayed: false, issues: [] },
+    { name: '渲染', plannedDuration: 3, actualDuration: 4, score: 85, status: 'ongoing', statusText: '进行中', isDelayed: false, issues: [] }
+  ];
+  
+  // 经验复盘数据
+  experienceData: any = {
+    customerNeeds: [
+      { text: '希望客厅采用现代简约风格,注重收纳功能', timestamp: '2024-01-15 10:30', source: '初次沟通' },
+      { text: '卧室需要温馨氛围,色调以暖色为主', timestamp: '2024-01-16 14:20', source: '需求确认' },
+      { text: '厨房要求实用性强,采用白色橱柜', timestamp: '2024-01-17 09:15', source: '细节讨论' }
+    ],
+    customerConcerns: [
+      { text: '担心渲染图与实际效果有差异', timestamp: '2024-01-18 16:40', resolved: true },
+      { text: '预算控制在合理范围内', timestamp: '2024-01-19 11:25', resolved: true }
+    ],
+    complaintPoints: [
+      { text: '首版方案色彩搭配不符合预期', timestamp: '2024-01-20 15:10', severity: 'medium', severityText: '中等', resolution: '已调整为客户偏好的配色方案' }
+    ],
+    projectHighlights: [
+      { text: '设计师对空间的功能分区把握精准', category: '设计亮点', praised: true },
+      { text: '材质选择符合客户品味且性价比高', category: '材质选择', praised: true },
+      { text: '渲染效果图质量优秀,客户非常满意', category: '技术表现', praised: true }
+    ],
+    communications: [
+      { timestamp: '2024-01-15 10:30', participant: '客户', type: 'requirement', typeText: '需求提出', message: '希望整体风格简约现代,注重实用性...', attachments: [] },
+      { timestamp: '2024-01-16 09:00', participant: '设计师', type: 'response', typeText: '设计反馈', message: '根据您的需求,我们建议采用...', attachments: ['方案草图.jpg'] },
+      { timestamp: '2024-01-20 14:30', participant: '客户', type: 'concern', typeText: '疑虑表达', message: '对配色方案有些疑虑...', attachments: [] }
+    ]
+  };
+  
+  // 优化建议数据
+  optimizationSuggestions: any[] = [
+    {
+      priority: 'high',
+      priorityText: '高优先级',
+      category: '需求沟通',
+      expectedImprovement: '减少30%改图次数',
+      problem: '该项目因需求理解不够深入导致改图3次,超出平均水平',
+      dataPoints: [
+        { label: '实际改图次数', value: '3次', isWarning: true },
+        { label: '平均改图次数', value: '2次', isWarning: false },
+        { label: '超出比例', value: '+50%', isWarning: true }
+      ],
+      solution: '建议在需求沟通阶段增加确认环节,通过详细的需求确认清单和参考案例,确保设计师完全理解客户需求',
+      actionPlan: [
+        '制定标准化的需求确认清单,覆盖风格、色彩、材质、功能等核心要素',
+        '在方案设计前与客户进行一次需求复核会议',
+        '准备3-5个同类型案例供客户参考,明确设计方向',
+        '使用可视化工具(如情绪板)帮助客户表达偏好'
+      ],
+      references: ['项目A-123', '项目B-456']
+    },
+    {
+      priority: 'medium',
+      priorityText: '中优先级',
+      category: '时间管理',
+      expectedImprovement: '缩短15%交付周期',
+      problem: '项目交付周期为18天,超出标准周期15天',
+      dataPoints: [
+        { label: '实际周期', value: '18天', isWarning: true },
+        { label: '标准周期', value: '15天', isWarning: false },
+        { label: '延期', value: '+3天', isWarning: true }
+      ],
+      solution: '优化建模和渲染阶段的时间分配,采用并行工作模式提高效率',
+      actionPlan: [
+        '建模和软装选择可以部分并行进行',
+        '提前准备常用材质库,减少临时查找时间',
+        '设置阶段里程碑提醒,避免单一阶段耗时过长'
+      ],
+      references: ['时间优化案例-001']
+    },
+    {
+      priority: 'low',
+      priorityText: '低优先级',
+      category: '客户体验',
+      expectedImprovement: '提升客户满意度至4.8分',
+      problem: '当前客户满意度为4.5分,仍有提升空间',
+      dataPoints: [
+        { label: '当前满意度', value: '4.5/5', isWarning: false },
+        { label: '目标满意度', value: '4.8/5', isWarning: false }
+      ],
+      solution: '增加交付过程中的沟通频率,及时展示阶段性成果',
+      actionPlan: [
+        '每个关键节点完成后主动向客户汇报进度',
+        '提供阶段性预览图,让客户参与到创作过程中',
+        '建立客户反馈快速响应机制'
+      ],
+      references: []
+    }
+  ];
+
+  switchAftercareTab(tab: string): void {
+    this.activeAftercareTab = tab;
+    console.log('切换到售后标签页:', tab);
+  }
+
+  // ==================== 自动结算相关 ====================
+  
+  // 检查项目是否已完成验收
+  isProjectAccepted(): boolean {
+    // 检查所有交付阶段是否完成
+    const deliveryStages: ProjectStage[] = ['建模', '软装', '渲染', '后期'];
+    return deliveryStages.every(stage => this.getStageStatus(stage) === 'completed');
+  }
+  
+  // 启动自动结算(只有技术人员可触发)
+  initiateAutoSettlement(): void {
+    if (this.isAutoSettling) return;
+    
+    // 权限检查
+    if (!this.isTechnicalView()) {
+      alert('⚠️ 仅技术人员可以启动自动化结算流程');
+      return;
+    }
+    
+    // 验收状态检查
+    if (!this.isProjectAccepted()) {
+      const confirmStart = confirm('项目尚未完成全部验收,确定要启动结算流程吗?');
+      if (!confirmStart) return;
+    }
+    
+    this.isAutoSettling = true;
+    console.log('启动自动化结算流程...');
+    
+    // 模拟启动各个自动化功能
+    setTimeout(() => {
+      // 1. 启动小程序支付监听
+      this.miniprogramPaymentStatus = 'active';
+      this.isSettlementInitiated = true;
+      
+      console.log('✅ 自动化结算已启动');
+      console.log('🟢 小程序支付监听已激活');
+      console.log('🔍 支付凭证智能识别已就绪');
+      console.log('📱 自动通知系统已就绪');
+      
+      // 2. 自动生成尾款结算记录
+      this.createFinalPaymentRecord();
+      
+      // 3. 通知客服跟进尾款
+      this.notifyCustomerServiceForFinalPayment();
+      
+      // 4. 设置支付监听和自动化流程
+      this.setupPaymentAutomation();
+      
+      this.isAutoSettling = false;
+      
+      // 显示启动成功消息
+      alert(`🚀 自动化结算已成功启动!
+
+✅ 已启动功能:
+• 小程序支付自动监听
+• 支付凭证智能识别  
+• 多渠道自动通知
+• 大图自动解锁
+• 已通知客服跟进尾款
+
+系统将自动处理后续支付流程。`);
+      
+    }, 2000);
+  }
+
+  // 创建尾款结算记录
+  private createFinalPaymentRecord(): void {
+    const finalPaymentRecord: Settlement = {
+      id: `settlement_${Date.now()}`,
+      projectId: this.projectId,
+      projectName: this.project?.name || '',
+      type: 'final_payment',
+      amount: this.project?.finalPaymentAmount || 0,
+      percentage: 30,
+      status: 'pending',
+      createdAt: new Date(),
+      initiatedBy: 'technical',
+      initiatedAt: new Date(),
+      notifiedCustomerService: false,
+      paymentReceived: false,
+      imagesUnlocked: false
+    };
+    
+    // 添加到结算列表
+    if (!this.settlements.find(s => s.type === 'final_payment')) {
+      this.settlements.unshift(finalPaymentRecord);
+    }
+    
+    console.log('📝 已创建尾款结算记录:', finalPaymentRecord);
+  }
+
+  // 通知客服跟进尾款
+  private notifyCustomerServiceForFinalPayment(): void {
+    const projectInfo = {
+      projectId: this.projectId,
+      projectName: this.project?.name || '未知项目',
+      customerName: this.project?.customerName || '未知客户',
+      customerPhone: this.project?.customerPhone || '',
+      finalPaymentAmount: this.project?.finalPaymentAmount || 0,
+      notificationTime: new Date(),
+      status: 'pending_followup',
+      priority: 'high',
+      autoGenerated: true,
+      message: `项目【${this.project?.name}】已完成技术验收,请及时跟进客户尾款支付。`
+    };
+
+    // 发送通知到客服系统
+    console.log('📢 正在通知客服跟进尾款...', projectInfo);
+    
+    // 模拟API调用到客服通知系统
+    // this.customerServiceAPI.addPendingTask(projectInfo).subscribe(...)
+    
+    setTimeout(() => {
+      console.log('✅ 客服通知已发送成功');
+      
+      // 更新结算记录状态
+      const settlement = this.settlements.find(s => s.type === 'final_payment');
+      if (settlement) {
+        settlement.notifiedCustomerService = true;
+      }
+    }, 500);
+  }
+  
+  // 设置支付自动化流程
+  private setupPaymentAutomation(): void {
+    console.log('⚙️ 设置支付自动化监听...');
+    
+    // 模拟支付监听(实际应使用WebSocket或轮询)
+    // 这里仅作演示
+    this.monitorPaymentStatus();
+  }
+  
+  // 监听支付状态
+  private monitorPaymentStatus(): void {
+    // 实际应该使用WebSocket连接或定时轮询API
+    // 这里仅作演示用setTimeout模拟
+    console.log('👀 开始监听支付状态...');
+    
+    // 当检测到支付完成时,自动触发后续流程
+    // this.onPaymentReceived();
+  }
+  
+  // 支付到账回调
+  onPaymentReceived(paymentInfo?: any): void {
+    console.log('💰 检测到支付到账:', paymentInfo);
+    
+    const settlement = this.settlements.find(s => s.type === 'final_payment');
+    if (!settlement) return;
+    
+    // 更新结算状态
+    settlement.status = '已结算';
+    settlement.settledAt = new Date();
+    settlement.paymentReceived = true;
+    settlement.paymentReceivedAt = new Date();
+    
+    // 自动解锁并发送大图
+    this.autoUnlockAndSendImages();
+    
+    // 通知客户和客服
+    this.sendPaymentConfirmationNotifications();
+  }
+  
+  // 自动解锁并发送大图
+  private autoUnlockAndSendImages(): void {
+    console.log('🔓 自动解锁大图...');
+    
+    // 更新结算记录
+    const settlement = this.settlements.find(s => s.type === 'final_payment');
+    if (settlement) {
+      settlement.imagesUnlocked = true;
+      settlement.imagesUnlockedAt = new Date();
+    }
+    
+    // 自动发送大图给客户(通过客服)
+    this.autoSendImagesToCustomer();
+    
+    console.log('✅ 大图已解锁并准备发送');
+  }
+  
+  // 自动发送图片给客户
+  private autoSendImagesToCustomer(): void {
+    console.log('📤 自动发送大图给客户...');
+    
+    // 收集所有渲染大图
+    const renderProcess = this.deliveryProcesses.find(p => p.id === 'rendering');
+    const images: string[] = [];
+    
+    if (renderProcess) {
+      Object.keys(renderProcess.content).forEach(spaceId => {
+        const content = renderProcess.content[spaceId];
+        if (content.images) {
+          content.images.forEach(img => {
+            images.push(img.url);
+          });
+        }
+      });
+    }
+    
+    const sendInfo = {
+      projectId: this.projectId,
+      customerName: this.project?.customerName || '',
+      images: images,
+      sendMethod: 'wechat',
+      autoGenerated: true
+    };
+    
+    console.log('📨 准备发送的大图信息:', sendInfo);
+    
+    // 调用客服系统API一键发图
+    // this.customerServiceAPI.sendImagesToCustomer(sendInfo).subscribe(...)
+  }
+  
+  // 发送支付确认通知
+  private sendPaymentConfirmationNotifications(): void {
+    console.log('📱 发送支付确认通知...');
+    
+    // 通知客户
+    const customerMessage = `尊敬的${this.project?.customerName || '客户'},您的尾款支付已确认,大图已自动解锁并发送,请查收。感谢您的信任!`;
+    
+    // 通知客服
+    const csMessage = `项目【${this.project?.name}】尾款已到账,大图已自动解锁,请一键发送给客户。`;
+    
+    console.log('📧 客户通知:', customerMessage);
+    console.log('📧 客服通知:', csMessage);
+    
+    // 实际发送通知
+    // this.notificationService.send({ to: 'customer', message: customerMessage });
+    // this.notificationService.send({ to: 'customer_service', message: csMessage });
+  }
+
+  // ==================== 全景图合成相关 ====================
+  
+  // 全景图合成数据
+  panoramicSyntheses: PanoramicSynthesis[] = [];
+  isUploadingPanoramicImages: boolean = false;
+  panoramicUploadProgress: number = 0;
+
+  // 启动全景图合成流程
+  // 上传支付凭证
+  uploadPaymentProof(): void {
+    console.log('📎 打开支付凭证上传...');
+    
+    const fileInput = document.createElement('input');
+    fileInput.type = 'file';
+    fileInput.accept = 'image/*';
+    fileInput.onchange = (event: any) => {
+      const file = (event.target as HTMLInputElement).files?.[0];
+      if (!file) return;
+      
+      console.log('📄 上传的凭证文件:', file.name);
+      alert(`📎 支付凭证已上传:${file.name}\n\n系统将自动识别支付金额和支付方式。`);
+      
+      // 模拟凭证识别和处理
+      setTimeout(() => {
+        const mockPaymentInfo = {
+          amount: this.project?.finalPaymentAmount || 5000,
+          method: '微信支付',
+          imageUrl: URL.createObjectURL(file),
+          uploadTime: new Date()
+        };
+        
+        console.log('✅ 支付凭证识别完成:', mockPaymentInfo);
+        this.onPaymentReceived(mockPaymentInfo);
+      }, 1500);
+    };
+    fileInput.click();
+  }
+  
+  startPanoramicSynthesis(): void {
+    console.log('🎨 启动全景图合成...');
+    
+    // 打开文件选择对话框,支持多文件选择
+    const fileInput = document.createElement('input');
+    fileInput.type = 'file';
+    fileInput.accept = 'image/*';
+    fileInput.multiple = true;
+    fileInput.onchange = (event: any) => {
+      const files = Array.from(event.target.files || []) as File[];
+      if (files.length === 0) return;
+      
+      console.log(`📸 选择了 ${files.length} 张图片进行合成`);
+      this.processPanoramicImages(files);
+    };
+    fileInput.click();
+  }
+  
+  // 处理全景图片上传和合成
+  private processPanoramicImages(files: File[]): void {
+    this.isUploadingPanoramicImages = true;
+    this.panoramicUploadProgress = 0;
+    
+    console.log(`📸 开始处理 ${files.length} 张全景图片...`);
+    
+    // 模拟上传进度
+    const uploadInterval = setInterval(() => {
+      this.panoramicUploadProgress += 10;
+      if (this.panoramicUploadProgress >= 100) {
+        clearInterval(uploadInterval);
+        this.panoramicUploadProgress = 100;
+        
+        // 上传完成后开始合成
+        this.synthesizePanoramicView(files);
+      }
+    }, 300);
+  }
+  
+  // 合成全景漫游
+  private synthesizePanoramicView(files: File[]): void {
+    console.log('🔄 开始合成全景漫游...');
+    
+    // 创建合成记录
+    const synthesis: PanoramicSynthesis = {
+      id: 'panoramic-' + Date.now(),
+      projectId: this.projectId,
+      projectName: this.project?.name || '未知项目',
+      spaces: [],
+      status: 'processing',
+      quality: 'high',
+      createdAt: new Date(),
+      updatedAt: new Date(),
+      progress: 0
+    };
+    
+    // 根据上传的文件创建空间列表
+    files.forEach((file, index) => {
+      // 从文件名提取空间名称(如"客厅-角度1.jpg")
+      const fileName = file.name.replace(/\.(jpg|jpeg|png|gif)$/i, '');
+      const match = fileName.match(/^(.+?)-/);
+      const spaceName = match ? match[1] : `空间${index + 1}`;
+      
+      // 根据空间名称推断类型
+      let spaceType: 'living_room' | 'bedroom' | 'kitchen' | 'bathroom' | 'dining_room' | 'study' | 'balcony' = 'living_room';
+      if (spaceName.includes('客厅')) spaceType = 'living_room';
+      else if (spaceName.includes('卧室')) spaceType = 'bedroom';
+      else if (spaceName.includes('厨房')) spaceType = 'kitchen';
+      else if (spaceName.includes('卫生间') || spaceName.includes('浴室')) spaceType = 'bathroom';
+      else if (spaceName.includes('餐厅')) spaceType = 'dining_room';
+      else if (spaceName.includes('书房')) spaceType = 'study';
+      else if (spaceName.includes('阳台')) spaceType = 'balcony';
+      
+      synthesis.spaces.push({
+        id: `space_${Date.now()}_${index}`,
+        name: spaceName,
+        type: spaceType,
+        imageCount: 1,
+        viewAngle: fileName
+      });
+    });
+    
+    this.panoramicSyntheses.unshift(synthesis);
+    
+    // 模拟KR Panel合成进度
+    let progress = 0;
+    const synthesisInterval = setInterval(() => {
+      progress += 15;
+      synthesis.progress = Math.min(progress, 100);
+      synthesis.updatedAt = new Date();
+      
+      if (progress >= 100) {
+        clearInterval(synthesisInterval);
+        
+        // 合成完成
+        synthesis.status = 'completed';
+        synthesis.completedAt = new Date();
+        synthesis.previewUrl = this.generateMockPanoramicUrl(synthesis.id);
+        synthesis.downloadUrl = this.generateMockDownloadUrl(synthesis.id);
+        synthesis.renderTime = 120 + Math.floor(Math.random() * 60);
+        synthesis.fileSize = files.reduce((sum, f) => sum + f.size, 0);
+        
+        this.isUploadingPanoramicImages = false;
+        
+        console.log('✅ 全景图合成完成:', synthesis);
+        
+        // 自动生成分享链接
+        this.generatePanoramicShareLink(synthesis);
+      }
+    }, 500);
+  }
+  
+  // 生成全景图分享链接
+  private generatePanoramicShareLink(synthesis: PanoramicSynthesis): void {
+    const shareLink = `https://panoramic.example.com/view/${synthesis.id}`;
+    synthesis.shareLink = shareLink;
+    
+    console.log('🔗 全景图分享链接:', shareLink);
+    
+    // 自动通知客服发送给客户
+    this.notifyCustomerServiceForPanoramicLink(synthesis);
+  }
+  
+  // 通知客服发送全景图链接
+  private notifyCustomerServiceForPanoramicLink(synthesis: PanoramicSynthesis): void {
+    const notification = {
+      type: 'panoramic_ready',
+      projectId: this.projectId,
+      projectName: synthesis.projectName,
+      shareLink: synthesis.shareLink,
+      message: `项目【${synthesis.projectName}】的全景漫游已生成完成,请发送给客户查看。`,
+      timestamp: new Date()
+    };
+    
+    console.log('📢 通知客服发送全景图链接:', notification);
+    
+    // 调用客服通知API
+    // this.customerServiceAPI.notifyPanoramicReady(notification).subscribe(...)
+  }
+  
+  // 生成模拟全景图URL
+  private generateMockPanoramicUrl(id: string): string {
+    return `https://panoramic.example.com/preview/${id}`;
+  }
+  
+  // 生成模拟下载URL
+  private generateMockDownloadUrl(id: string): string {
+    return `https://panoramic.example.com/download/${id}`;
+  }
+
+  // 查看全景图画廊
+  viewPanoramicGallery(): void {
+    console.log('打开全景图画廊');
+    
+    if (this.panoramicSyntheses.length === 0) {
+      alert('暂无全景图记录');
+      return;
+    }
+    
+    // 显示全景图列表
+    const galleryInfo = this.panoramicSyntheses.map((s, i) => 
+      `${i + 1}. ${s.projectName} - ${s.status === 'completed' ? '已完成' : '处理中'} (${s.spaces.length}个空间)`
+    ).join('\n');
+    
+    alert(`全景图画廊\n\n${galleryInfo}\n\n点击查看详情功能开发中...`);
+  }
+  
+  // 复制全景图链接
+  copyPanoramicLink(synthesis: PanoramicSynthesis): void {
+    if (!synthesis.shareLink) {
+      alert('全景图链接尚未生成');
+      return;
+    }
+    
+    navigator.clipboard.writeText(synthesis.shareLink).then(() => {
+      alert(`✅ 全景图链接已复制!\n\n${synthesis.shareLink}`);
+    }).catch(() => {
+      alert(`全景图链接:\n${synthesis.shareLink}`);
+    });
+  }
+
+  // ==================== 评价统计相关 ====================
+  
+  // 评价统计数据
+  reviewStats: {
+    overallScore: number;
+    timelinessScore: number;
+    qualityScore: number;
+    communicationScore: number;
+  } = {
+    overallScore: 4.8,
+    timelinessScore: 4.7,
+    qualityScore: 4.9,
+    communicationScore: 4.6
+  };
+
+  // ==================== 客户评价相关 ====================
+  
+  // 生成评价链接
+  generateReviewLink(): void {
+    console.log('📋 生成客户评价链接...');
+    
+    // 生成唯一的评价令牌
+    const reviewToken = this.generateReviewToken();
+    const reviewLink = `https://review.yss.com/project/${this.projectId}?token=${reviewToken}`;
+    
+    // 保存评价链接记录
+    const reviewRecord = {
+      projectId: this.projectId,
+      projectName: this.project?.name || '',
+      customerName: this.project?.customerName || '',
+      reviewLink: reviewLink,
+      token: reviewToken,
+      createdAt: new Date(),
+      expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天有效期
+      status: 'active',
+      accessed: false
+    };
+    
+    console.log('✅ 评价链接已生成:', reviewRecord);
+    
+    // 复制到剪贴板
+    navigator.clipboard.writeText(reviewLink).then(() => {
+      alert(`✅ 评价链接已复制到剪贴板!\n\n链接:${reviewLink}\n\n有效期:30天\n\n请通过企业微信发送给客户`);
+    }).catch(() => {
+      alert(`评价链接:\n\n${reviewLink}\n\n有效期:30天\n\n请通过企业微信发送给客户`);
+    });
+    
+    // 通知客服发送评价链接
+    this.notifyCustomerServiceForReviewLink(reviewRecord);
+  }
+  
+  // 生成评价令牌
+  private generateReviewToken(): string {
+    return `review_${this.projectId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+  }
+  
+  // 通知客服发送评价链接
+  private notifyCustomerServiceForReviewLink(reviewRecord: any): void {
+    const notification = {
+      type: 'review_link_ready',
+      projectId: this.projectId,
+      projectName: reviewRecord.projectName,
+      customerName: reviewRecord.customerName,
+      reviewLink: reviewRecord.reviewLink,
+      message: `项目【${reviewRecord.projectName}】的客户评价链接已生成,请发送给客户。`,
+      timestamp: new Date()
+    };
+    
+    console.log('📢 通知客服发送评价链接:', notification);
+    
+    // 调用客服通知API
+    // this.customerServiceAPI.notifyReviewLinkReady(notification).subscribe(...)
+  }
+  // 确认客户评价完成
+  confirmCustomerReview(): void {
+    console.log('✅ 确认客户评价完成');
+    
+    // 更新项目状态
+    if (this.project) {
+      this.project.customerReviewCompleted = true;
+      this.project.customerReviewCompletedAt = new Date();
+    }
+    
+    alert('✅ 客户评价已确认完成!');
+    
+    // 可选:自动进入下一阶段
+    // this.advanceToNextStage('客户评价');
+  }
+
+  // ==================== 投诉管理相关 ====================
+  
+  // 关键词监控配置
+  complaintKeywords: string[] = ['不满意', '投诉', '退款', '差评', '质量问题', '延期', '态度差'];
+  isKeywordMonitoringActive: boolean = false;
+  
+  // 手动创建投诉
+  createComplaintManually(): void {
+    console.log('📝 手动创建投诉');
+    
+    // 弹出创建投诉表单
+    const complaintReason = prompt('请输入投诉原因:');
+    if (!complaintReason || complaintReason.trim() === '') return;
+    
+    const complaintStage = prompt('请输入投诉环节(如:需求沟通、建模、渲染等):') || '未指定';
+    
+    // 创建投诉记录
+    const complaint: any = {
+      id: `complaint_${Date.now()}`,
+      projectId: this.projectId,
+      projectName: this.project?.name || '',
+      customerName: this.project?.customerName || '',
+      type: '人工创建',
+      stage: complaintStage,
+      reason: complaintReason,
+      severity: 'medium',
+      status: 'pending',
+      createdBy: 'manual',
+      createdAt: new Date(),
+      handler: '',
+      resolution: '',
+      resolvedAt: null
+    };
+    
+    // 智能标注核心问题
+    complaint.tags = this.analyzeComplaintTags(complaintReason);
+    
+    // 添加到投诉列表
+    this.exceptionHistories.unshift(complaint);
+    
+    console.log('✅ 投诉记录已创建:', complaint);
+    
+    alert(`✅ 投诉记录已创建!\n\n投诉环节:${complaintStage}\n核心问题:${complaint.tags.join('、')}\n\n系统将自动跟踪处理进度。`);
+    
+    // 通知相关人员
+    this.notifyComplaintHandlers(complaint);
+  }
+  
+  // 分析投诉标签
+  private analyzeComplaintTags(reason: string): string[] {
+    const tags: string[] = [];
+    
+    const tagPatterns = {
+      '需求理解': ['需求', '理解', '沟通', '误解'],
+      '设计质量': ['质量', '效果', '不好', '不满意'],
+      '交付延期': ['延期', '超时', '慢', '着急'],
+      '服务态度': ['态度', '不礼貌', '敷衍', '回复慢'],
+      '价格问题': ['价格', '费用', '贵', '退款']
+    };
+    
+    Object.entries(tagPatterns).forEach(([tag, keywords]) => {
+      if (keywords.some(keyword => reason.includes(keyword))) {
+        tags.push(tag);
+      }
+    });
+    
+    return tags.length > 0 ? tags : ['其他'];
+  }
+  
+  // 通知投诉处理人员
+  private notifyComplaintHandlers(complaint: any): void {
+    const notification = {
+      type: 'new_complaint',
+      projectId: this.projectId,
+      complaintId: complaint.id,
+      projectName: complaint.projectName,
+      customerName: complaint.customerName,
+      severity: complaint.severity,
+      tags: complaint.tags,
+      message: `项目【${complaint.projectName}】收到新投诉,请及时处理。`,
+      timestamp: new Date()
+    };
+    
+    console.log('📢 通知投诉处理人员:', notification);
+    
+    // 调用通知API
+    // this.complaintService.notifyHandlers(notification).subscribe(...)
+  }
+
+  // 设置关键词监控
+  setupKeywordMonitoring(): void {
+    console.log('⚙️ 设置关键词监控');
+    
+    if (this.isKeywordMonitoringActive) {
+      const confirmStop = confirm('关键词监控已激活,是否停止监控?');
+      if (confirmStop) {
+        this.isKeywordMonitoringActive = false;
+        alert('✅ 关键词监控已停止');
+      }
+      return;
+    }
+    
+    // 显示当前监控关键词
+    const currentKeywords = this.complaintKeywords.join('、');
+    const newKeywords = prompt(`当前监控关键词:\n\n${currentKeywords}\n\n请输入要添加的关键词(多个关键词用逗号分隔):`);
+    
+    if (newKeywords && newKeywords.trim()) {
+      const keywords = newKeywords.split(/[,,、]/).map(k => k.trim()).filter(k => k);
+      this.complaintKeywords = [...new Set([...this.complaintKeywords, ...keywords])];
+    }
+    
+    // 激活监控
+    this.isKeywordMonitoringActive = true;
+    this.startKeywordMonitoring();
+    
+    alert(`✅ 关键词监控已激活!\n\n监控关键词:${this.complaintKeywords.join('、')}\n\n系统将自动检测企业微信群消息中的关键词并创建投诉记录。`);
+  }
+  
+  // 开始关键词监控
+  private startKeywordMonitoring(): void {
+    console.log('👀 开始关键词监控...');
+    console.log('监控关键词:', this.complaintKeywords);
+    
+    // 模拟监控企业微信消息(实际应使用企业微信API或webhook)
+    // 这里仅作演示
+    
+    // 监控到关键词后自动创建投诉
+    // this.onKeywordDetected(message, keyword);
+  }
+  
+  // 关键词检测回调
+  onKeywordDetected(message: string, keyword: string): void {
+    console.log('🚨 检测到关键词:', keyword);
+    console.log('消息内容:', message);
+    
+    // 自动创建投诉记录
+    const complaint: any = {
+      id: `complaint_auto_${Date.now()}`,
+      projectId: this.projectId,
+      projectName: this.project?.name || '',
+      customerName: this.project?.customerName || '',
+      type: '关键词自动抓取',
+      keyword: keyword,
+      message: message,
+      severity: this.assessComplaintSeverity(message),
+      status: 'pending',
+      createdBy: 'auto',
+      createdAt: new Date(),
+      handler: '',
+      resolution: '',
+      resolvedAt: null
+    };
+    
+    // 智能标注问题环节和核心问题
+    complaint.stage = this.identifyComplaintStage(message);
+    complaint.tags = this.analyzeComplaintTags(message);
+    
+    // 添加到投诉列表
+    this.exceptionHistories.unshift(complaint);
+    
+    console.log('✅ 自动投诉记录已创建:', complaint);
+    
+    // 实时通知相关人员
+    this.notifyComplaintHandlers(complaint);
+  }
+  
+  // 评估投诉严重程度
+  private assessComplaintSeverity(message: string): 'low' | 'medium' | 'high' {
+    const highSeverityKeywords = ['退款', '投诉', '举报', '律师', '曝光'];
+    const mediumSeverityKeywords = ['不满意', '差评', '质量问题'];
+    
+    if (highSeverityKeywords.some(k => message.includes(k))) return 'high';
+    if (mediumSeverityKeywords.some(k => message.includes(k))) return 'medium';
+    return 'low';
+  }
+  
+  // 识别投诉环节
+  private identifyComplaintStage(message: string): string {
+    const stageKeywords = {
+      '需求沟通': ['需求', '沟通', '理解'],
+      '方案确认': ['方案', '设计', '效果'],
+      '建模': ['建模', '模型', '白模'],
+      '软装': ['软装', '家具', '配饰'],
+      '渲染': ['渲染', '出图', '大图'],
+      '交付': ['交付', '发送', '收到']
+    };
+    
+    for (const [stage, keywords] of Object.entries(stageKeywords)) {
+      if (keywords.some(k => message.includes(k))) {
+        return stage;
+      }
+    }
+    
+    return '未指定';
+  }
+  
+  // 确认投诉处理完成
+  confirmComplaint(): void {
+    console.log('✅ 确认投诉处理完成');
+    
+    // 检查是否有未处理的投诉
+    const pendingComplaints = this.exceptionHistories.filter(c => c.status === '待处理');
+    
+    if (pendingComplaints.length > 0) {
+      const confirmAnyway = confirm(`还有 ${pendingComplaints.length} 个投诉未处理,确定要标记为已完成吗?`);
+      if (!confirmAnyway) return;
+    }
+    
+    alert('✅ 所有投诉已确认处理完成!');
+  }
+
+  // 处理评价表单提交
+  onReviewSubmitted(reviewData: any): void {
+    console.log('客户评价已提交:', reviewData);
+    
+    // 这里应该调用API将评价数据保存到服务器
+    // 模拟API调用
+    setTimeout(() => {
+      alert('客户评价提交成功!评价数据已保存。');
+      
+      // 更新本地反馈数据
+      const newFeedback: CustomerFeedback = {
+        id: Date.now().toString(),
+        customerName: '客户', // 应该从项目信息中获取
+        rating: reviewData.overallRating,
+        content: reviewData.improvementSuggestions || '无具体建议',
+        createdAt: new Date(),
+        status: '已解决',
+        isSatisfied: reviewData.overallRating >= 4,
+        projectId: this.projectId
+      };
+      
+      this.feedbacks = [...this.feedbacks, newFeedback];
+      
+      // 自动标记客户评价完成
+      this.confirmCustomerReview();
+    }, 1000);
+  }
+
+  // 处理评价表单保存草稿
+  onReviewSaved(reviewData: any): void {
+    console.log('客户评价草稿已保存:', reviewData);
+    
+    // 这里应该调用API将草稿数据保存到服务器
+    // 模拟API调用
+    setTimeout(() => {
+      alert('评价草稿保存成功!您可以稍后继续完善。');
+    }, 500);
+  }
+
+  // ============ 缺少的方法实现 ============
+  
+  // 初始化售后模块数据
+  private initializeAftercareData(): void {
+    // 初始化一些示例全景图合成记录
+    this.panoramicSyntheses = [
+      {
+        id: 'panoramic_001',
+        projectId: this.projectId,
+        projectName: '示例项目',
+        spaces: [
+          { id: 'space_001', name: '客厅', type: 'living_room' as const, imageCount: 3, viewAngle: '客厅-角度1' },
+          { id: 'space_002', name: '卧室', type: 'bedroom' as const, imageCount: 2, viewAngle: '卧室-角度1' }
+        ],
+        status: 'completed' as const,
+        quality: 'high' as const,
+        createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
+        updatedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
+        completedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
+        previewUrl: 'https://panoramic.example.com/preview/panoramic_001',
+        downloadUrl: 'https://panoramic.example.com/download/panoramic_001',
+        shareLink: 'https://panoramic.example.com/view/panoramic_001',
+        renderTime: 135,
+        fileSize: 52428800,
+        progress: 100
+      }
+    ];
+    
+    // 初始化一些示例结算记录
+    if (this.settlements.length === 0) {
+      this.settlements = [
+        {
+          id: 'settlement_001',
+          projectId: this.projectId,
+          projectName: '示例项目',
+          type: 'deposit',
+          amount: 5000,
+          percentage: 30,
+          status: '已结算',
+          createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
+          settledAt: new Date(Date.now() - 28 * 24 * 60 * 60 * 1000)
+        },
+        {
+          id: 'settlement_002',
+          projectId: this.projectId,
+          projectName: '示例项目',
+          type: 'progress',
+          amount: 7000,
+          percentage: 40,
+          status: '已结算',
+          createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000),
+          settledAt: new Date(Date.now() - 13 * 24 * 60 * 60 * 1000)
+        }
+      ];
+    }
+    
+    // 初始化一些示例客户反馈
+    if (this.feedbacks.length === 0) {
+      this.feedbacks = [
+        {
+          id: 'feedback_001',
+          projectId: this.projectId,
+          customerName: '张先生',
+          rating: 5,
+          content: '设计师非常专业,效果图很满意!',
+          isSatisfied: true,
+          status: '已解决',
+          createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000)
+        }
+      ];
+    }
+    
+    // 初始化一些示例投诉记录
+    if (this.exceptionHistories.length === 0) {
+      this.exceptionHistories = [];
+    }
+  }
+  
+  // 初始化表单
+  initializeForms(): void {
+    // 初始化订单创建表单(必填项)
+    this.orderCreationForm = this.fb.group({
+      orderAmount: ['', [Validators.required, Validators.min(0)]],
+      smallImageDeliveryTime: ['', Validators.required],
+      decorationType: ['', Validators.required],
+      requirementReason: ['', Validators.required],
+      isMultiDesigner: [false] // 移除requiredTrue验证,改为普通布尔值
+    });
+
+    // 初始化可选信息表单
+    this.optionalForm = this.fb.group({
+      largeImageDeliveryTime: [''],
+      spaceRequirements: [''],
+      designAngles: [''],
+      specialAreaHandling: [''],
+      materialRequirements: [''],
+      lightingRequirements: ['']
+    });
+  }
+
+  // 检查是否可以创建订单
+  canCreateOrder(): boolean {
+    return this.orderCreationForm ? this.orderCreationForm.valid : false;
+  }
+
+  // 创建订单
+  createOrder(): void {
+    if (!this.canCreateOrder()) {
+      // 标记所有字段为已触摸,以显示验证错误
+      this.orderCreationForm.markAllAsTouched();
+      return;
+    }
+
+    const orderData = {
+      ...this.orderCreationForm.value,
+      ...this.optionalForm.value,
+      customerInfo: this.orderCreationData?.customerInfo,
+      quotationData: this.quotationData,
+      designerAssignment: this.designerAssignmentData
+    };
+
+    console.log('创建订单:', orderData);
+    
+    // 这里应该调用API创建订单
+    // 模拟API调用
+    setTimeout(() => {
+      alert('订单创建成功!');
+      // 订单创建成功后自动切换到下一环节
+      this.advanceToNextStage('订单创建');
+    }, 500);
+  }
+  
+  // 处理空间文件选择
+  onSpaceFileSelected(event: Event, processId: string, spaceId: string): void {
+    const input = event.target as HTMLInputElement;
+    if (!input.files || input.files.length === 0) return;
+    
+    const files = Array.from(input.files);
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return;
+    
+    files.forEach(file => {
+      if (/\.(jpg|jpeg|png)$/i.test(file.name)) {
+        const imageItem = this.makeImageItem(file);
+        process.content[spaceId].images.push({
+          id: imageItem.id,
+          name: imageItem.name,
+          url: imageItem.url,
+          size: this.formatFileSize(file.size)
+        });
+      }
+    });
+    
+    // 清空输入
+    input.value = '';
+  }
+
+
+
+  // 更新模型检查项状态
+  updateModelCheckItem(itemId: string, isPassed: boolean): void {
+    const item = this.modelCheckItems.find(i => i.id === itemId);
+    if (item) {
+      item.isPassed = isPassed;
+      console.log(`模型检查项 ${item.name} 状态更新为: ${isPassed ? '已通过' : '待处理'}`);
+    }
+  }
+
+  // 删除空间图片
+  removeSpaceImage(processId: string, spaceId: string, imageId: string): void {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (process && process.content[spaceId]) {
+      const images = process.content[spaceId].images;
+      const imageIndex = images.findIndex(img => img.id === imageId);
+      if (imageIndex > -1) {
+        // 释放URL资源
+        const image = images[imageIndex];
+        if (image.url && image.url.startsWith('blob:')) {
+          URL.revokeObjectURL(image.url);
+        }
+        // 从数组中移除
+        images.splice(imageIndex, 1);
+        console.log(`已删除空间图片: ${processId}/${spaceId}/${imageId}`);
+      }
+    }
+  }
+
+  // 项目复盘相关方法
+  getReviewStatus(): 'not_started' | 'generating' | 'completed' {
+    if (this.isGeneratingReview) return 'generating';
+    if (this.projectReview) return 'completed';
+    return 'not_started';
+  }
+
+  getReviewStatusText(): string {
+    const status = this.getReviewStatus();
+    switch (status) {
+      case 'not_started': return '未开始';
+      case 'generating': return '生成中';
+      case 'completed': return '已完成';
+      default: return '未知';
+    }
+  }
+
+  getScoreClass(score: number): string {
+    if (score >= 90) return 'excellent';
+    if (score >= 80) return 'good';
+    if (score >= 70) return 'average';
+    return 'poor';
+  }
+
+  getExecutionStatusText(status: 'excellent' | 'good' | 'average' | 'poor'): string {
+    switch (status) {
+      case 'excellent': return '优秀';
+      case 'good': return '良好';
+      case 'average': return '一般';
+      case 'poor': return '较差';
+      default: return '未知';
+    }
+  }
+
+  generateReviewReport(): void {
+    if (this.isGeneratingReview) return;
+    
+    this.isGeneratingReview = true;
+    
+    // 基于真实项目数据生成复盘报告
+    setTimeout(() => {
+      const sopAnalysisData = this.analyzeSopExecution();
+      const experienceInsights = this.generateExperienceInsights();
+      const performanceMetrics = this.calculatePerformanceMetrics();
+      const plannedBudget = this.quotationData.totalAmount || 150000;
+      const actualBudget = this.calculateActualBudget();
+      
+      this.projectReview = {
+        id: 'review_' + Date.now(),
+        projectId: this.projectId,
+        generatedAt: new Date(),
+        overallScore: this.calculateOverallScore(),
+        sopAnalysis: sopAnalysisData,
+        keyHighlights: experienceInsights.keyHighlights,
+        improvementSuggestions: experienceInsights.improvementSuggestions,
+        customerSatisfaction: {
+          overallRating: this.reviewStats.overallScore,
+          feedback: this.detailedReviews.length > 0 ? this.detailedReviews[0].overallFeedback : '客户反馈良好,对项目整体满意',
+          responseTime: this.calculateAverageResponseTime(),
+          completionTime: this.calculateProjectDuration()
+        },
+        teamPerformance: performanceMetrics,
+        budgetAnalysis: {
+          plannedBudget: plannedBudget,
+          actualBudget: actualBudget,
+          variance: this.calculateBudgetVariance(plannedBudget, actualBudget),
+          costBreakdown: [
+            { category: '设计费', planned: plannedBudget * 0.3, actual: actualBudget * 0.3 },
+            { category: '材料费', planned: plannedBudget * 0.6, actual: actualBudget * 0.57 },
+            { category: '人工费', planned: plannedBudget * 0.1, actual: actualBudget * 0.13 }
+          ]
+        },
+        lessonsLearned: experienceInsights.lessonsLearned,
+        recommendations: experienceInsights.recommendations
+      };
+      
+      this.isGeneratingReview = false;
+      alert('复盘报告生成完成!基于真实SOP执行数据和智能分析生成。');
+    }, 3000);
+  }
+
+  regenerateReviewReport(): void {
+    this.projectReview = null;
+    this.generateReviewReport();
+  }
+
+  exportReviewReport(): void {
+    if (!this.projectReview) return;
+    
+    const exportRequest: ReviewReportExportRequest = {
+      projectId: this.projectId,
+      reviewId: this.projectReview.id,
+      format: 'pdf',
+      includeCharts: true,
+      includeDetails: true,
+      language: 'zh-CN'
+    };
+    
+    this.projectReviewService.exportReviewReport(exportRequest).subscribe({
+      next: (response) => {
+        if (response.success && response.downloadUrl) {
+          // 创建下载链接
+          const link = document.createElement('a');
+          link.href = response.downloadUrl;
+          link.download = response.fileName || `复盘报告_${this.project?.name || '项目'}_${new Date().toISOString().split('T')[0]}.pdf`;
+          document.body.appendChild(link);
+          link.click();
+          document.body.removeChild(link);
+          
+          alert('复盘报告导出成功!');
+        } else {
+          alert('导出失败:' + (response.message || '未知错误'));
+        }
+      },
+      error: (error) => {
+        console.error('导出复盘报告失败:', error);
+        alert('导出失败,请稍后重试');
+      }
+    });
+  }
+
+  shareReviewReport(): void {
+    if (!this.projectReview) return;
+    
+    const shareRequest: ReviewReportShareRequest = {
+      projectId: this.projectId,
+      reviewId: this.projectReview.id,
+      shareType: 'link',
+      expirationDays: 30,
+      allowDownload: true,
+      requirePassword: false
+    };
+    
+    this.projectReviewService.shareReviewReport(shareRequest).subscribe({
+      next: (response) => {
+        if (response.success && response.shareUrl) {
+          // 复制到剪贴板
+          if (navigator.clipboard) {
+            navigator.clipboard.writeText(response.shareUrl).then(() => {
+              alert(`复盘报告分享链接已复制到剪贴板!\n链接有效期:${response.expirationDate ? new Date(response.expirationDate).toLocaleDateString() : '30天'}`);
+            }).catch(() => {
+              alert(`复盘报告分享链接:\n${response.shareUrl}\n\n链接有效期:${response.expirationDate ? new Date(response.expirationDate).toLocaleDateString() : '30天'}`);
+            });
+          } else {
+            alert(`复盘报告分享链接:\n${response.shareUrl}\n\n链接有效期:${response.expirationDate ? new Date(response.expirationDate).toLocaleDateString() : '30天'}`);
+          }
+        } else {
+          alert('分享失败:' + (response.message || '未知错误'));
+        }
+      },
+      error: (error) => {
+        console.error('分享复盘报告失败:', error);
+        alert('分享失败,请稍后重试');
+      }
+    });
+  }
+
+  // 项目复盘工具方法
+  getMaxDuration(): number {
+    if (!this.sopStagesData || this.sopStagesData.length === 0) return 1;
+    return Math.max(...this.sopStagesData.map(s => Math.max(s.plannedDuration, s.actualDuration)));
+  }
+  
+  getAverageScore(): number {
+    if (!this.sopStagesData || this.sopStagesData.length === 0) return 0;
+    const sum = this.sopStagesData.reduce((acc, s) => acc + s.score, 0);
+    return Math.round(sum / this.sopStagesData.length);
+  }
+  
+  getSuggestionCountByPriority(priority: string): number {
+    if (!this.optimizationSuggestions) return 0;
+    return this.optimizationSuggestions.filter(s => s.priority === priority).length;
+  }
+  
+  getAverageImprovementPercent(): number {
+    if (!this.optimizationSuggestions || this.optimizationSuggestions.length === 0) return 0;
+    return 25;
+  }
+  
+  acceptSuggestion(suggestion: any): void {
+    console.log('采纳建议:', suggestion);
+  }
+  
+  viewSuggestionDetail(suggestion: any): void {
+    console.log('查看建议详情:', suggestion);
+  }
+
+  // 分析SOP执行情况
+  private analyzeSopExecution(): any[] {
+    const sopStages = [
+      { name: '需求沟通', planned: 3, actual: 2.5 },
+      { name: '方案确认', planned: 5, actual: 4 },
+      { name: '建模', planned: 7, actual: 8 },
+      { name: '软装', planned: 3, actual: 3.5 },
+      { name: '渲染', planned: 5, actual: 4.5 },
+      { name: '后期', planned: 2, actual: 2 }
+    ];
+
+    return sopStages.map(stage => {
+      const variance = ((stage.actual - stage.planned) / stage.planned) * 100;
+      let executionStatus: 'excellent' | 'good' | 'average' | 'poor';
+      let score: number;
+
+      if (variance <= -10) {
+        executionStatus = 'excellent';
+        score = 95;
+      } else if (variance <= 0) {
+        executionStatus = 'good';
+        score = 85;
+      } else if (variance <= 20) {
+        executionStatus = 'average';
+        score = 70;
+      } else {
+        executionStatus = 'poor';
+        score = 50;
+      }
+
+      const issues: string[] = [];
+      if (variance > 20) {
+        issues.push('执行时间超出计划较多');
+      }
+      if (stage.name === '建模' && variance > 0) {
+        issues.push('建模阶段需要优化流程');
+      }
+
+      return {
+        stageName: stage.name,
+        plannedDuration: stage.planned,
+        actualDuration: stage.actual,
+        score,
+        executionStatus,
+        issues: issues.length > 0 ? issues : undefined
+      };
+    });
+  }
+
+  // 生成经验洞察
+  private generateExperienceInsights(): { keyHighlights: string[]; improvementSuggestions: string[]; lessonsLearned: string[]; recommendations: string[] } {
+    return {
+      keyHighlights: [
+        '需求沟通阶段效率显著提升,客户满意度高',
+        '渲染质量获得客户高度认可',
+        '团队协作配合默契,沟通顺畅',
+        '项目交付时间控制良好'
+      ],
+      improvementSuggestions: [
+        '建模阶段可以进一步优化工作流程',
+        '加强前期需求确认的深度和准确性',
+        '建立更完善的质量检查机制',
+        '提升跨部门协作效率'
+      ],
+      lessonsLearned: [
+        '充分的前期沟通能显著减少后期修改',
+        '标准化流程有助于提高执行效率',
+        '及时的客户反馈对项目成功至关重要',
+        '团队技能匹配度直接影响项目质量'
+      ],
+      recommendations: [
+        '建议在类似项目中复用成功的沟通模式',
+        '可以将本项目的渲染标准作为团队参考',
+        '建议建立项目经验知识库',
+        '推荐定期进行团队技能培训'
+      ]
+    };
+  }
+
+  // 计算绩效指标
+  private calculatePerformanceMetrics(): { designerScore: number; communicationScore: number; timelinessScore: number; qualityScore: number } {
+    return {
+      designerScore: 88,
+      communicationScore: 92,
+      timelinessScore: 85,
+      qualityScore: 90
+    };
+  }
+
+  // 计算总体评分
+  private calculateOverallScore(): number {
+    const metrics = this.calculatePerformanceMetrics();
+    return Math.round((metrics.designerScore + metrics.communicationScore + metrics.timelinessScore + metrics.qualityScore) / 4);
+  }
+
+  // 计算平均响应时间
+  private calculateAverageResponseTime(): number {
+    // 模拟计算平均响应时间(小时)
+    return 2.5;
+  }
+
+  // 计算项目持续时间
+  private calculateProjectDuration(): number {
+    // 模拟计算项目持续时间(天)
+    return 28;
+  }
+
+  // 计算实际预算
+  private calculateActualBudget(): number {
+    // 基于订单金额计算实际预算
+    return this.orderAmount || 150000;
+  }
+
+  // 计算预算偏差
+  private calculateBudgetVariance(plannedBudget: number, actualBudget: number): number {
+    return ((actualBudget - plannedBudget) / plannedBudget) * 100;
+  }
+
+  formatDateTime(date: Date): string {
+    return date.toLocaleString('zh-CN', {
+      year: 'numeric',
+      month: '2-digit',
+      day: '2-digit',
+      hour: '2-digit',
+      minute: '2-digit'
+    });
+  }
+
+  // ============ 空间管理相关方法 ============
+  
+  // 添加新空间
+  addSpace(processId: string): void {
+    const spaceName = this.newSpaceName[processId]?.trim();
+    if (!spaceName) return;
+    
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process) return;
+    
+    // 生成新的空间ID
+    const spaceId = `space_${Date.now()}`;
+    
+    // 添加到spaces数组
+    const newSpace: DeliverySpace = {
+      id: spaceId,
+      name: spaceName,
+      isExpanded: false,
+      order: process.spaces.length + 1
+    };
+    
+    process.spaces.push(newSpace);
+    
+    // 初始化content数据
+    process.content[spaceId] = {
+      images: [],
+      progress: 0,
+      status: 'pending',
+      notes: '',
+      lastUpdated: new Date()
+    };
+    
+    // 清空输入框并隐藏
+    this.newSpaceName[processId] = '';
+    this.showAddSpaceInput[processId] = false;
+    
+    console.log(`已添加空间: ${spaceName} 到流程 ${process.name}`);
+  }
+  
+  // 取消添加空间
+  cancelAddSpace(processId: string): void {
+    this.newSpaceName[processId] = '';
+    this.showAddSpaceInput[processId] = false;
+  }
+  
+  // 获取指定流程的活跃空间列表
+  getActiveProcessSpaces(processId: string): DeliverySpace[] {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process) return [];
+    return process.spaces.sort((a, b) => a.order - b.order);
+  }
+  
+  // 切换空间展开状态
+  toggleSpace(processId: string, spaceId: string): void {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process) return;
+    
+    const space = process.spaces.find(s => s.id === spaceId);
+    if (space) {
+      space.isExpanded = !space.isExpanded;
+    }
+  }
+  
+  // 获取空间进度
+  getSpaceProgress(processId: string, spaceId: string): number {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return 0;
+    
+    return process.content[spaceId].progress || 0;
+  }
+  
+  // 删除空间
+  removeSpace(processId: string, spaceId: string): void {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process) return;
+    
+    // 从spaces数组中移除
+    const spaceIndex = process.spaces.findIndex(s => s.id === spaceId);
+    if (spaceIndex > -1) {
+      const spaceName = process.spaces[spaceIndex].name;
+      process.spaces.splice(spaceIndex, 1);
+      
+      // 清理content数据
+      if (process.content[spaceId]) {
+        // 释放图片URL资源
+        process.content[spaceId].images.forEach(img => {
+          if (img.url && img.url.startsWith('blob:')) {
+            URL.revokeObjectURL(img.url);
+          }
+        });
+        delete process.content[spaceId];
+      }
+      
+      console.log(`已删除空间: ${spaceName} 从流程 ${process.name}`);
+    }
+  }
+  
+  // 触发空间文件输入
+  triggerSpaceFileInput(processId: string, spaceId: string): void {
+    const inputId = `space-file-input-${processId}-${spaceId}`;
+    const input = document.getElementById(inputId) as HTMLInputElement;
+    if (input) {
+      input.click();
+    }
+  }
+  
+  // 处理空间文件拖拽
+  onSpaceFileDrop(event: DragEvent, processId: string, spaceId: string): void {
+    event.preventDefault();
+    event.stopPropagation();
+    
+    const files = event.dataTransfer?.files;
+    if (!files || files.length === 0) return;
+    
+    this.handleSpaceFiles(Array.from(files), processId, spaceId);
+  }
+  
+  // 处理空间文件
+  private handleSpaceFiles(files: File[], processId: string, spaceId: string): void {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return;
+    
+    files.forEach(file => {
+      if (/\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name)) {
+        const imageItem = this.makeImageItem(file);
+        process.content[spaceId].images.push({
+          id: imageItem.id,
+          name: imageItem.name,
+          url: imageItem.url,
+          size: this.formatFileSize(file.size),
+          reviewStatus: 'pending'
+        });
+        
+        // 更新进度
+        this.updateSpaceProgress(processId, spaceId);
+      }
+    });
+  }
+  
+  // 获取空间图片列表
+  getSpaceImages(processId: string, spaceId: string): Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected' }> {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return [];
+    
+    return process.content[spaceId].images || [];
+  }
+  
+  // 确认阶段上传并推进工作流
+  confirmStageUpload(stageId: string): void {
+    const stageMap: { [key: string]: ProjectStage } = {
+      'modeling': '建模',
+      'softDecor': '软装', 
+      'rendering': '渲染',
+      'postProduction': '后期'
+    };
+    
+    const currentStage = stageMap[stageId];
+    if (!currentStage) return;
+    
+    // 检查当前阶段是否有上传的图片
+    const process = this.deliveryProcesses.find(p => p.id === stageId);
+    if (!process) return;
+    
+    const hasImages = Object.values(process.content).some(space => 
+      space.images && space.images.length > 0
+    );
+    
+    if (!hasImages) {
+      alert('请先上传图片再确认');
+      return;
+    }
+    
+    // 推进到下一阶段
+    const currentIndex = this.stageOrder.indexOf(currentStage);
+    if (currentIndex < this.stageOrder.length - 1) {
+      const nextStage = this.stageOrder[currentIndex + 1];
+      
+      // 更新当前阶段
+      this.currentStage = nextStage;
+      if (this.project) {
+        this.project.currentStage = nextStage;
+      }
+      
+      // 展开下一阶段
+      this.expandedStages[nextStage] = true;
+      
+      // 滚动到下一阶段
+      setTimeout(() => {
+        this.scrollToStage(nextStage);
+      }, 100);
+      
+      console.log(`阶段推进: ${currentStage} -> ${nextStage}`);
+    } else {
+      // 如果是最后一个阶段,标记为完成
+      console.log(`交付执行阶段完成: ${currentStage}`);
+      alert('交付执行阶段已完成!');
+    }
+  }
+  
+  // 检查是否可以确认阶段上传
+  canConfirmStageUpload(stageId: string): boolean {
+    // 检查是否有上传的图片
+    const process = this.deliveryProcesses.find(p => p.id === stageId);
+    if (!process) return false;
+    
+    return Object.values(process.content).some(space => 
+      space.images && space.images.length > 0
+    );
+  }
+  
+  // 获取空间备注
+  getSpaceNotes(processId: string, spaceId: string): string {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return '';
+    
+    return process.content[spaceId].notes || '';
+  }
+  
+  // 更新空间备注
+  updateSpaceNotes(processId: string, spaceId: string, notes: string): void {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return;
+    
+    process.content[spaceId].notes = notes;
+    process.content[spaceId].lastUpdated = new Date();
+    
+    console.log(`已更新空间备注: ${processId}/${spaceId}`);
+  }
+  
+  // 更新空间进度
+  private updateSpaceProgress(processId: string, spaceId: string): void {
+    const process = this.deliveryProcesses.find(p => p.id === processId);
+    if (!process || !process.content[spaceId]) return;
+    
+    const content = process.content[spaceId];
+    const imageCount = content.images.length;
+    
+    // 根据图片数量和状态计算进度
+    if (imageCount === 0) {
+      content.progress = 0;
+      content.status = 'pending';
+    } else if (imageCount < 3) {
+      content.progress = Math.min(imageCount * 30, 90);
+      content.status = 'in_progress';
+    } else {
+      content.progress = 100;
+      content.status = 'completed';
+    }
+    
+    content.lastUpdated = new Date();
+  }
+
+  // ==================== 功能卡片点击事件 ====================
+  
+  /**
+   * 显示功能详情
+   * @param title 功能标题
+   * @param description 功能详细描述
+   */
+  showFeatureDetail(title: string, description: string): void {
+    console.log(`📋 功能详情: ${title}`);
+    console.log(`📝 ${description}`);
+    
+    alert(`✨ ${title}\n\n${description}\n\n点击确定关闭`);
+  }
+}

+ 866 - 0
copy/requirements-confirm-card1.html

@@ -0,0 +1,866 @@
+<div class="info-card requirements-confirm-card">
+  <div class="card-header">
+    <h4>确认需求</h4>
+    <div class="header-actions">
+      <button class="btn-ghost btn-sm" (click)="refreshProgress()">刷新进度</button>
+      
+      <!-- 紧凑型流程进度卡片 -->
+      <div class="compact-stage-indicators">
+        <div class="stage-chain">
+          <div class="stage-dot" [class]="getStageStatusClass('materialAnalysis')" 
+               title="素材分析 - {{ getStageStatusText('materialAnalysis') }}">
+            <span class="stage-number">1</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.materialAnalysis ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('requirementMapping')" 
+               title="需求映射 - {{ getStageStatusText('requirementMapping') }}">
+            <span class="stage-number">2</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.requirementMapping ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('collaboration')" 
+               title="协作验证 - {{ getStageStatusText('collaboration') }}">
+            <span class="stage-number">3</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.collaboration ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('progressReview')" 
+               title="进度审查 - {{ getStageStatusText('progressReview') }}">
+            <span class="stage-number">4</span>
+          </div>
+        </div>
+      </div>
+      
+      <div class="progress-indicator">
+        <div class="progress-bar">
+          <div class="progress-fill" [style.width.%]="getProgressPercentage()"></div>
+        </div>
+        <span class="progress-text">{{ getProgressPercentage() | number:'1.0-0' }}% 完成</span>
+      </div>
+    </div>
+  </div>
+
+  <!-- 标签页导航 -->
+  <div class="tab-navigation">
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'materials'"
+      (click)="switchTab('materials')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+        <polyline points="14,2 14,8 20,8"></polyline>
+      </svg>
+      素材解析
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'mapping'"
+      (click)="switchTab('mapping')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <circle cx="12" cy="12" r="3"></circle>
+        <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"></path>
+      </svg>
+      需求映射
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'collaboration'"
+      (click)="switchTab('collaboration')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+        <circle cx="9" cy="7" r="4"></circle>
+        <path d="M23 21v-2a4 4 0 0 0-3-3.87m-4-12a4 4 0 0 1 0 7.75"></path>
+      </svg>
+      协作验证
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'progress'"
+      (click)="switchTab('progress')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <polyline points="22,12 18,12 15,21 9,3 6,12 2,12"></polyline>
+      </svg>
+      进度管理
+    </button>
+  </div>
+
+  <!-- 标签页内容 -->
+  <div class="tab-content">
+    
+    <!-- 素材解析标签页 -->
+    @if (activeTab === 'materials') {
+      <div class="materials-section">
+        <!-- 文本输入区域 - 独立一行 -->
+        <div class="text-upload-section">
+          <div class="upload-item text-item">
+            <h5>文本描述</h5>
+            <form [formGroup]="materialUploadForm" (ngSubmit)="onTextSubmit()">
+              <textarea 
+                formControlName="textContent" 
+                placeholder="请描述您的装修需求,如:希望温馨的暖木色调,适合亲子家庭,需要瑜伽区收纳..."
+                rows="4">
+              </textarea>
+              <button type="submit" class="btn-primary btn-sm" [disabled]="!materialUploadForm.get('textContent')?.value">
+                解析文本
+              </button>
+            </form>
+          </div>
+        </div>
+
+        <!-- 参考图片和CAD图纸并排布局 -->
+        <div class="file-upload-grid">
+          <!-- 参考图片上传区域 -->
+          <div class="upload-item image-item">
+            <h5>参考图片</h5>
+            <div class="file-upload-zone" (click)="imageInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
+                <circle cx="8.5" cy="8.5" r="1.5"></circle>
+                <polyline points="21,15 16,10 5,21"></polyline>
+              </svg>
+              <p>点击上传参考图片</p>
+              <span class="hint">支持多张图片,自动分析色调和材质</span>
+            </div>
+            <input #imageInput type="file" multiple accept="image/*" (change)="onFileSelected($event, 'image')" style="display: none;">
+          </div>
+
+          <!-- CAD图纸上传区域 -->
+          <div class="upload-item cad-item">
+            <h5>CAD图纸</h5>
+            <div class="file-upload-zone" (click)="cadInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+                <polyline points="14,2 14,8 20,8"></polyline>
+                <line x1="16" y1="13" x2="8" y2="13"></line>
+                <line x1="16" y1="17" x2="8" y2="17"></line>
+              </svg>
+              <p>点击上传CAD图纸</p>
+              <span class="hint">自动识别结构和空间尺寸</span>
+            </div>
+            <input #cadInput type="file" accept=".dwg,.dxf,.pdf" (change)="onFileSelected($event, 'cad')" style="display: none;">
+          </div>
+        </div>
+
+        <!-- 已上传素材列表 -->
+        @if (materials.length > 0) {
+          <div class="materials-list">
+            <h5>已上传素材</h5>
+            <div class="material-cards">
+              @for (material of materials; track material.id) {
+                <div class="material-card" [class]="'material-' + material.type">
+                  <div class="material-header">
+                    <span class="material-type">{{ material.type === 'text' ? '文本' : material.type === 'image' ? '图片' : 'CAD' }}</span>
+                    <button class="btn-ghost btn-xs" (click)="removeMaterial(material.id)">
+                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                        <line x1="18" y1="6" x2="6" y2="18"></line>
+                        <line x1="6" y1="6" x2="18" y2="18"></line>
+                      </svg>
+                    </button>
+                  </div>
+                  <div class="material-name">{{ material.name }}</div>
+                  @if (material.type === 'image') {
+                    <button class="btn-ghost btn-xs" (click)="previewImage(material.url)">预览图片</button>
+                  } @else if (material.type === 'cad') {
+                    <button class="btn-ghost btn-xs" (click)="previewCad(material.url)">预览CAD</button>
+                  }
+                  @if (material.analysis) {
+                    <div class="parsed-info">
+                      @if (material.type === 'text') {
+                        <div class="parsed-tags">
+                          @if (material.analysis.atmosphere) {
+                          <div class="tag-group">
+                            <span class="tag-label">氛围:</span>
+                            <span class="tag">{{ material.analysis.atmosphere.description }}</span>
+                          </div>
+                        }
+                        </div>
+                      } @else if (material.type === 'image') {
+                        <div class="analysis-results">
+                          <!-- 基础色彩信息 -->
+                          <div class="color-info">
+                            <span class="color-temp">色温: {{ material.analysis.colorTemperature }}K</span>
+                          </div>
+                          
+                          <!-- 增强色彩分析 -->
+                          @if (material.analysis.enhancedColorAnalysis) {
+                            <div class="enhanced-analysis">
+                              <div class="analysis-section">
+                                <h6>色彩分析</h6>
+                                
+                                <!-- 色轮分析 -->
+                                @if (material.analysis.enhancedColorAnalysis.colorWheel) {
+                                  <div class="color-wheel-info">
+                                    <div class="color-wheel-icon">
+                                      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                                        <circle cx="12" cy="12" r="10"></circle>
+                                        <path d="M12 2a10 10 0 0 1 10 10"></path>
+                                        <path d="M12 2a10 10 0 0 0-10 10"></path>
+                                        <path d="M12 12l8.66-5"></path>
+                                        <path d="M12 12l-8.66-5"></path>
+                                      </svg>
+                                    </div>
+                                    <span>主色调: {{ material.analysis.enhancedColorAnalysis.colorWheel.primaryHue }}°</span>
+                                    <span>饱和度: {{ material.analysis.enhancedColorAnalysis.colorWheel.saturation }}%</span>
+                                  </div>
+                                }
+                                
+                                <!-- 色彩心理学 -->
+                                @if (material.analysis.enhancedColorAnalysis.colorPsychology) {
+                                  <div class="psychology-info">
+                                    <span class="mood-tag">{{ material.analysis.enhancedColorAnalysis.colorPsychology.primaryMood }}</span>
+                                    <span class="atmosphere-tag">{{ material.analysis.enhancedColorAnalysis.colorPsychology.atmosphere }}</span>
+                                  </div>
+                                }
+                              </div>
+                              
+                              <!-- 形体分析 -->
+                              @if (material.analysis.formAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>形体分析</h6>
+                                  @if (material.analysis.formAnalysis.overallAssessment) {
+                                    <div class="form-metrics">
+                                      <div class="metric-item">
+                                        <span class="metric-label">复杂度:</span>
+                                        <div class="metric-bar">
+                                          <div class="metric-fill" [style.width.%]="material.analysis.formAnalysis.overallAssessment.formComplexity"></div>
+                                        </div>
+                                      </div>
+                                      <div class="metric-item">
+                                        <span class="metric-label">视觉冲击:</span>
+                                        <div class="metric-bar">
+                                          <div class="metric-fill" [style.width.%]="material.analysis.formAnalysis.overallAssessment.visualImpact"></div>
+                                        </div>
+                                      </div>
+                                    </div>
+                                  }
+                                </div>
+                              }
+                              
+                              <!-- 质感分析 -->
+                              @if (material.analysis.textureAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>质感分析</h6>
+                                  @if (material.analysis.textureAnalysis.materialClassification) {
+                                    <div class="material-info">
+                                      <span class="material-tag">{{ material.analysis.textureAnalysis.materialClassification.primaryMaterial.category }}</span>
+                                      @if (material.analysis.textureAnalysis.surfaceProperties) {
+                                        <span class="surface-tag">{{ material.analysis.textureAnalysis.surfaceProperties.roughness.level }}</span>
+                                      }
+                                    </div>
+                                  }
+                                </div>
+                              }
+                              
+                              <!-- 纹理分析 -->
+                              @if (material.analysis.patternAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>纹理分析</h6>
+                                  @if (material.analysis.patternAnalysis.patternRecognition) {
+                                    <div class="pattern-info">
+                                      @for (pattern of material.analysis.patternAnalysis.patternRecognition.primaryPatterns; track pattern.type) {
+                                        <span class="pattern-tag">{{ pattern.type }} ({{ pattern.coverage }}%)</span>
+                                      }
+                                    </div>
+                                  }
+                                </div>
+                              }
+                              
+                              <!-- 灯光分析 -->
+                              @if (material.analysis.lightingAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>灯光分析</h6>
+                                  @if (material.analysis.lightingAnalysis.ambientAnalysis) {
+                                    <div class="lighting-info">
+                                      <span class="mood-tag">{{ material.analysis.lightingAnalysis.ambientAnalysis.lightingMood?.primary || '未知' }}</span>
+                                      @if (material.analysis.lightingAnalysis.illuminationAnalysis) {
+                                        <span class="brightness-tag">亮度: {{ material.analysis.lightingAnalysis.illuminationAnalysis.brightness?.overall || 0 }}%</span>
+                                      }
+                                      @if (material.analysis.lightingAnalysis.lightSourceIdentification) {
+                                        <span class="source-tag">光源: {{ material.analysis.lightingAnalysis.lightSourceIdentification.lightingSetup?.dominantSource || '未知' }}</span>
+                                      }
+                                    </div>
+                                  }
+                                </div>
+                              }
+                            </div>
+                          }
+                        </div>
+                      }
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    }
+
+    <!-- 需求映射标签页 -->
+    @if (activeTab === 'mapping') {
+      <div class="mapping-section">
+        <!-- 测试步骤进度 -->
+        <div class="test-progress">
+          <h3>需求映射进度</h3>
+          <div class="steps-container">
+            @for (step of testSteps; track step.id) {
+              <div class="step-item" [class]="getStepClass(step.status)">
+                <div class="step-icon">{{ getStepIcon(step.status) }}</div>
+                <div class="step-info">
+                  <div class="step-name">{{ step.name }}</div>
+                  <div class="step-status">
+                    @switch (step.status) {
+                      @case ('pending') { 等待中 }
+                      @case ('in-progress') { 进行中... }
+                      @case ('completed') { 已完成 }
+                      @case ('error') { 失败 }
+                    }
+                  </div>
+                </div>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 文件上传区域 -->
+        <div class="upload-section">
+          <h3>1. 图片上传</h3>
+          <div class="upload-area" [class.uploading]="isUploading">
+            @if (uploadedFiles.length === 0) {
+              <div class="upload-dropzone">
+                <div class="upload-icon">📁</div>
+                <div class="upload-text">选择图片文件进行测试</div>
+                <div class="upload-hint">支持 JPG、PNG 格式,可选择多张图片</div>
+                <input type="file" 
+                       accept="image/*" 
+                       multiple 
+                       (change)="onFileSelectedForMapping($event)"
+                       class="file-input">
+              </div>
+            } @else {
+              <div class="uploaded-files">
+                <h4>已上传文件 ({{ uploadedFiles.length }})</h4>
+                <div class="files-grid">
+                  @for (file of uploadedFiles; track file.id) {
+                    <div class="file-item">
+                      <img [src]="file.preview || file.url" [alt]="file.name" class="file-preview">
+                      <div class="file-info">
+                        <div class="file-name">{{ file.name }}</div>
+                        <div class="file-size">{{ (file.size! / 1024 / 1024).toFixed(2) }} MB</div>
+                      </div>
+                    </div>
+                  }
+                </div>
+              </div>
+            }
+            
+            @if (isUploading) {
+              <div class="loading-overlay">
+                <div class="loading-spinner"></div>
+                <div class="loading-text">正在上传文件...</div>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 分析结果区域 -->
+        <div class="analysis-section" [class.disabled]="uploadedFiles.length === 0">
+          <h3>2. 图片分析</h3>
+          
+          @if (isAnalyzing) {
+            <div class="analysis-loading">
+              <div class="loading-spinner"></div>
+              <div class="loading-text">
+                <h4>正在分析图片...</h4>
+                <p>系统正在进行色彩、纹理、形态、图案和灯光分析</p>
+              </div>
+            </div>
+          } @else if (analysisError) {
+            <div class="analysis-error">
+              <div class="error-icon">❌</div>
+              <div class="error-text">
+                <h4>分析失败</h4>
+                <p>{{ analysisError }}</p>
+                <button class="retry-btn" (click)="retryAnalysis()">重新分析</button>
+              </div>
+            </div>
+          } @else if (analysisResult) {
+            <div class="analysis-result">
+              <h4>分析完成 ✅</h4>
+              <div class="analysis-summary">
+                <div class="summary-item">
+                  <span class="label">主要颜色:</span>
+                  <span class="value">{{ analysisResult.enhancedAnalysis?.colorWheel?.colorDistribution?.length || 0 }} 种</span>
+                </div>
+                <div class="summary-item">
+                  <span class="label">材质类型:</span>
+                  <span class="value">{{ analysisResult.textureAnalysis?.materialClassification?.primary || '未识别' }}</span>
+                </div>
+                <div class="summary-item">
+                  <span class="label">灯光情绪:</span>
+                  <span class="value">{{ analysisResult.lightingAnalysis?.ambientAnalysis?.lightingMood || '未识别' }}</span>
+                </div>
+                <div class="summary-item">
+                  <span class="label">空间形态:</span>
+                  <span class="value">{{ analysisResult.formAnalysis?.spaceAnalysis?.spaceType || '未识别' }}</span>
+                </div>
+              </div>
+            </div>
+          } @else {
+            <div class="analysis-placeholder">
+              <div class="placeholder-icon">🔍</div>
+              <div class="placeholder-text">
+                <h4>等待分析</h4>
+                <p>请先上传图片,系统将自动开始分析</p>
+              </div>
+            </div>
+          }
+        </div>
+
+        <!-- 需求映射结果区域 -->
+        <div class="mapping-section" [class.disabled]="!analysisResult">
+          <h3>3. 需求映射生成</h3>
+          
+          @if (isGeneratingMapping) {
+            <div class="mapping-loading">
+              <div class="loading-spinner"></div>
+              <div class="loading-text">
+                <h4>正在生成需求映射...</h4>
+                <p>基于分析结果生成场景参数和映射关系</p>
+              </div>
+            </div>
+          } @else if (mappingError) {
+            <div class="mapping-error">
+              <div class="error-icon">❌</div>
+              <div class="error-text">
+                <h4>映射生成失败</h4>
+                <p>{{ mappingError }}</p>
+                <button class="retry-btn" (click)="retryMapping()">重新生成</button>
+              </div>
+            </div>
+          } @else if (requirementMapping) {
+            <div class="mapping-result">
+              <h4>需求映射生成完成 ✅</h4>
+              
+              <!-- 场景生成信息 -->
+              <div class="mapping-section-item">
+                <h5>场景生成</h5>
+                <div class="scene-info">
+                  <div class="info-row">
+                    <span class="label">基础场景:</span>
+                    <span class="value">{{ requirementMapping.sceneGeneration.baseScene }}</span>
+                  </div>
+                  @if (requirementMapping.sceneGeneration.atmospherePreview) {
+                    <div class="atmosphere-preview">
+                      <img [src]="requirementMapping.sceneGeneration.atmospherePreview" 
+                           alt="氛围感预览图"
+                           class="preview-image">
+                      <div class="preview-label">氛围感预览图</div>
+                    </div>
+                  }
+                </div>
+              </div>
+
+              <!-- 参数映射信息 -->
+              <div class="mapping-section-item">
+                <h5>参数映射</h5>
+                <div class="params-grid">
+                  <!-- 颜色参数 -->
+                  <div class="param-group">
+                    <h6>颜色映射</h6>
+                    <div class="color-params">
+                      <div class="param-item">
+                        <span class="label">主要颜色:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.primaryColors.length }} 种</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">色彩和谐:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.colorHarmony }}</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">饱和度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.saturation }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">亮度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.brightness }}%</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 空间参数 -->
+                  <div class="param-group">
+                    <h6>空间映射</h6>
+                    <div class="space-params">
+                      <div class="param-item">
+                        <span class="label">布局类型:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout.type }}</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">空间流线:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout.flow }}</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">家具比例:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.furniture }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">开放度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.openness }}%</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 材质参数 -->
+                  <div class="param-group">
+                    <h6>材质映射</h6>
+                    <div class="material-params">
+                      <div class="param-item">
+                        <span class="label">纹理缩放:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.textureScale }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">反射率:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.reflectivity }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">粗糙度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.roughness }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">金属度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.metallic }}%</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 测试结果下载 -->
+              <div class="test-actions">
+                <button class="download-btn" (click)="downloadTestResult()">
+                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
+                    <polyline points="7,10 12,15 17,10"></polyline>
+                    <line x1="12" y1="15" x2="12" y2="3"></line>
+                  </svg>
+                  下载测试结果
+                </button>
+              </div>
+            </div>
+          } @else {
+            <div class="mapping-placeholder">
+              <div class="placeholder-icon">🎯</div>
+              <div class="placeholder-text">
+                <h4>等待映射生成</h4>
+                <p>请先完成图片分析,系统将自动生成需求映射</p>
+              </div>
+            </div>
+          }
+        </div>
+      </div>
+    }
+
+    <!-- 协作验证标签页 -->
+    @if (activeTab === 'collaboration') {
+      <div class="collaboration-section">
+        <!-- 一致性预警 -->
+        @if (consistencyWarnings.length > 0) {
+          <div class="consistency-warning">
+            <svg viewBox="0 0 24 24" fill="currentColor">
+              <path d="M12 2L1 21h22L12 2zm0 3.99L19.53 19H4.47L12 5.99zM11 16h2v2h-2v-2zm0-6h2v4h-2v-4z"/>
+            </svg>
+            <div>
+              <strong>一致性预警:</strong>
+              @for (warning of consistencyWarnings; track warning) {
+                <div>{{ warning }}</div>
+              }
+            </div>
+          </div>
+        }
+
+        <!-- 需求列表 -->
+        <div class="requirements-list">
+          <div class="list-header">
+            <h5>需求项目</h5>
+            <div class="list-controls">
+              <button class="btn-ghost btn-sm" (click)="sortRequirementsByPriority()">按优先级排序</button>
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存状态</button>
+            </div>
+          </div>
+
+          @for (requirement of requirementItems; track requirement.id) {
+            <div class="requirement-item" [class]="getStatusClass(requirement.status)">
+              <div class="requirement-header">
+                <div class="requirement-info">
+                  <h6>{{ requirement.title }}</h6>
+                  <p>{{ requirement.description }}</p>
+                  <div class="requirement-tags">
+                    @for (tag of requirement.tags; track tag) {
+                      <span class="tag">{{ tag }}</span>
+                    }
+                  </div>
+                </div>
+                <div class="requirement-meta">
+                  <select class="priority-select" 
+                          [value]="requirement.priority" 
+                          (change)="onPriorityChange(requirement.id, $event)">
+                    <option value="high">高优先级</option>
+                    <option value="medium">中优先级</option>
+                    <option value="low">低优先级</option>
+                  </select>
+                  <span class="status-badge" [class]="getStatusClass(requirement.status)">
+                    {{ requirement.status === 'confirmed' ? '已确认' : requirement.status === 'rejected' ? '已拒绝' : '待确认' }}
+                  </span>
+                </div>
+              </div>
+              
+              <div class="requirement-actions">
+                @if (requirement.status === 'pending') {
+                  <button class="btn-success btn-sm" (click)="confirmRequirement(requirement.id)">确认</button>
+                  <button class="btn-danger btn-sm" (click)="rejectRequirement(requirement.id, '需要进一步讨论')">拒绝</button>
+                }
+                <button class="btn-ghost btn-sm" (click)="requirement.showComments = !requirement.showComments">
+                  评论 ({{ getCommentsForRequirement(requirement.id).length }})
+                  @if (hasUnreadComments(requirement.id)) {
+                    <span class="unread-indicator"></span>
+                  }
+                </button>
+              </div>
+
+              @if (requirement.showComments) {
+                <div class="comments-section">
+                  @if (getCommentsForRequirement(requirement.id).length > 0) {
+                    <div class="comments-list">
+                      @for (comment of getCommentsForRequirement(requirement.id); track comment.id) {
+                        <div class="comment-item" [class.resolved]="comment.status === 'resolved'">
+                          <div class="comment-header">
+                            <span class="comment-author">{{ comment.author }}</span>
+                            <span class="comment-role">{{ comment.role === 'designer' ? '设计师' : comment.role === 'customer-service' ? '客服' : '客户' }}</span>
+                            <span class="comment-time">{{ comment.timestamp | date:'MM-dd HH:mm' }}</span>
+                            @if (comment.status === 'pending') {
+                              <button class="btn-ghost btn-xs" (click)="resolveComment(comment.id)">标记已解决</button>
+                            }
+                          </div>
+                          <div class="comment-content">{{ comment.content }}</div>
+                        </div>
+                      }
+                    </div>
+                  }
+                  <div class="add-comment">
+                    <textarea #commentInput placeholder="添加评论..." rows="2"></textarea>
+                    <button class="btn-primary btn-sm" (click)="addCommentToRequirement(requirement.id, commentInput.value); commentInput.value = ''">
+                      发送
+                    </button>
+                  </div>
+                </div>
+              }
+            </div>
+          }
+        </div>
+      </div>
+    }
+
+    <!-- 进度管理标签页 -->
+    @if (activeTab === 'progress') {
+      <div class="progress-section">
+        <!-- 整体进度 -->
+        <div class="overall-progress">
+          <div class="section-header">
+            <h5>整体进度</h5>
+          </div>
+          
+          <div class="progress-container">
+            <div class="progress-visual">
+              <div class="linear-progress">
+                <div class="progress-track">
+                  <div class="progress-bar-fill" [style.width.%]="getProgressPercentage()"></div>
+                </div>
+                <div class="progress-text">{{ getProgressPercentage() }}%</div>
+              </div>
+              
+              <div class="circular-progress">
+                <svg viewBox="0 0 120 120" class="progress-circle">
+                  <circle cx="60" cy="60" r="54" fill="none" stroke="#e5e7eb" stroke-width="8"></circle>
+                  <circle cx="60" cy="60" r="54" fill="none" stroke="#3b82f6" stroke-width="8"
+                    stroke-linecap="round"
+                    [attr.stroke-dasharray]="339.292"
+                    [attr.stroke-dashoffset]="339.292 - (339.292 * getProgressPercentage() / 100)"
+                    transform="rotate(-90 60 60)">
+                  </circle>
+                </svg>
+                <div class="progress-text">
+                  <div class="progress-percentage">{{ getProgressPercentage() }}%</div>
+                  <div class="progress-label">完成度</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 进度统计 -->
+        <div class="progress-stats">
+          <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('confirmed') }}</div>
+              <div class="stat-label">已确认</div>
+            </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('pending') }}</div>
+              <div class="stat-label">待确认</div>
+            </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('rejected') }}</div>
+              <div class="stat-label">已拒绝</div>
+            </div>
+          <div class="stat-item">
+            <div class="stat-number">{{ getUnresolvedCommentsCount() }}</div>
+            <div class="stat-label">待解决评论</div>
+          </div>
+        </div>
+
+        <!-- 历史状态 -->
+        <div class="history-section">
+          <div class="section-header">
+            <h5>历史状态</h5>
+            <div class="history-controls">
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+              <select class="history-select" (change)="onHistoryStateChange($event)">
+                <option value="-1">选择历史状态</option>
+                @for (state of historyStates; track $index) {
+                  <option [value]="$index">{{ state.timestamp | date:'MM-dd HH:mm' }} - {{ state.author }}</option>
+                }
+              </select>
+            </div>
+          </div>
+
+          @if (historyStates.length > 0) {
+            <div class="history-timeline">
+              @for (state of historyStates; track $index) {
+                <div class="timeline-item" (click)="restoreHistoryState($index)">
+                  <div class="timeline-marker"></div>
+                  <div class="timeline-content">
+                    <div class="timeline-header">
+                      <span class="timeline-time">{{ state.timestamp | date:'MM-dd HH:mm' }}</span>
+                      <span class="timeline-author">{{ state.author }}</span>
+                    </div>
+                    <div class="timeline-summary">
+                      状态快照:{{ state.requirementItems.length }}个需求项,
+                      {{ getRequirementCountByStatus('confirmed') }}个已确认
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-history">
+              <p>暂无历史状态记录</p>
+              <button class="btn-primary btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+            </div>
+          }
+        </div>
+
+        <!-- 需求状态分布 -->
+        <div class="status-distribution">
+          <h5>需求状态分布</h5>
+          <div class="status-bars">
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('confirmed') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar confirmed" 
+                     [style.width.%]="getStatusPercentage('confirmed')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">待确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('pending') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar pending" 
+                     [style.width.%]="getStatusPercentage('pending')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已拒绝</span>
+                <span class="status-count">{{ getRequirementCountByStatus('rejected') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar rejected" 
+                     [style.width.%]="getStatusPercentage('rejected')"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+  </div>
+</div>
+
+<!-- 保存状态和手动保存按钮 -->
+<div class="save-section">
+  <div class="save-status">
+    <span class="save-icon" [class]="'save-icon-' + saveStatus">{{ getSaveStatusIcon() }}</span>
+    <span class="save-text">{{ getSaveStatusText() }}</span>
+  </div>
+  
+  <div class="save-actions">
+    <button class="btn-secondary" 
+            [disabled]="isSaving || !hasUnsavedChanges"
+            (click)="manualSave()">
+      @if (isSaving) {
+        <span class="loading-spinner"></span>
+        保存中...
+      } @else {
+        手动保存
+      }
+    </button>
+    
+    <div class="auto-save-toggle">
+      <label class="toggle-label">
+        <input type="checkbox" 
+               [(ngModel)]="autoSaveEnabled"
+               class="toggle-input">
+        <span class="toggle-slider"></span>
+        <span class="toggle-text">自动保存</span>
+      </label>
+    </div>
+  </div>
+</div>
+
+<!-- 上传成功弹窗 -->
+<app-upload-success-modal
+  [isVisible]="showUploadSuccessModal"
+  [uploadedFiles]="uploadedFiles"
+  [uploadType]="uploadType"
+  [analysisResult]="colorAnalysisResult"
+  [isAnalyzing]="isAnalyzingColors"
+  (closeModal)="onModalClose()"
+  (analyzeColors)="onAnalyzeColors()"
+  (viewReport)="onViewReport()">
+</app-upload-success-modal>
+
+<!-- 全局提示(支持全屏与角落两模式) -->
+<app-global-prompt
+  [visible]="showGlobalPrompt"
+  [title]="promptTitle"
+  [message]="promptMessage"
+  [mode]="promptMode"
+  [position]="promptPosition"
+  [autoDismissMs]="4000"
+  (closed)="onPromptClose()">
+</app-global-prompt>
+
+<!-- 完整报告全屏覆盖层 -->
+<app-full-report-overlay
+  [visible]="showFullReportOverlay"
+  [colorResult]="colorAnalysisResult"
+  [cadResult]="cadAnalysisResult"
+  (close)="onCloseFullReport()">
+</app-full-report-overlay>

+ 888 - 0
copy/requirements-confirm-card2.html

@@ -0,0 +1,888 @@
+<div class="info-card requirements-confirm-card">
+  <div class="card-header">
+    <h4>确认需求</h4>
+    <div class="header-actions">
+      <button class="btn-ghost btn-sm" (click)="refreshProgress()">刷新进度</button>
+      
+      <!-- 紧凑型流程进度卡片 -->
+      <div class="compact-stage-indicators">
+        <div class="stage-chain">
+          <div class="stage-dot" [class]="getStageStatusClass('materialAnalysis')" 
+               title="素材分析 - {{ getStageStatusText('materialAnalysis') }}">
+            <span class="stage-number">1</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.materialAnalysis ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('requirementMapping')" 
+               title="需求映射 - {{ getStageStatusText('requirementMapping') }}">
+            <span class="stage-number">2</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.requirementMapping ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('collaboration')" 
+               title="协作验证 - {{ getStageStatusText('collaboration') }}">
+            <span class="stage-number">3</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.collaboration ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('progressReview')" 
+               title="进度审查 - {{ getStageStatusText('progressReview') }}">
+            <span class="stage-number">4</span>
+          </div>
+        </div>
+      </div>
+      
+      <div class="progress-indicator">
+        <div class="progress-bar">
+          <div class="progress-fill" [style.width.%]="getProgressPercentage()"></div>
+        </div>
+        <span class="progress-text">{{ getProgressPercentage() | number:'1.0-0' }}% 完成</span>
+      </div>
+    </div>
+  </div>
+
+  <!-- 标签页导航 -->
+  <div class="tab-navigation">
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'materials'"
+      (click)="switchTab('materials')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+        <polyline points="14,2 14,8 20,8"></polyline>
+      </svg>
+      素材解析
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'mapping'"
+      (click)="switchTab('mapping')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <circle cx="12" cy="12" r="3"></circle>
+        <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"></path>
+      </svg>
+      需求映射
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'collaboration'"
+      (click)="switchTab('collaboration')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+        <circle cx="9" cy="7" r="4"></circle>
+        <path d="M23 21v-2a4 4 0 0 0-3-3.87m-4-12a4 4 0 0 1 0 7.75"></path>
+      </svg>
+      协作验证
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'progress'"
+      (click)="switchTab('progress')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <polyline points="22,12 18,12 15,21 9,3 6,12 2,12"></polyline>
+      </svg>
+      进度管理
+    </button>
+  </div>
+
+  <!-- 标签页内容 -->
+  <div class="tab-content">
+    
+    <!-- 素材解析标签页 -->
+    @if (activeTab === 'materials') {
+      <div class="materials-section">
+        <!-- 文本输入区域 - 独立一行 -->
+        <div class="text-upload-section">
+          <div class="upload-item text-item">
+            <h5>文本描述</h5>
+            <form [formGroup]="materialUploadForm" (ngSubmit)="onTextSubmit()">
+              <textarea 
+                formControlName="textContent" 
+                placeholder="请描述您的装修需求,如:希望温馨的暖木色调,适合亲子家庭,需要瑜伽区收纳..."
+                rows="4">
+              </textarea>
+              <button type="submit" class="btn-primary btn-sm" [disabled]="!materialUploadForm.get('textContent')?.value">
+                解析文本
+              </button>
+            </form>
+          </div>
+        </div>
+
+        <!-- 参考图片和CAD图纸并排布局 -->
+        <div class="file-upload-grid">
+          <!-- 参考图片上传区域 -->
+          <div class="upload-item image-item">
+            <h5>参考图片</h5>
+            <div class="file-upload-zone" (click)="imageInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
+                <circle cx="8.5" cy="8.5" r="1.5"></circle>
+                <polyline points="21,15 16,10 5,21"></polyline>
+              </svg>
+              <p>点击上传参考图片</p>
+              <span class="hint">支持多张图片,自动分析色调和材质</span>
+            </div>
+            <input #imageInput type="file" multiple accept="image/*" (change)="onFileSelected($event, 'image')" style="display: none;">
+          </div>
+
+          <!-- CAD图纸上传区域 -->
+          <div class="upload-item cad-item">
+            <h5>CAD图纸</h5>
+            <div class="file-upload-zone" (click)="cadInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+                <polyline points="14,2 14,8 20,8"></polyline>
+                <line x1="16" y1="13" x2="8" y2="13"></line>
+                <line x1="16" y1="17" x2="8" y2="17"></line>
+              </svg>
+              <p>点击上传CAD图纸</p>
+              <span class="hint">自动识别结构和空间尺寸</span>
+            </div>
+            <input #cadInput type="file" accept=".dwg,.dxf,.pdf" (change)="onFileSelected($event, 'cad')" style="display: none;">
+          </div>
+        </div>
+
+        <!-- 已上传素材列表 -->
+        @if (materials.length > 0) {
+          <div class="materials-list">
+            <h5>已上传素材</h5>
+            <div class="material-cards">
+              @for (material of materials; track material.id) {
+                <div class="material-card" [class]="'material-' + material.type">
+                  <div class="material-header">
+                    <span class="material-type">{{ material.type === 'text' ? '文本' : material.type === 'image' ? '图片' : 'CAD' }}</span>
+                    <button class="btn-ghost btn-xs" (click)="removeMaterial(material.id)">
+                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                        <line x1="18" y1="6" x2="6" y2="18"></line>
+                        <line x1="6" y1="6" x2="18" y2="18"></line>
+                      </svg>
+                    </button>
+                  </div>
+                  <div class="material-name">{{ material.name }}</div>
+                  @if (material.type === 'image') {
+                    <button class="btn-ghost btn-xs" (click)="previewImage(material.url)">预览图片</button>
+                  } @else if (material.type === 'cad') {
+                    <button class="btn-ghost btn-xs" (click)="previewCad(material.url)">预览CAD</button>
+                  }
+                  @if (material.analysis) {
+                    <div class="parsed-info">
+                      @if (material.type === 'text') {
+                        <div class="parsed-tags">
+                          @if (material.analysis.atmosphere) {
+                          <div class="tag-group">
+                            <span class="tag-label">氛围:</span>
+                            <span class="tag">{{ material.analysis.atmosphere.description }}</span>
+                          </div>
+                        }
+                        </div>
+                      } @else if (material.type === 'image') {
+                        <div class="analysis-results">
+                          <!-- 基础色彩信息 -->
+                          <div class="color-info">
+                            <span class="color-temp">色温: {{ material.analysis.colorTemperature }}K</span>
+                          </div>
+                          
+                          <!-- 增强色彩分析 -->
+                          @if (material.analysis.enhancedColorAnalysis) {
+                            <div class="enhanced-analysis">
+                              <div class="analysis-section">
+                                <h6>色彩分析</h6>
+                                
+                                <!-- 色轮分析 -->
+                                @if (material.analysis.enhancedColorAnalysis.colorWheel) {
+                                  <div class="color-wheel-info">
+                                    <div class="color-wheel-icon">
+                                      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                                        <circle cx="12" cy="12" r="10"></circle>
+                                        <path d="M12 2a10 10 0 0 1 10 10"></path>
+                                        <path d="M12 2a10 10 0 0 0-10 10"></path>
+                                        <path d="M12 12l8.66-5"></path>
+                                        <path d="M12 12l-8.66-5"></path>
+                                      </svg>
+                                    </div>
+                                    <span>主色调: {{ material.analysis.enhancedColorAnalysis.colorWheel.primaryHue }}°</span>
+                                    <span>饱和度: {{ material.analysis.enhancedColorAnalysis.colorWheel.saturation }}%</span>
+                                  </div>
+                                }
+                                
+                                <!-- 色彩心理学 -->
+                                @if (material.analysis.enhancedColorAnalysis.colorPsychology) {
+                                  <div class="psychology-info">
+                                    <span class="mood-tag">{{ material.analysis.enhancedColorAnalysis.colorPsychology.primaryMood }}</span>
+                                    <span class="atmosphere-tag">{{ material.analysis.enhancedColorAnalysis.colorPsychology.atmosphere }}</span>
+                                  </div>
+                                }
+                              </div>
+                              
+                              <!-- 形体分析 -->
+                              @if (material.analysis.formAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>形体分析</h6>
+                                  @if (material.analysis.formAnalysis.overallAssessment) {
+                                    <div class="form-metrics">
+                                      <div class="metric-item">
+                                        <span class="metric-label">复杂度:</span>
+                                        <div class="metric-bar">
+                                          <div class="metric-fill" [style.width.%]="material.analysis.formAnalysis.overallAssessment.formComplexity"></div>
+                                        </div>
+                                      </div>
+                                      <div class="metric-item">
+                                        <span class="metric-label">视觉冲击:</span>
+                                        <div class="metric-bar">
+                                          <div class="metric-fill" [style.width.%]="material.analysis.formAnalysis.overallAssessment.visualImpact"></div>
+                                        </div>
+                                      </div>
+                                    </div>
+                                  }
+                                </div>
+                              }
+                              
+                              <!-- 质感分析 -->
+                              @if (material.analysis.textureAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>质感分析</h6>
+                                  @if (material.analysis.textureAnalysis.materialClassification) {
+                                    <div class="material-info">
+                                      <span class="material-tag">{{ material.analysis.textureAnalysis.materialClassification.primaryMaterial.category }}</span>
+                                      @if (material.analysis.textureAnalysis.surfaceProperties) {
+                                        <span class="surface-tag">{{ material.analysis.textureAnalysis.surfaceProperties.roughness.level }}</span>
+                                      }
+                                    </div>
+                                  }
+                                </div>
+                              }
+                              
+                              <!-- 纹理分析 -->
+                              @if (material.analysis.patternAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>纹理分析</h6>
+                                  @if (material.analysis.patternAnalysis.patternRecognition) {
+                                    <div class="pattern-info">
+                                      @for (pattern of material.analysis.patternAnalysis.patternRecognition.primaryPatterns; track pattern.type) {
+                                        <span class="pattern-tag">{{ pattern.type }} ({{ pattern.coverage }}%)</span>
+                                      }
+                                    </div>
+                                  }
+                                </div>
+                              }
+                              
+                              <!-- 灯光分析 -->
+                              @if (material.analysis.lightingAnalysis) {
+                                <div class="analysis-section">
+                                  <h6>灯光分析</h6>
+                                  @if (material.analysis.lightingAnalysis.ambientAnalysis) {
+                                    <div class="lighting-info">
+                                      <span class="mood-tag">{{ material.analysis.lightingAnalysis.ambientAnalysis.lightingMood?.primary || '未知' }}</span>
+                                      @if (material.analysis.lightingAnalysis.illuminationAnalysis) {
+                                        <span class="brightness-tag">亮度: {{ material.analysis.lightingAnalysis.illuminationAnalysis.brightness?.overall || 0 }}%</span>
+                                      }
+                                      @if (material.analysis.lightingAnalysis.lightSourceIdentification) {
+                                        <span class="source-tag">光源: {{ material.analysis.lightingAnalysis.lightSourceIdentification.lightingSetup?.dominantSource || '未知' }}</span>
+                                      }
+                                    </div>
+                                  }
+                                </div>
+                              }
+                            </div>
+                          }
+                        </div>
+                      }
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    }
+
+    <!-- 需求映射标签页 -->
+    @if (activeTab === 'mapping') {
+      <div class="mapping-section">
+        <!-- 测试步骤进度 -->
+        <div class="test-progress">
+          <h3>需求映射进度</h3>
+          <div class="progress-summary">
+            @if (uploadedFiles.length > 0) {
+              <span class="progress-badge">
+                📸 已同步 {{ uploadedFiles.length }} 张参考图片
+              </span>
+            }
+          </div>
+          <div class="steps-container">
+            @for (step of testSteps; track step.id) {
+              <div class="step-item" [class]="getStepClass(step.status)">
+                <div class="step-icon">{{ getStepIcon(step.status) }}</div>
+                <div class="step-info">
+                  <div class="step-name">{{ step.name }}</div>
+                  <div class="step-status">
+                    @switch (step.status) {
+                      @case ('pending') { 等待中 }
+                      @case ('in-progress') { 进行中... }
+                      @case ('completed') { 已完成 }
+                      @case ('error') { 失败 }
+                    }
+                  </div>
+                </div>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 文件上传区域 -->
+        <div class="upload-section">
+          <h3>1. 图片上传</h3>
+          <div class="upload-area" [class.uploading]="isUploading">
+            @if (uploadedFiles.length === 0) {
+              <div class="upload-dropzone">
+                <div class="upload-icon">📁</div>
+                <div class="upload-text">选择图片文件进行测试</div>
+                <div class="upload-hint">支持 JPG、PNG 格式,可选择多张图片</div>
+                <input type="file" 
+                       accept="image/*" 
+                       multiple 
+                       (change)="onFileSelectedForMapping($event)"
+                       class="file-input">
+              </div>
+            } @else {
+              <div class="uploaded-files">
+                <h4>已上传文件 ({{ uploadedFiles.length }} 张图片)</h4>
+                <div class="upload-source-info">
+                  <span class="info-tag">✨ 来自素材分析的参考图片</span>
+                </div>
+                <div class="files-grid">
+                  @for (file of uploadedFiles; track file.id) {
+                    <div class="file-item">
+                      <img [src]="file.preview || file.url" [alt]="file.name" class="file-preview">
+                      <div class="file-info">
+                        <div class="file-name">{{ file.name }}</div>
+                        <div class="file-size">{{ (file.size! / 1024 / 1024).toFixed(2) }} MB</div>
+                      </div>
+                      <button class="remove-file-btn" (click)="removeUploadedFile(file.id)" title="移除">
+                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                          <line x1="18" y1="6" x2="6" y2="18"></line>
+                          <line x1="6" y1="6" x2="18" y2="18"></line>
+                        </svg>
+                      </button>
+                    </div>
+                  }
+                </div>
+              </div>
+            }
+            
+            @if (isUploading) {
+              <div class="loading-overlay">
+                <div class="loading-spinner"></div>
+                <div class="loading-text">正在上传文件...</div>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 分析结果区域 -->
+        <div class="analysis-section" [class.disabled]="uploadedFiles.length === 0">
+          <h3>2. 图片分析</h3>
+          
+          @if (isAnalyzing) {
+            <div class="analysis-loading">
+              <div class="loading-spinner"></div>
+              <div class="loading-text">
+                <h4>正在分析图片...</h4>
+                <p>
+                  @if (uploadedFiles.length > 1) {
+                    正在分析 {{ uploadedFiles.length }} 张图片,系统将合并分析结果
+                  } @else {
+                    系统正在进行色彩、纹理、形态、图案和灯光分析
+                  }
+                </p>
+              </div>
+            </div>
+          } @else if (analysisError) {
+            <div class="analysis-error">
+              <div class="error-icon">❌</div>
+              <div class="error-text">
+                <h4>分析失败</h4>
+                <p>{{ analysisError }}</p>
+                <button class="retry-btn" (click)="retryAnalysis()">重新分析</button>
+              </div>
+            </div>
+          } @else if (analysisResult) {
+            <div class="analysis-result">
+              <h4>分析完成 ✅</h4>
+              <div class="analysis-summary">
+                <div class="summary-item">
+                  <span class="label">主要颜色:</span>
+                  <span class="value">{{ analysisResult.enhancedAnalysis?.colorWheel?.colorDistribution?.length || 0 }} 种</span>
+                </div>
+                <div class="summary-item">
+                  <span class="label">材质类型:</span>
+                  <span class="value">{{ getMaterialName(analysisResult.textureAnalysis?.materialClassification?.primaryMaterial?.category) || '未识别' }}</span>
+                </div>
+                <div class="summary-item">
+                  <span class="label">灯光情绪:</span>
+                  <span class="value">{{ getLightingMoodName(analysisResult.lightingAnalysis?.ambientAnalysis?.lightingMood?.primary) || '未识别' }}</span>
+                </div>
+                <div class="summary-item">
+                  <span class="label">空间形态:</span>
+                  <span class="value">{{ analysisResult.formAnalysis?.geometryAnalysis?.complexity ? getComplexityName(analysisResult.formAnalysis.geometryAnalysis.complexity) : '未识别' }}</span>
+                </div>
+              </div>
+            </div>
+          } @else {
+            <div class="analysis-placeholder">
+              <div class="placeholder-icon">🔍</div>
+              <div class="placeholder-text">
+                <h4>等待分析</h4>
+                <p>请先上传图片,系统将自动开始分析</p>
+              </div>
+            </div>
+          }
+        </div>
+
+        <!-- 需求映射结果区域 -->
+        <div class="mapping-section" [class.disabled]="!analysisResult">
+          <h3>3. 需求映射生成</h3>
+          
+          @if (isGeneratingMapping) {
+            <div class="mapping-loading">
+              <div class="loading-spinner"></div>
+              <div class="loading-text">
+                <h4>正在生成需求映射...</h4>
+                <p>基于分析结果生成场景参数和映射关系</p>
+              </div>
+            </div>
+          } @else if (mappingError) {
+            <div class="mapping-error">
+              <div class="error-icon">❌</div>
+              <div class="error-text">
+                <h4>映射生成失败</h4>
+                <p>{{ mappingError }}</p>
+                <button class="retry-btn" (click)="retryMapping()">重新生成</button>
+              </div>
+            </div>
+          } @else if (requirementMapping) {
+            <div class="mapping-result">
+              <h4>需求映射生成完成 ✅</h4>
+              
+              <!-- 场景生成信息 -->
+              <div class="mapping-section-item">
+                <h5>场景生成</h5>
+                <div class="scene-info">
+                  <div class="info-row">
+                    <span class="label">基础场景:</span>
+                    <span class="value">{{ requirementMapping.sceneGeneration.baseScene }}</span>
+                  </div>
+                  @if (requirementMapping.sceneGeneration.atmospherePreview) {
+                    <div class="atmosphere-preview">
+                      <img [src]="requirementMapping.sceneGeneration.atmospherePreview" 
+                           alt="氛围感预览图"
+                           class="preview-image">
+                      <div class="preview-label">氛围感预览图</div>
+                    </div>
+                  }
+                </div>
+              </div>
+
+              <!-- 参数映射信息 -->
+              <div class="mapping-section-item">
+                <h5>参数映射</h5>
+                <div class="params-grid">
+                  <!-- 颜色参数 -->
+                  <div class="param-group">
+                    <h6>颜色映射</h6>
+                    <div class="color-params">
+                      <div class="param-item">
+                        <span class="label">主要颜色:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.primaryColors.length }} 种</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">色彩和谐:</span>
+                        <span class="value">{{ getColorHarmonyName(requirementMapping.parameterMapping.colorParams.colorHarmony) }}</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">饱和度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.saturation }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">亮度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.brightness }}%</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 空间参数 -->
+                  <div class="param-group">
+                    <h6>空间映射</h6>
+                    <div class="space-params">
+                      <div class="param-item">
+                        <span class="label">布局类型:</span>
+                        <span class="value">{{ getLayoutTypeName(requirementMapping.parameterMapping.spaceParams.layout.type) }}</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">空间流线:</span>
+                        <span class="value">{{ getFlowTypeName(requirementMapping.parameterMapping.spaceParams.layout.flow) }}</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">家具比例:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.furniture }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">开放度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.openness }}%</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 材质参数 -->
+                  <div class="param-group">
+                    <h6>材质映射</h6>
+                    <div class="material-params">
+                      <div class="param-item">
+                        <span class="label">纹理缩放:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.textureScale }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">反射率:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.reflectivity }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">粗糙度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.roughness }}%</span>
+                      </div>
+                      <div class="param-item">
+                        <span class="label">金属度:</span>
+                        <span class="value">{{ requirementMapping.parameterMapping.materialParams.metallic }}%</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 测试结果下载 -->
+              <div class="test-actions">
+                <button class="download-btn" (click)="downloadTestResult()">
+                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
+                    <polyline points="7,10 12,15 17,10"></polyline>
+                    <line x1="12" y1="15" x2="12" y2="3"></line>
+                  </svg>
+                  下载测试结果
+                </button>
+              </div>
+            </div>
+          } @else {
+            <div class="mapping-placeholder">
+              <div class="placeholder-icon">🎯</div>
+              <div class="placeholder-text">
+                <h4>等待映射生成</h4>
+                <p>请先完成图片分析,系统将自动生成需求映射</p>
+              </div>
+            </div>
+          }
+        </div>
+      </div>
+    }
+
+    <!-- 协作验证标签页 -->
+    @if (activeTab === 'collaboration') {
+      <div class="collaboration-section">
+        <!-- 一致性预警 -->
+        @if (consistencyWarnings.length > 0) {
+          <div class="consistency-warning">
+            <svg viewBox="0 0 24 24" fill="currentColor">
+              <path d="M12 2L1 21h22L12 2zm0 3.99L19.53 19H4.47L12 5.99zM11 16h2v2h-2v-2zm0-6h2v4h-2v-4z"/>
+            </svg>
+            <div>
+              <strong>一致性预警:</strong>
+              @for (warning of consistencyWarnings; track warning) {
+                <div>{{ warning }}</div>
+              }
+            </div>
+          </div>
+        }
+
+        <!-- 需求列表 -->
+        <div class="requirements-list">
+          <div class="list-header">
+            <h5>需求项目</h5>
+            <div class="list-controls">
+              <button class="btn-ghost btn-sm" (click)="sortRequirementsByPriority()">按优先级排序</button>
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存状态</button>
+            </div>
+          </div>
+
+          @for (requirement of requirementItems; track requirement.id) {
+            <div class="requirement-item" [class]="getStatusClass(requirement.status)">
+              <div class="requirement-header">
+                <div class="requirement-info">
+                  <h6>{{ requirement.title }}</h6>
+                  <p>{{ requirement.description }}</p>
+                  <div class="requirement-tags">
+                    @for (tag of requirement.tags; track tag) {
+                      <span class="tag">{{ tag }}</span>
+                    }
+                  </div>
+                </div>
+                <div class="requirement-meta">
+                  <select class="priority-select" 
+                          [value]="requirement.priority" 
+                          (change)="onPriorityChange(requirement.id, $event)">
+                    <option value="high">高优先级</option>
+                    <option value="medium">中优先级</option>
+                    <option value="low">低优先级</option>
+                  </select>
+                  <span class="status-badge" [class]="getStatusClass(requirement.status)">
+                    {{ requirement.status === 'confirmed' ? '已确认' : requirement.status === 'rejected' ? '已拒绝' : '待确认' }}
+                  </span>
+                </div>
+              </div>
+              
+              <div class="requirement-actions">
+                @if (requirement.status === 'pending') {
+                  <button class="btn-success btn-sm" (click)="confirmRequirement(requirement.id)">确认</button>
+                  <button class="btn-danger btn-sm" (click)="rejectRequirement(requirement.id, '需要进一步讨论')">拒绝</button>
+                }
+                <button class="btn-ghost btn-sm" (click)="requirement.showComments = !requirement.showComments">
+                  评论 ({{ getCommentsForRequirement(requirement.id).length }})
+                  @if (hasUnreadComments(requirement.id)) {
+                    <span class="unread-indicator"></span>
+                  }
+                </button>
+              </div>
+
+              @if (requirement.showComments) {
+                <div class="comments-section">
+                  @if (getCommentsForRequirement(requirement.id).length > 0) {
+                    <div class="comments-list">
+                      @for (comment of getCommentsForRequirement(requirement.id); track comment.id) {
+                        <div class="comment-item" [class.resolved]="comment.status === 'resolved'">
+                          <div class="comment-header">
+                            <span class="comment-author">{{ comment.author }}</span>
+                            <span class="comment-role">{{ comment.role === 'designer' ? '设计师' : comment.role === 'customer-service' ? '客服' : '客户' }}</span>
+                            <span class="comment-time">{{ comment.timestamp | date:'MM-dd HH:mm' }}</span>
+                            @if (comment.status === 'pending') {
+                              <button class="btn-ghost btn-xs" (click)="resolveComment(comment.id)">标记已解决</button>
+                            }
+                          </div>
+                          <div class="comment-content">{{ comment.content }}</div>
+                        </div>
+                      }
+                    </div>
+                  }
+                  <div class="add-comment">
+                    <textarea #commentInput placeholder="添加评论..." rows="2"></textarea>
+                    <button class="btn-primary btn-sm" (click)="addCommentToRequirement(requirement.id, commentInput.value); commentInput.value = ''">
+                      发送
+                    </button>
+                  </div>
+                </div>
+              }
+            </div>
+          }
+        </div>
+      </div>
+    }
+
+    <!-- 进度管理标签页 -->
+    @if (activeTab === 'progress') {
+      <div class="progress-section">
+        <!-- 整体进度 -->
+        <div class="overall-progress">
+          <div class="section-header">
+            <h5>整体进度</h5>
+          </div>
+          
+          <div class="progress-container">
+            <div class="progress-visual">
+              <div class="linear-progress">
+                <div class="progress-track">
+                  <div class="progress-bar-fill" [style.width.%]="getProgressPercentage()"></div>
+                </div>
+                <div class="progress-text">{{ getProgressPercentage() }}%</div>
+              </div>
+              
+              <div class="circular-progress">
+                <svg viewBox="0 0 120 120" class="progress-circle">
+                  <circle cx="60" cy="60" r="54" fill="none" stroke="#e5e7eb" stroke-width="8"></circle>
+                  <circle cx="60" cy="60" r="54" fill="none" stroke="#3b82f6" stroke-width="8"
+                    stroke-linecap="round"
+                    [attr.stroke-dasharray]="339.292"
+                    [attr.stroke-dashoffset]="339.292 - (339.292 * getProgressPercentage() / 100)"
+                    transform="rotate(-90 60 60)">
+                  </circle>
+                </svg>
+                <div class="progress-text">
+                  <div class="progress-percentage">{{ getProgressPercentage() }}%</div>
+                  <div class="progress-label">完成度</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 进度统计 -->
+        <div class="progress-stats">
+          <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('confirmed') }}</div>
+              <div class="stat-label">已确认</div>
+            </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('pending') }}</div>
+              <div class="stat-label">待确认</div>
+            </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('rejected') }}</div>
+              <div class="stat-label">已拒绝</div>
+            </div>
+          <div class="stat-item">
+            <div class="stat-number">{{ getUnresolvedCommentsCount() }}</div>
+            <div class="stat-label">待解决评论</div>
+          </div>
+        </div>
+
+        <!-- 历史状态 -->
+        <div class="history-section">
+          <div class="section-header">
+            <h5>历史状态</h5>
+            <div class="history-controls">
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+              <select class="history-select" (change)="onHistoryStateChange($event)">
+                <option value="-1">选择历史状态</option>
+                @for (state of historyStates; track $index) {
+                  <option [value]="$index">{{ state.timestamp | date:'MM-dd HH:mm' }} - {{ state.author }}</option>
+                }
+              </select>
+            </div>
+          </div>
+
+          @if (historyStates.length > 0) {
+            <div class="history-timeline">
+              @for (state of historyStates; track $index) {
+                <div class="timeline-item" (click)="restoreHistoryState($index)">
+                  <div class="timeline-marker"></div>
+                  <div class="timeline-content">
+                    <div class="timeline-header">
+                      <span class="timeline-time">{{ state.timestamp | date:'MM-dd HH:mm' }}</span>
+                      <span class="timeline-author">{{ state.author }}</span>
+                    </div>
+                    <div class="timeline-summary">
+                      状态快照:{{ state.requirementItems.length }}个需求项,
+                      {{ getRequirementCountByStatus('confirmed') }}个已确认
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-history">
+              <p>暂无历史状态记录</p>
+              <button class="btn-primary btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+            </div>
+          }
+        </div>
+
+        <!-- 需求状态分布 -->
+        <div class="status-distribution">
+          <h5>需求状态分布</h5>
+          <div class="status-bars">
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('confirmed') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar confirmed" 
+                     [style.width.%]="getStatusPercentage('confirmed')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">待确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('pending') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar pending" 
+                     [style.width.%]="getStatusPercentage('pending')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已拒绝</span>
+                <span class="status-count">{{ getRequirementCountByStatus('rejected') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar rejected" 
+                     [style.width.%]="getStatusPercentage('rejected')"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+  </div>
+</div>
+
+<!-- 保存状态和手动保存按钮 -->
+<div class="save-section">
+  <div class="save-status">
+    <span class="save-icon" [class]="'save-icon-' + saveStatus">{{ getSaveStatusIcon() }}</span>
+    <span class="save-text">{{ getSaveStatusText() }}</span>
+  </div>
+  
+  <div class="save-actions">
+    <button class="btn-secondary" 
+            [disabled]="isSaving || !hasUnsavedChanges"
+            (click)="manualSave()">
+      @if (isSaving) {
+        <span class="loading-spinner"></span>
+        保存中...
+      } @else {
+        手动保存
+      }
+    </button>
+    
+    <div class="auto-save-toggle">
+      <label class="toggle-label">
+        <input type="checkbox" 
+               [(ngModel)]="autoSaveEnabled"
+               class="toggle-input">
+        <span class="toggle-slider"></span>
+        <span class="toggle-text">自动保存</span>
+      </label>
+    </div>
+  </div>
+</div>
+
+<!-- 上传成功弹窗 -->
+<app-upload-success-modal
+  [isVisible]="showUploadSuccessModal"
+  [uploadedFiles]="uploadedFiles"
+  [uploadType]="uploadType"
+  [analysisResult]="colorAnalysisResult"
+  [isAnalyzing]="isAnalyzingColors"
+  (closeModal)="onModalClose()"
+  (analyzeColors)="onAnalyzeColors()"
+  (viewReport)="onViewReport()">
+</app-upload-success-modal>
+
+<!-- 全局提示(支持全屏与角落两模式) -->
+<app-global-prompt
+  [visible]="showGlobalPrompt"
+  [title]="promptTitle"
+  [message]="promptMessage"
+  [mode]="promptMode"
+  [position]="promptPosition"
+  [autoDismissMs]="4000"
+  (closed)="onPromptClose()">
+</app-global-prompt>
+
+<!-- 完整报告全屏覆盖层 -->
+<app-full-report-overlay
+  [visible]="showFullReportOverlay"
+  [colorResult]="colorAnalysisResult"
+  [cadResult]="cadAnalysisResult"
+  (close)="onCloseFullReport()">
+</app-full-report-overlay>

+ 78 - 82
src/app/pages/designer/project-detail/project-detail.html

@@ -1390,7 +1390,7 @@
                     @if (stage !== '订单创建') {
                       <div class="vertical-stage-header">
                         <span class="dot" [class.completed]="getStageStatus(stage) === 'completed'" [class.active]="getStageStatus(stage) === 'active'"></span>
-                        <h3>{{ stage === '方案确认' ? '色彩分析报告' : stage }}</h3>
+                        <h3>{{ stage === '需求沟通' ? '需求映射' : (stage === '方案确认' ? '色彩分析报告' : stage) }}</h3>
                       </div>
                     }
                     <!-- 直接复用原阶段内容卡片:按stage匹配显示 -->
@@ -1656,104 +1656,100 @@
                           </div>
                         </div>
                       } @else if (stage === '需求沟通') {
+                        <!-- 需求沟通阶段:确认需求组件 -->
                         <app-requirements-confirm-card 
                           (requirementConfirmed)="syncRequirementKeyInfo($event)"
                           (progressUpdated)="syncRequirementKeyInfo($event)"
                           (stageCompleted)="onRequirementsStageCompleted($event)"
-                          (dataUpdated)="onRequirementDataUpdated($event)">
+                          (dataUpdated)="onRequirementDataUpdated($event)"
+                          (mappingDataUpdated)="onMappingDataUpdated($event)">
                         </app-requirements-confirm-card>
-                        <!-- 新增:需求确认右侧下方左右分区,滚动展示参考图片与CAD图纸 -->
-                        <div class="materials-two-pane" style="display:flex;gap:16px;margin-top:12px;">
-                          <div class="pane left" style="flex:1;">
-                            <div class="pane-header" style="font-weight:600;margin-bottom:8px;">参考图片</div>
-                            <div class="pane-body scroller" style="max-height:240px;overflow:auto;border:1px solid #eee;border-radius:8px;padding:8px;">
-                              @if (referenceImages.length > 0) {
-                                <div class="images-grid" style="display:grid;grid-template-columns:repeat(auto-fill, minmax(100px, 1fr));gap:8px;">
-                                  @for (img of referenceImages; track img.id) {
-                                    <div class="image-thumb" style="cursor:pointer;border:1px solid #f0f0f0;border-radius:6px;overflow:hidden;" (click)="previewImageFile(img.url)">
-                                      <img [src]="img.url" [alt]="img.name || '参考图'" style="width:100%;height:90px;object-fit:cover;" />
-                                    </div>
-                                  }
-                                </div>
-                              } @else {
-                                <div class="empty-tip" style="color:#888;">暂无参考图片</div>
-                              }
-                            </div>
-                          </div>
-                          <!-- 中间列:保留 CAD 文件列表(不改动原展示) -->
-                          <div class="pane middle" style="flex:1;">
-                            <div class="pane-header" style="font-weight:600;margin-bottom:8px;">CAD图纸</div>
-                            <div class="pane-body scroller" style="max-height:240px;overflow:auto;border:1px solid #eee;border-radius:8px;padding:8px;">
-                              @if (cadFiles.length > 0) {
-                                <div class="files-grid" style="display:grid;grid-template-columns:repeat(auto-fill, minmax(140px, 1fr));gap:8px;">
-                                  @for (file of cadFiles; track file.id) {
-                                    <div class="file-thumb" style="cursor:pointer;border:1px dashed #e5e5e5;border-radius:6px;padding:8px;background:#fafafa;" (click)="previewCadFile(file.url)">
-                                      <div class="file-icon" style="font-size:12px;color:#555;margin-bottom:6px;">CAD</div>
-                                      <div class="file-name" style="font-size:12px;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{{ file.name }}</div>
-                                    </div>
-                                  }
-                                </div>
-                              } @else {
-                                <div class="empty-tip" style="color:#888;">暂无CAD文件</div>
-                              }
-                            </div>
-                          </div>
-
-                          
-                        </div>
                         
                       } @else if (stage === '方案确认') {
-                        <!-- 直接以色彩分析面板填充最右侧全部区域 -->
-                        <div class="color-analysis-panel" style="width:100%; display:flex; flex-direction:column; gap:14px;">
-                          @if (isAnalyzing) {
-                            <div class="analysis-progress" style="margin-bottom:4px;">
-                              <div class="progress-header" style="display:flex;align-items:center;justify-content:space-between;">
-                                <h5 style="margin:0;font-size:14px;">正在解析素材...</h5>
-                                <span class="progress-percentage">{{ analysisProgress.toFixed(0) }}%</span>
+                        <!-- 需求映射面板(替换原色彩分析报告区域) -->
+                        <div class="requirement-mapping-panel" style="width:100%; display:flex; flex-direction:column; gap:14px;">
+                          <h3 class="panel-title" style="margin:0 0 16px 0; font-size:18px; font-weight:700; color:#495057;">🎯 需求映射</h3>
+                          
+                          <div class="mapping-progress" style="background:#f8f9fa; border-radius:8px; padding:16px; margin-bottom:12px;">
+                            @if (mappingUploadedFiles.length > 0) {
+                              <div class="progress-badge" style="display:inline-block; padding:6px 14px; background:linear-gradient(135deg, #667eea 0%, #764ba2 100%); color:white; border-radius:14px; font-size:12px; font-weight:600; margin-bottom:12px;">
+                                📸 已同步 {{ mappingUploadedFiles.length }} 张参考图片
                               </div>
-                              <div class="progress-bar" style="height:8px;background:#eee;border-radius:6px;overflow:hidden;">
-                                <div class="progress-fill" [style.width.%]="analysisProgress" style="height:100%;background:#007AFF;"></div>
+                            }
+                            
+                            <div class="steps-list" style="display:flex; flex-direction:column; gap:10px;">
+                              <div class="step-item" style="display:flex; align-items:center; gap:10px;">
+                                <span class="step-index" style="width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:#e9ecef;color:#495057;font-size:12px;">1</span>
+                                <span class="step-title" style="flex:1;font-size:14px;">图片上传</span>
+                                <span class="step-status" style="font-size:12px;color:#666;">@if (mappingUploadedFiles.length > 0) { ✅ 完成 } @else { ⭕ 待上传 }</span>
+                              </div>
+                              <div class="step-item" style="display:flex; align-items:center; gap:10px;">
+                                <span class="step-index" style="width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:#e9ecef;color:#495057;font-size:12px;">2</span>
+                                <span class="step-title" style="flex:1;font-size:14px;">图片分析</span>
+                                <span class="step-status" style="font-size:12px;color:#666;">@if (mappingIsAnalyzing) { ⏳ 进行中 } @else if (mappingAnalysisResult) { ✅ 完成 } @else { ⭕ 待开始 }</span>
+                              </div>
+                              <div class="step-item" style="display:flex; align-items:center; gap:10px;">
+                                <span class="step-index" style="width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:#e9ecef;color:#495057;font-size:12px;">3</span>
+                                <span class="step-title" style="flex:1;font-size:14px;">需求映射</span>
+                                <span class="step-status" style="font-size:12px;color:#666;">@if (mappingIsGeneratingMapping) { ⏳ 生成中 } @else if (mappingRequirementMapping) { ✅ 完成 } @else { ⭕ 待生成 }</span>
                               </div>
                             </div>
-                          }
-                          @if (colorAnalysisResult) {
-                            <div class="report-summary" style="display:flex;flex-direction:column;gap:18px;">
-                              <div class="report-images">
-                                <div class="section-title" style="font-weight:600;margin-bottom:8px;">参考图</div>
-                                <div class="images-row" style="display:flex;gap:10px;flex-wrap:wrap;">
-                                  @if (colorAnalysisResult.originalImage) {
-                                    <img [src]="colorAnalysisResult.originalImage" alt="参考图" (click)="previewImageFile(colorAnalysisResult.originalImage)" style="width:140px;height:140px;object-fit:cover;border-radius:8px;cursor:pointer;border:1px solid #eee;" />
-                                  } @else {
-                                    <div class="empty-tip" style="color:#888;">暂无参考图</div>
-                                  }
-                                </div>
+                          </div>
+                          
+                          @if (mappingAnalysisResult) {
+                            <div class="analysis-summary-panel" style="background:#f8f9fa;border-radius:8px;padding:16px;">
+                              <h4 style="margin:0 0 12px 0; font-size:15px; font-weight:600; color:#333;">图片分析摘要</h4>
+                              <div class="param-items" style="display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px;">
+                                <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">主色</span><span style="font-weight:500;">{{ mappingAnalysisResult.primaryColor?.hex || '未知' }}</span></div>
+                                <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">材质</span><span style="font-weight:500;">{{ getMaterialName(mappingAnalysisResult.materialType) }}</span></div>
+                                <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">灯光</span><span style="font-weight:500;">{{ getLightingMoodName(mappingAnalysisResult.lightingMood) }}</span></div>
+                                <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">对比</span><span style="font-weight:500;">{{ mappingAnalysisResult.contrast || '未知' }}</span></div>
                               </div>
-                              <div class="palette">
-                                <div class="section-title" style="font-weight:600;margin-bottom:8px;font-size:13px;">色板</div>
-                                <div class="palette-row" style="display:flex;gap:8px;flex-wrap:wrap;">
-                                  @for (c of colorAnalysisResult.colors; track c.hex) {
-                                    <div class="palette-item" style="display:flex;align-items:center;gap:6px;padding:6px;border:1px solid #eee;border-radius:6px;">
-                                      <div class="swatch" [style.backgroundColor]="c.hex" style="width:20px;height:20px;border-radius:4px;border:1px solid #ddd;"></div>
-                                      <div class="hex" style="font-size:12px;color:#333;">{{ c.hex }}</div>
-                                      <div class="pct" style="font-size:12px;color:#666;">{{ c.percentage }}%</div>
-                                    </div>
-                                  }
+                            </div>
+                          }
+                          
+                          @if (mappingRequirementMapping) {
+                            <div class="mapping-result-panel" style="background:#f8f9fa;border-radius:8px;padding:16px;">
+                              <h4 style="margin:0 0 12px 0; font-size:15px; font-weight:600; color:#333;">需求映射结果</h4>
+                              <div class="param-section" style="margin-bottom:12px;">
+                                <div class="section-title" style="font-weight:600;color:#555;margin-bottom:8px;font-size:13px;">色彩参数</div>
+                                <div class="param-items" style="display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px;">
+                                  <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">和谐度</span><span style="font-weight:500;">{{ getColorHarmonyName(mappingRequirementMapping.color.harmony) }}</span></div>
+                                  <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">色温</span><span style="font-weight:500;">{{ getTemperatureName(mappingRequirementMapping.color.temperature) }}</span></div>
                                 </div>
                               </div>
-                              <div class="dominant-color">
-                                <div class="section-title" style="font-weight:600;margin-bottom:8px;font-size:13px;">主色</div>
-                                <div class="dominant-row" style="display:flex;align-items:center;gap:8px;">
-                                  <div class="swatch" [style.backgroundColor]="dominantColorHex || '#ccc'" style="width:28px;height:28px;border-radius:6px;border:1px solid #ddd;"></div>
-                                  <span class="hex" style="font-size:12px;color:#333;">
-                                    {{ dominantColorHex || (colorAnalysisResult.colors.length > 0 ? colorAnalysisResult.colors[0].hex : '未知') }}
-                                  </span>
+                              <div class="param-section">
+                                <div class="section-title" style="font-weight:600;color:#555;margin-bottom:8px;font-size:13px;">空间参数</div>
+                                <div class="param-items" style="display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px;">
+                                  <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">布局</span><span style="font-weight:500;">{{ getLayoutTypeName(mappingRequirementMapping.space.layoutType) }}</span></div>
+                                  <div class="param-row" style="font-size:13px;"><span style="color:#868e96;">流线</span><span style="font-weight:500;">{{ getFlowTypeName(mappingRequirementMapping.space.flowType) }}</span></div>
                                 </div>
                               </div>
                             </div>
-                          } @else {
-                            <div class="empty-tip" style="color:#888;">暂无色彩分析结果</div>
+                          }
+                          
+                          @if (mappingIsAnalyzing) {
+                            <div class="analyzing-indicator" style="display:flex; align-items:center; gap:10px; background:#f8f9fa; padding:12px; border-radius:8px;">
+                              <div class="spinner" style="width:16px;height:16px;border:2px solid #e5e7eb;border-top-color:#3b82f6;border-radius:50%;animation:spin 1s linear infinite;"></div>
+                              <span style="font-size:14px; color:#666;">正在解析图片...</span>
+                            </div>
+                          }
+                          
+                          @if (mappingIsGeneratingMapping) {
+                            <div class="analyzing-indicator" style="display:flex; align-items:center; gap:10px; background:#f8f9fa; padding:12px; border-radius:8px;">
+                              <div class="spinner" style="width:16px;height:16px;border:2px solid #e5e7eb;border-top-color:#10b981;border-radius:50%;animation:spin 1s linear infinite;"></div>
+                              <span style="font-size:14px; color:#666;">正在生成需求映射...</span>
+                            </div>
+                          }
+                          
+                          @if (!mappingAnalysisResult && !mappingIsAnalyzing && mappingUploadedFiles.length === 0) {
+                            <div class="empty-state" style="text-align:center; padding:32px; color:#999;">
+                              <div style="font-size:48px; margin-bottom:12px;">📊</div>
+                              <div style="font-size:14px;">在需求沟通中上传图片后,这里将实时显示需求映射结果</div>
+                            </div>
                           }
                         </div>
+                        
                       } @else if (stage === '建模') {
                         <!-- 建模阶段:直接显示建模相关内容 -->
                         <div class="modeling-stage-panel">
@@ -2047,7 +2043,7 @@
                                                     <div class="image-overlay">
                                                       <div class="image-name">{{ img.name }}</div>
                                 </div>
-                              </div>
+                            </div>
                                         }
                                       </div>
                                     } @else {

+ 218 - 0
src/app/pages/designer/project-detail/project-detail.scss

@@ -5238,3 +5238,221 @@
   }
 }
 
+// ==================== 需求映射面板样式 ====================
+.requirement-mapping-panel {
+  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+  max-height: calc(100vh - 200px);
+  overflow-y: auto;
+  position: sticky;
+  top: 20px;
+  
+  // 美化滚动条
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+  
+  &::-webkit-scrollbar-track {
+    background: #f1f1f1;
+    border-radius: 3px;
+  }
+  
+  &::-webkit-scrollbar-thumb {
+    background: #888;
+    border-radius: 3px;
+    
+    &:hover {
+      background: #555;
+    }
+  }
+  
+  scrollbar-width: thin;
+  scrollbar-color: #888 #f1f1f1;
+  
+  .panel-title {
+    margin: 0 0 20px 0;
+    font-size: 22px;
+    font-weight: 700;
+    color: #495057;
+  }
+  
+  .mapping-progress {
+    margin-bottom: 20px;
+    
+    .progress-badge {
+      display: inline-block;
+      padding: 8px 16px;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      color: white;
+      border-radius: 16px;
+      font-size: 13px;
+      font-weight: 600;
+      box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+      margin-bottom: 16px;
+      animation: badgePulse 2s ease-in-out infinite;
+    }
+    
+    .steps-list {
+      display: flex;
+      flex-direction: column;
+      gap: 8px;
+      
+      .step-item {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 8px 12px;
+        background: white;
+        border-radius: 6px;
+        border: 1px solid #e9ecef;
+        
+        &.completed {
+          border-color: #28a745;
+          background: #f0fdf4;
+        }
+        
+        &.in-progress {
+          border-color: #007bff;
+          background: #eff6ff;
+        }
+        
+        .step-icon {
+          font-size: 16px;
+        }
+        
+        .step-name {
+          font-size: 14px;
+          color: #495057;
+        }
+      }
+    }
+  }
+  
+  .analysis-summary-panel,
+  .mapping-result-panel {
+    background: white;
+    border-radius: 8px;
+    padding: 16px;
+    margin-bottom: 16px;
+    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
+    
+    h4 {
+      margin: 0 0 12px 0;
+      font-size: 16px;
+      font-weight: 600;
+      color: #333;
+    }
+    
+    .summary-grid {
+      display: flex;
+      flex-direction: column;
+      gap: 8px;
+      
+      .summary-item {
+        display: flex;
+        justify-content: space-between;
+        padding: 6px 0;
+        border-bottom: 1px solid #f0f0f0;
+        
+        &:last-child {
+          border-bottom: none;
+        }
+        
+        .label {
+          font-size: 13px;
+          color: #666;
+        }
+        
+        .value {
+          font-size: 13px;
+          font-weight: 600;
+          color: #333;
+        }
+      }
+    }
+  }
+  
+  .mapping-result-panel {
+    .param-section {
+      margin-bottom: 16px;
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      h5 {
+        margin: 0 0 8px 0;
+        font-size: 14px;
+        font-weight: 600;
+        color: #495057;
+        padding-bottom: 4px;
+        border-bottom: 2px solid #e9ecef;
+      }
+      
+      .param-items {
+        display: flex;
+        flex-direction: column;
+        gap: 6px;
+        
+        .param-row {
+          display: flex;
+          justify-content: space-between;
+          padding: 4px 0;
+          font-size: 13px;
+          
+          .label {
+            color: #666;
+          }
+          
+          .value {
+            font-weight: 600;
+            color: #333;
+          }
+        }
+      }
+    }
+  }
+  
+  .analyzing-indicator {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 12px;
+    padding: 16px;
+    background: white;
+    border-radius: 8px;
+    margin-top: 16px;
+    
+    .spinner {
+      width: 20px;
+      height: 20px;
+      border: 3px solid #f3f3f3;
+      border-top: 3px solid #667eea;
+      border-radius: 50%;
+      animation: spin 1s linear infinite;
+    }
+    
+    span {
+      font-size: 14px;
+      color: #666;
+    }
+  }
+}
+
+@keyframes badgePulse {
+  0%, 100% {
+    box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+    transform: scale(1);
+  }
+  50% {
+    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.5);
+    transform: scale(1.02);
+  }
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}

+ 208 - 68
src/app/pages/designer/project-detail/project-detail.ts

@@ -326,6 +326,14 @@ export class ProjectDetail implements OnInit, OnDestroy {
   lightingAnalysis: any = null;
   materialAnalysisData: any[] = [];
   
+  // 新增:需求映射数据(用于右侧面板显示)
+  mappingUploadedFiles: any[] = [];
+  mappingAnalysisResult: any = null;
+  mappingRequirementMapping: any = null;
+  mappingTestSteps: any[] = [];
+  mappingIsAnalyzing: boolean = false;
+  mappingIsGeneratingMapping: boolean = false;
+  
   // 新增:9阶段顺序(串式流程)- 包含后期阶段
   stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价', '投诉处理'];
   // 新增:阶段展开状态(默认全部收起,当前阶段在数据加载后自动展开)
@@ -619,22 +627,28 @@ export class ProjectDetail implements OnInit, OnDestroy {
     // 初始化售后模块示例数据
     this.initializeAftercareData();
     
-    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();
-      
-      // 启动客户信息自动同步
-      this.startAutoSync();
+    this.route.paramMap.subscribe({
+      next: (params) => {
+        this.projectId = params.get('id') || '';
+        // 根据当前URL检测视图上下文
+        this.roleContext = this.detectRoleContextFromUrl();
+        this.loadProjectData();
+        this.loadExceptionHistories();
+        this.loadProjectMembers();
+        this.loadProjectFiles();
+        this.loadTimelineEvents();
+        
+        // 启动客户信息自动同步
+        this.startAutoSync();
+      },
+      error: (error) => {
+        console.error('路由参数订阅失败:', error);
+      }
     });
 
     // 新增:监听查询参数,支持通过 activeTab 设置初始标签页和 currentStage 设置当前阶段
-    this.route.queryParamMap.subscribe(qp => {
+    this.route.queryParamMap.subscribe({
+      next: (qp) => {
       const raw = qp.get('activeTab');
       const alias: Record<string, 'progress' | 'members' | 'files'> = {
         requirements: 'progress',
@@ -742,6 +756,10 @@ export class ProjectDetail implements OnInit, OnDestroy {
           this.isSyncingCustomerInfo = false;
         }
       }
+      },
+      error: (error) => {
+        console.error('查询参数订阅失败:', error);
+      }
     });
     
     // 添加点击事件监听器,当点击页面其他位置时关闭下拉菜单
@@ -1092,31 +1110,42 @@ export class ProjectDetail implements OnInit, OnDestroy {
   
   // 加载历史反馈记录
   loadExceptionHistories(): void {
-    this.projectService.getExceptionHistories(this.projectId).subscribe(histories => {
-      this.exceptionHistories = histories;
+    this.projectService.getExceptionHistories(this.projectId).subscribe({
+      next: (histories) => {
+        this.exceptionHistories = histories;
+      },
+      error: (error) => {
+        console.error('加载异常历史失败:', error);
+      }
     });
   }
 
   loadProjectDetails(): void {
     console.log('=== loadProjectDetails 开始加载项目数据 ===');
     console.log('当前项目ID:', this.projectId);
-    this.projectService.getProjectById(this.projectId).subscribe(project => {
-      console.log('获取到的项目数据:', project);
-      if (!project) {
-        console.error('未找到项目数据,项目ID:', this.projectId);
-        // 如果找不到项目,尝试使用默认项目ID
-        console.log('尝试使用默认项目ID: proj-001');
-        this.projectService.getProjectById('proj-001').subscribe(defaultProject => {
-          console.log('默认项目数据:', defaultProject);
-          if (defaultProject) {
-            this.project = defaultProject;
-            this.currentStage = defaultProject.currentStage || '';
-            console.log('使用默认项目,设置当前阶段:', this.currentStage);
-            this.setupStageExpansion(defaultProject);
-          }
-        });
-        return;
-      }
+    this.projectService.getProjectById(this.projectId).subscribe({
+      next: (project) => {
+        console.log('获取到的项目数据:', project);
+        if (!project) {
+          console.error('未找到项目数据,项目ID:', this.projectId);
+          // 如果找不到项目,尝试使用默认项目ID
+          console.log('尝试使用默认项目ID: proj-001');
+          this.projectService.getProjectById('proj-001').subscribe({
+            next: (defaultProject) => {
+              console.log('默认项目数据:', defaultProject);
+              if (defaultProject) {
+                this.project = defaultProject;
+                this.currentStage = defaultProject.currentStage || '';
+                console.log('使用默认项目,设置当前阶段:', this.currentStage);
+                this.setupStageExpansion(defaultProject);
+              }
+            },
+            error: (error) => {
+              console.error('加载默认项目失败:', error);
+            }
+          });
+          return;
+        }
       
       this.project = project;
       // 设置当前阶段
@@ -1127,6 +1156,10 @@ export class ProjectDetail implements OnInit, OnDestroy {
       }
       // 检查技能匹配度 - 已注释掉以取消弹窗警告
       // this.checkSkillMismatch();
+      },
+      error: (error) => {
+        console.error('加载项目详情失败:', error);
+      }
     });
   }
 
@@ -1184,30 +1217,42 @@ export class ProjectDetail implements OnInit, OnDestroy {
     
     // 模拟API加载过程
     setTimeout(() => {
-      this.projectService.getRenderProgress(this.projectId).subscribe(progress => {
-        this.renderProgress = progress;
-        this.isLoadingRenderProgress = false;
-        
-        // 模拟API加载失败的情况
-        if (!progress) {
-          this.errorLoadingRenderProgress = true;
-          // 通知技术组长
+      this.projectService.getRenderProgress(this.projectId).subscribe({
+        next: (progress) => {
+          this.renderProgress = progress;
+          this.isLoadingRenderProgress = false;
+          
+          // 模拟API加载失败的情况
+          if (!progress) {
+            this.errorLoadingRenderProgress = true;
+            // 通知技术组长
           this.notifyTeamLeader('render-failed');
         } else {
           // 检查是否需要显示超时预警
           this.checkRenderTimeout();
         }
+        },
+        error: (error) => {
+          console.error('加载渲染进度失败:', error);
+          this.isLoadingRenderProgress = false;
+          this.errorLoadingRenderProgress = true;
+        }
       });
     }, 1000);
   }
 
   loadCustomerFeedbacks(): void {
-    this.projectService.getCustomerFeedbacks().subscribe(feedbacks => {
-      this.feedbacks = feedbacks.filter(f => f.projectId === this.projectId);
-      // 为反馈添加分类标签
-      this.tagCustomerFeedbacks();
-      // 检查是否有需要处理的反馈并启动倒计时
-      this.checkFeedbackTimeout();
+    this.projectService.getCustomerFeedbacks().subscribe({
+      next: (feedbacks) => {
+        this.feedbacks = feedbacks.filter(f => f.projectId === this.projectId);
+        // 为反馈添加分类标签
+        this.tagCustomerFeedbacks();
+        // 检查是否有需要处理的反馈并启动倒计时
+        this.checkFeedbackTimeout();
+      },
+      error: (error) => {
+        console.error('加载客户反馈失败:', error);
+      }
     });
   }
 
@@ -1231,35 +1276,55 @@ export class ProjectDetail implements OnInit, OnDestroy {
   }
 
   loadSettlements(): void {
-    this.projectService.getSettlements().subscribe(settlements => {
-      this.settlements = settlements.filter(s => s.projectId === this.projectId);
+    this.projectService.getSettlements().subscribe({
+      next: (settlements) => {
+        this.settlements = settlements.filter(s => s.projectId === this.projectId);
+      },
+      error: (error) => {
+        console.error('加载结算信息失败:', error);
+      }
     });
   }
 
   loadRequirementChecklist(): void {
-    this.projectService.generateRequirementChecklist(this.projectId).subscribe(checklist => {
-      this.requirementChecklist = checklist;
+    this.projectService.generateRequirementChecklist(this.projectId).subscribe({
+      next: (checklist) => {
+        this.requirementChecklist = checklist;
+      },
+      error: (error) => {
+        console.error('加载需求清单失败:', error);
+      }
     });
   }
 
   updateFeedbackStatus(feedbackId: string, status: '处理中' | '已解决'): void {
-    this.projectService.updateFeedbackStatus(feedbackId, status).subscribe(() => {
-      this.loadCustomerFeedbacks(); // 重新加载反馈
-      // 清除倒计时
-      if (this.countdownInterval) {
-        clearInterval(this.countdownInterval);
-        this.feedbackTimeoutCountdown = 0;
+    this.projectService.updateFeedbackStatus(feedbackId, status).subscribe({
+      next: () => {
+        this.loadCustomerFeedbacks(); // 重新加载反馈
+        // 清除倒计时
+        if (this.countdownInterval) {
+          clearInterval(this.countdownInterval);
+          this.feedbackTimeoutCountdown = 0;
+        }
+      },
+      error: (error) => {
+        console.error('更新反馈状态失败:', error);
       }
     });
   }
 
   updateProjectStage(stage: ProjectStage): void {
     if (this.project) {
-      this.projectService.updateProjectStage(this.projectId, stage).subscribe(() => {
-        this.currentStage = stage; // 同步更新本地状态
-        this.project!.currentStage = stage; // 同步更新project对象的currentStage
-        this.loadProjectDetails(); // 重新加载项目详情
-        this.cdr.detectChanges(); // 触发变更检测以更新导航栏颜色
+      this.projectService.updateProjectStage(this.projectId, stage).subscribe({
+        next: () => {
+          this.currentStage = stage; // 同步更新本地状态
+          this.project!.currentStage = stage; // 同步更新project对象的currentStage
+          this.loadProjectDetails(); // 重新加载项目详情
+          this.cdr.detectChanges(); // 触发变更检测以更新导航栏颜色
+        },
+        error: (error) => {
+          console.error('更新项目阶段失败:', error);
+        }
       });
     }
   }
@@ -1298,13 +1363,18 @@ export class ProjectDetail implements OnInit, OnDestroy {
     }
   }
   generateReminderMessage(): void {
-    this.projectService.generateReminderMessage('stagnation').subscribe(message => {
-      this.reminderMessage = message;
-      
-      // 3秒后自动清除提醒
-      setTimeout(() => {
-        this.reminderMessage = '';
-      }, 3000);
+    this.projectService.generateReminderMessage('stagnation').subscribe({
+      next: (message) => {
+        this.reminderMessage = message;
+        
+        // 3秒后自动清除提醒
+        setTimeout(() => {
+          this.reminderMessage = '';
+        }, 3000);
+      },
+      error: (error) => {
+        console.error('生成提醒消息失败:', error);
+      }
     });
   }
 
@@ -2938,7 +3008,24 @@ export class ProjectDetail implements OnInit, OnDestroy {
     
     // 同步关键信息
     this.syncRequirementKeyInfo(data);
+  }
+
+  // 新增:接收需求映射数据更新
+  onMappingDataUpdated(mappingData: any): void {
+    console.log('🔄 收到需求映射数据更新:', mappingData);
     
+    this.mappingUploadedFiles = mappingData.uploadedFiles || [];
+    this.mappingAnalysisResult = mappingData.analysisResult;
+    this.mappingRequirementMapping = mappingData.requirementMapping;
+    this.mappingTestSteps = mappingData.testSteps || [];
+    this.mappingIsAnalyzing = mappingData.isAnalyzing || false;
+    this.mappingIsGeneratingMapping = mappingData.isGeneratingMapping || false;
+    
+    this.cdr.detectChanges(); // 触发界面更新
+  }
+
+  // 继续原有的onRequirementDataUpdated方法内容
+  private updateProjectInfoFromRequirementData(data: any): void {
     // 更新客户信息显示
     if (data && this.project) {
       // 更新项目的客户信息
@@ -5280,4 +5367,57 @@ export class ProjectDetail implements OnInit, OnDestroy {
     
     alert(`✨ ${title}\n\n${description}\n\n点击确定关闭`);
   }
+
+  // ==================== 需求映射辅助方法 ====================
+  
+  getMaterialName(category: string | undefined): string {
+    if (!category) return '未识别';
+    const nameMap: { [key: string]: string } = {
+      'wood': '木材', 'metal': '金属', 'fabric': '织物', 'leather': '皮革',
+      'plastic': '塑料', 'glass': '玻璃', 'ceramic': '陶瓷', 'stone': '石材', 'composite': '复合材料'
+    };
+    return nameMap[category] || category;
+  }
+
+  getLightingMoodName(mood: string | undefined): string {
+    if (!mood) return '未识别';
+    const nameMap: { [key: string]: string } = {
+      'dramatic': '戏剧性', 'romantic': '浪漫', 'energetic': '活力',
+      'calm': '平静', 'mysterious': '神秘', 'cheerful': '愉悦', 'professional': '专业'
+    };
+    return nameMap[mood] || mood;
+  }
+
+  getColorHarmonyName(harmony: string | undefined): string {
+    if (!harmony) return '未知';
+    const nameMap: { [key: string]: string } = {
+      'monochromatic': '单色调和', 'analogous': '类似色调和', 'complementary': '互补色调和',
+      'triadic': '三角色调和', 'tetradic': '四角色调和', 'split-complementary': '分裂互补色调和'
+    };
+    return nameMap[harmony] || harmony;
+  }
+
+  getTemperatureName(temp: string | undefined): string {
+    if (!temp) return '未知';
+    const nameMap: { [key: string]: string } = {
+      'warm': '暖色调', 'neutral': '中性色调', 'cool': '冷色调'
+    };
+    return nameMap[temp] || temp;
+  }
+
+  getLayoutTypeName(type: string | undefined): string {
+    if (!type) return '未知';
+    const nameMap: { [key: string]: string } = {
+      'open': '开放式', 'enclosed': '封闭式', 'semi-open': '半开放式', 'multi-level': '多层次'
+    };
+    return nameMap[type] || type;
+  }
+
+  getFlowTypeName(flow: string | undefined): string {
+    if (!flow) return '未知';
+    const nameMap: { [key: string]: string } = {
+      'linear': '线性流线', 'circular': '环形流线', 'grid': '网格流线', 'organic': '有机流线'
+    };
+    return nameMap[flow] || flow;
+  }
 }

+ 30 - 8
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.html

@@ -303,6 +303,13 @@
         <!-- 测试步骤进度 -->
         <div class="test-progress">
           <h3>需求映射进度</h3>
+          <div class="progress-summary">
+            @if (uploadedFiles.length > 0) {
+              <span class="progress-badge">
+                📸 已同步 {{ uploadedFiles.length }} 张参考图片
+              </span>
+            }
+          </div>
           <div class="steps-container">
             @for (step of testSteps; track step.id) {
               <div class="step-item" [class]="getStepClass(step.status)">
@@ -340,7 +347,10 @@
               </div>
             } @else {
               <div class="uploaded-files">
-                <h4>已上传文件 ({{ uploadedFiles.length }})</h4>
+                <h4>已上传文件 ({{ uploadedFiles.length }} 张图片)</h4>
+                <div class="upload-source-info">
+                  <span class="info-tag">✨ 来自素材分析的参考图片</span>
+                </div>
                 <div class="files-grid">
                   @for (file of uploadedFiles; track file.id) {
                     <div class="file-item">
@@ -349,6 +359,12 @@
                         <div class="file-name">{{ file.name }}</div>
                         <div class="file-size">{{ (file.size! / 1024 / 1024).toFixed(2) }} MB</div>
                       </div>
+                      <button class="remove-file-btn" (click)="removeUploadedFile(file.id)" title="移除">
+                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                          <line x1="18" y1="6" x2="6" y2="18"></line>
+                          <line x1="6" y1="6" x2="18" y2="18"></line>
+                        </svg>
+                      </button>
                     </div>
                   }
                 </div>
@@ -373,7 +389,13 @@
               <div class="loading-spinner"></div>
               <div class="loading-text">
                 <h4>正在分析图片...</h4>
-                <p>系统正在进行色彩、纹理、形态、图案和灯光分析</p>
+                <p>
+                  @if (uploadedFiles.length > 1) {
+                    正在分析 {{ uploadedFiles.length }} 张图片,系统将合并分析结果
+                  } @else {
+                    系统正在进行色彩、纹理、形态、图案和灯光分析
+                  }
+                </p>
               </div>
             </div>
           } @else if (analysisError) {
@@ -395,15 +417,15 @@
                 </div>
                 <div class="summary-item">
                   <span class="label">材质类型:</span>
-                  <span class="value">{{ analysisResult.textureAnalysis?.materialClassification?.primary || '未识别' }}</span>
+                  <span class="value">{{ getMaterialName(analysisResult.textureAnalysis?.materialClassification?.primaryMaterial?.category) || '未识别' }}</span>
                 </div>
                 <div class="summary-item">
                   <span class="label">灯光情绪:</span>
-                  <span class="value">{{ analysisResult.lightingAnalysis?.ambientAnalysis?.lightingMood || '未识别' }}</span>
+                  <span class="value">{{ getLightingMoodName(analysisResult.lightingAnalysis?.ambientAnalysis?.lightingMood?.primary) || '未识别' }}</span>
                 </div>
                 <div class="summary-item">
                   <span class="label">空间形态:</span>
-                  <span class="value">{{ analysisResult.formAnalysis?.spaceAnalysis?.spaceType || '未识别' }}</span>
+                  <span class="value">{{ analysisResult.formAnalysis?.geometryAnalysis?.complexity ? getComplexityName(analysisResult.formAnalysis.geometryAnalysis.complexity) : '未识别' }}</span>
                 </div>
               </div>
             </div>
@@ -476,7 +498,7 @@
                       </div>
                       <div class="param-item">
                         <span class="label">色彩和谐:</span>
-                        <span class="value">{{ requirementMapping.parameterMapping.colorParams.colorHarmony }}</span>
+                        <span class="value">{{ getColorHarmonyName(requirementMapping.parameterMapping.colorParams.colorHarmony) }}</span>
                       </div>
                       <div class="param-item">
                         <span class="label">饱和度:</span>
@@ -495,11 +517,11 @@
                     <div class="space-params">
                       <div class="param-item">
                         <span class="label">布局类型:</span>
-                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout.type }}</span>
+                        <span class="value">{{ getLayoutTypeName(requirementMapping.parameterMapping.spaceParams.layout.type) }}</span>
                       </div>
                       <div class="param-item">
                         <span class="label">空间流线:</span>
-                        <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout.flow }}</span>
+                        <span class="value">{{ getFlowTypeName(requirementMapping.parameterMapping.spaceParams.layout.flow) }}</span>
                       </div>
                       <div class="param-item">
                         <span class="label">家具比例:</span>

+ 79 - 2
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.scss

@@ -1928,11 +1928,27 @@
   border: 1px solid #e9ecef;
 
   h3 {
-    margin: 0 0 16px 0;
+    margin: 0 0 8px 0;
     font-size: 18px;
     color: #495057;
   }
 
+  .progress-summary {
+    margin-bottom: 16px;
+    
+    .progress-badge {
+      display: inline-block;
+      padding: 6px 16px;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      color: white;
+      border-radius: 16px;
+      font-size: 13px;
+      font-weight: 600;
+      box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+      animation: badgePulse 2s ease-in-out infinite;
+    }
+  }
+
   .steps-container {
     display: grid;
     grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
@@ -2099,11 +2115,25 @@
 
   .uploaded-files {
     h4 {
-      margin: 0 0 16px 0;
+      margin: 0 0 8px 0;
       font-size: 16px;
       color: #495057;
     }
 
+    .upload-source-info {
+      margin-bottom: 12px;
+      
+      .info-tag {
+        display: inline-block;
+        padding: 4px 12px;
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        color: white;
+        border-radius: 12px;
+        font-size: 12px;
+        font-weight: 500;
+      }
+    }
+
     .files-grid {
       display: grid;
       grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
@@ -2111,16 +2141,52 @@
     }
 
     .file-item {
+      position: relative;
       border: 1px solid #e9ecef;
       border-radius: 6px;
       overflow: hidden;
       background: white;
+      transition: all 0.2s ease;
+      
+      &:hover {
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+        
+        .remove-file-btn {
+          opacity: 1;
+        }
+      }
 
       .file-preview {
         width: 100%;
         height: 120px;
         object-fit: cover;
       }
+      
+      .remove-file-btn {
+        position: absolute;
+        top: 4px;
+        right: 4px;
+        width: 24px;
+        height: 24px;
+        border: none;
+        border-radius: 50%;
+        background: rgba(220, 53, 69, 0.9);
+        color: white;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        opacity: 0;
+        transition: opacity 0.2s ease;
+        
+        &:hover {
+          background: rgba(220, 53, 69, 1);
+        }
+        
+        svg {
+          pointer-events: none;
+        }
+      }
 
       .file-info {
         padding: 8px;
@@ -2452,4 +2518,15 @@
 @keyframes pulse {
   0%, 100% { opacity: 1; }
   50% { opacity: 0.5; }
+}
+
+@keyframes badgePulse {
+  0%, 100% {
+    box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+    transform: scale(1);
+  }
+  50% {
+    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.5);
+    transform: scale(1.02);
+  }
 }

+ 364 - 31
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.ts

@@ -247,6 +247,14 @@ export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
   @Output() progressUpdated = new EventEmitter<number>();
   @Output() stageCompleted = new EventEmitter<{ stage: string; allStagesCompleted: boolean }>();
   @Output() dataUpdated = new EventEmitter<any>(); // 新增:实时数据更新事件
+  @Output() mappingDataUpdated = new EventEmitter<{
+    uploadedFiles: any[];
+    analysisResult: any;
+    requirementMapping: any;
+    testSteps: any[];
+    isAnalyzing: boolean;
+    isGeneratingMapping: boolean;
+  }>(); // 新增:需求映射数据更新事件
 
   // 表单
   materialForm!: FormGroup;
@@ -569,16 +577,41 @@ export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
 
       // 显示上传成功弹窗
       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'
-      }];
+      
+      // 如果是图片类型,添加到需求映射界面的uploadedFiles数组(支持多图片)
+      if (type === 'image') {
+        // 检查是否已存在
+        const existingIndex = this.uploadedFiles.findIndex(f => f.id === materialFile.id);
+        if (existingIndex === -1) {
+          const uploadedFile = {
+            id: materialFile.id,
+            name: file.name,
+            url: materialFile.url || '',
+            size: file.size,
+            type: 'image' as const,
+            preview: materialFile.url || ''
+          };
+          this.uploadedFiles.push(uploadedFile);
+          
+          console.log('✅ 图片已同步到需求映射,当前共', this.uploadedFiles.length, '张');
+          
+          // 🔥 新增:立即触发需求映射界面的实时分析
+          this.triggerRealtimeAnalysis(uploadedFile);
+        }
+      } else {
+        // 非图片类型,保持原有逻辑
+        this.uploadedFiles = [{
+          id: materialFile.id,
+          name: file.name,
+          url: materialFile.url || '',
+          size: file.size,
+          type: type === 'text' ? 'text' : type === 'cad' ? 'cad' : 'image'
+        }];
+      }
+      
       this.uploadType = type === 'text' ? 'document' : type === 'image' ? 'image' : 'mixed';
       
-      // 自动解析
+      // 自动解析(素材分析用)
       this.analyzeMaterial(materialFile);
     }, 1000);
   }
@@ -1925,7 +1958,7 @@ export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
     }
   }
 
-  // 开始分析
+  // 开始分析 - 支持多图片分析
   startAnalysis(): void {
     if (this.uploadedFiles.length === 0) return;
 
@@ -1933,48 +1966,157 @@ export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
     this.isAnalyzing = true;
     this.analysisError = null;
 
-    // 使用第一个文件进行分析
-    const firstFile = this.uploadedFiles[0];
+    // 过滤出所有图片文件
+    const imageFiles = this.uploadedFiles.filter(f => f.type === 'image');
     
-    // 添加null检查和错误处理
-    if (!firstFile) {
-      this.analysisError = '未找到有效的文件';
+    if (imageFiles.length === 0) {
+      this.analysisError = '未找到有效的图片文件';
       this.isAnalyzing = false;
       this.updateStepStatus('analysis', 'error');
       return;
     }
 
     try {
-      const analysisSubscription = this.colorAnalysisService.analyzeImage(firstFile).subscribe({
+      // 如果只有一张图片,使用单图分析
+      if (imageFiles.length === 1) {
+        const analysisSubscription = this.colorAnalysisService.analyzeImage(imageFiles[0]).subscribe({
+          next: (result: ColorAnalysisResult) => {
+            if (result) {
+              this.analysisResult = result;
+              this.isAnalyzing = false;
+              this.updateStepStatus('analysis', 'completed');
+              
+              // 自动开始需求映射
+              this.startRequirementMapping();
+            } else {
+              this.analysisError = '分析结果为空';
+              this.isAnalyzing = false;
+              this.updateStepStatus('analysis', 'error');
+            }
+          },
+          error: (error: any) => {
+            console.error('分析失败:', error);
+            this.analysisError = '图片分析失败,请重试';
+            this.isAnalyzing = false;
+            this.updateStepStatus('analysis', 'error');
+          }
+        });
+        this.subscriptions.push(analysisSubscription);
+      } else {
+        // 多图片分析:分析所有图片并合并结果
+        this.analyzeBatchImages(imageFiles);
+      }
+    } catch (error) {
+      console.error('启动分析失败:', error);
+      this.analysisError = '启动分析失败,请重试';
+      this.isAnalyzing = false;
+      this.updateStepStatus('analysis', 'error');
+    }
+  }
+
+  // 批量图片分析
+  private analyzeBatchImages(imageFiles: any[]): void {
+    console.log(`开始批量分析 ${imageFiles.length} 张图片...`);
+    
+    let completedCount = 0;
+    const allResults: ColorAnalysisResult[] = [];
+    
+    imageFiles.forEach((file, index) => {
+      const analysisSubscription = this.colorAnalysisService.analyzeImage(file).subscribe({
         next: (result: ColorAnalysisResult) => {
-          if (result) {
-            this.analysisResult = result;
+          allResults.push(result);
+          completedCount++;
+          
+          console.log(`图片 ${index + 1}/${imageFiles.length} 分析完成`);
+          
+          // 所有图片分析完成后,合并结果
+          if (completedCount === imageFiles.length) {
+            this.analysisResult = this.mergeAnalysisResults(allResults);
             this.isAnalyzing = false;
             this.updateStepStatus('analysis', 'completed');
             
+            console.log('批量分析完成,合并结果:', this.analysisResult);
+            
             // 自动开始需求映射
             this.startRequirementMapping();
-          } else {
-            this.analysisError = '分析结果为空';
-            this.isAnalyzing = false;
-            this.updateStepStatus('analysis', 'error');
           }
         },
         error: (error: any) => {
-          console.error('分析失败:', error);
-          this.analysisError = '图片分析失败,请重试';
-          this.isAnalyzing = false;
-          this.updateStepStatus('analysis', 'error');
+          console.error(`图片 ${index + 1} 分析失败:`, error);
+          completedCount++;
+          
+          // 即使部分失败,也尝试合并已成功的结果
+          if (completedCount === imageFiles.length) {
+            if (allResults.length > 0) {
+              this.analysisResult = this.mergeAnalysisResults(allResults);
+              this.isAnalyzing = false;
+              this.updateStepStatus('analysis', 'completed');
+              this.startRequirementMapping();
+            } else {
+              this.analysisError = '所有图片分析失败,请重试';
+              this.isAnalyzing = false;
+              this.updateStepStatus('analysis', 'error');
+            }
+          }
         }
       });
-
+      
       this.subscriptions.push(analysisSubscription);
-    } catch (error) {
-      console.error('启动分析失败:', error);
-      this.analysisError = '启动分析失败,请重试';
-      this.isAnalyzing = false;
-      this.updateStepStatus('analysis', 'error');
+    });
+  }
+
+  // 合并多个分析结果
+  private mergeAnalysisResults(results: ColorAnalysisResult[]): ColorAnalysisResult {
+    if (results.length === 0) {
+      throw new Error('没有有效的分析结果');
+    }
+    
+    if (results.length === 1) {
+      return results[0];
     }
+    
+    console.log('合并多个分析结果:', results.length);
+    
+    // 合并颜色信息
+    const allColors: any[] = [];
+    results.forEach(result => {
+      result.colors.forEach(color => {
+        const existing = allColors.find(c => c.hex === color.hex);
+        if (existing) {
+          existing.percentage += color.percentage / results.length;
+        } else {
+          allColors.push({
+            ...color,
+            percentage: color.percentage / results.length
+          });
+        }
+      });
+    });
+    
+    // 按百分比排序并取前7种颜色
+    allColors.sort((a, b) => b.percentage - a.percentage);
+    const mergedColors = allColors.slice(0, 7);
+    
+    // 重新计算百分比使总和为100%
+    const totalPercentage = mergedColors.reduce((sum, c) => sum + c.percentage, 0);
+    mergedColors.forEach(c => {
+      c.percentage = (c.percentage / totalPercentage) * 100;
+    });
+    
+    // 合并增强分析数据(取平均值或第一个)
+    const baseResult = results[0];
+    
+    return {
+      colors: mergedColors,
+      originalImage: results[0].originalImage,
+      mosaicImage: results[0].mosaicImage,
+      reportPath: results[0].reportPath,
+      enhancedAnalysis: baseResult.enhancedAnalysis,
+      formAnalysis: baseResult.formAnalysis,
+      textureAnalysis: baseResult.textureAnalysis,
+      patternAnalysis: baseResult.patternAnalysis,
+      lightingAnalysis: baseResult.lightingAnalysis
+    };
   }
 
   // 开始需求映射
@@ -2002,6 +2144,9 @@ export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
             
             console.log('=== 需求映射测试完成 ===');
             console.log('映射结果:', this.requirementMapping);
+            
+            // 发送映射数据更新到父组件
+            this.emitMappingDataUpdate();
           } else {
             this.mappingError = '需求映射结果为空';
             this.isGeneratingMapping = false;
@@ -2086,4 +2231,192 @@ export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
     link.click();
     URL.revokeObjectURL(url);
   }
+
+  // 辅助方法:获取材质名称的中文翻译
+  getMaterialName(category: string | undefined): string {
+    if (!category) return '未识别';
+    const nameMap: { [key: string]: string } = {
+      'wood': '木材',
+      'metal': '金属',
+      'fabric': '织物',
+      'leather': '皮革',
+      'plastic': '塑料',
+      'glass': '玻璃',
+      'ceramic': '陶瓷',
+      'stone': '石材',
+      'composite': '复合材料'
+    };
+    return nameMap[category] || category;
+  }
+
+  // 获取灯光情绪名称的中文翻译
+  getLightingMoodName(mood: string | undefined): string {
+    if (!mood) return '未识别';
+    const nameMap: { [key: string]: string } = {
+      'dramatic': '戏剧性',
+      'romantic': '浪漫',
+      'energetic': '活力',
+      'calm': '平静',
+      'mysterious': '神秘',
+      'cheerful': '愉悦',
+      'professional': '专业'
+    };
+    return nameMap[mood] || mood;
+  }
+
+  // 获取复杂度名称的中文翻译
+  getComplexityName(level: string): string {
+    const nameMap: { [key: string]: string } = {
+      'simple': '简单',
+      'moderate': '中等',
+      'complex': '复杂',
+      'very-complex': '非常复杂'
+    };
+    return nameMap[level] || level;
+  }
+
+  // 获取色彩和谐类型的中文翻译
+  getColorHarmonyName(harmony: string): string {
+    const nameMap: { [key: string]: string } = {
+      'monochromatic': '单色调和',
+      'analogous': '类似色调和',
+      'complementary': '互补色调和',
+      'triadic': '三角色调和',
+      'tetradic': '四角色调和',
+      'split-complementary': '分裂互补色调和'
+    };
+    return nameMap[harmony] || harmony;
+  }
+
+  // 获取空间布局类型的中文翻译
+  getLayoutTypeName(type: string): string {
+    const nameMap: { [key: string]: string } = {
+      'open': '开放式',
+      'enclosed': '封闭式',
+      'semi-open': '半开放式',
+      'multi-level': '多层次'
+    };
+    return nameMap[type] || type;
+  }
+
+  // 获取空间流线类型的中文翻译
+  getFlowTypeName(flow: string): string {
+    const nameMap: { [key: string]: string } = {
+      'linear': '线性流线',
+      'circular': '环形流线',
+      'grid': '网格流线',
+      'organic': '有机流线'
+    };
+    return nameMap[flow] || flow;
+  }
+
+  // 移除需求映射界面中已上传的文件
+  removeUploadedFile(fileId: string): void {
+    const index = this.uploadedFiles.findIndex(f => f.id === fileId);
+    if (index !== -1) {
+      const file = this.uploadedFiles[index];
+      // 清理对象URL
+      if (file.url && file.url.startsWith('blob:')) {
+        URL.revokeObjectURL(file.url);
+      }
+      if (file.preview && file.preview.startsWith('blob:')) {
+        URL.revokeObjectURL(file.preview);
+      }
+      
+      this.uploadedFiles.splice(index, 1);
+      
+      // 如果没有文件了,重置分析状态
+      if (this.uploadedFiles.length === 0) {
+        this.analysisResult = undefined;
+        this.requirementMapping = null;
+        this.updateStepStatus('upload', 'pending');
+        this.updateStepStatus('analysis', 'pending');
+        this.updateStepStatus('mapping', 'pending');
+        this.updateStepStatus('preview', 'pending');
+      } else {
+        // 还有其他图片,重新分析剩余图片
+        this.restartAnalysisWithRemainingImages();
+      }
+    }
+  }
+
+  // 实时触发分析(每上传一张图片就分析一张)
+  private triggerRealtimeAnalysis(newFile: any): void {
+    console.log('🔄 触发实时分析:', newFile.name);
+    
+    // 第一张图片:初始化分析状态
+    if (this.uploadedFiles.length === 1) {
+      this.updateStepStatus('upload', 'completed');
+      this.updateStepStatus('analysis', 'in-progress');
+      this.isAnalyzing = true;
+    }
+    
+    // 分析新上传的图片
+    this.colorAnalysisService.analyzeImage(newFile).subscribe({
+      next: (newResult: ColorAnalysisResult) => {
+        console.log('📊 新图片分析完成:', newFile.name);
+        
+        // 如果是第一张图片,直接设置结果
+        if (!this.analysisResult) {
+          this.analysisResult = newResult;
+          console.log('✅ 设置第一张图片的分析结果');
+        } else {
+          // 如果已有结果,合并新结果
+          this.analysisResult = this.mergeAnalysisResults([this.analysisResult, newResult]);
+          console.log('🔗 合并新图片的分析结果,当前共', this.uploadedFiles.length, '张');
+        }
+        
+        // 检查是否所有图片都已分析完成
+        // 这里简化处理:每次上传都会触发分析,所以当前图片数量 = 已分析数量
+        this.isAnalyzing = false;
+        this.updateStepStatus('analysis', 'completed');
+        
+        // 🔥 自动触发需求映射生成
+        this.startRequirementMapping();
+        
+        // 发送映射数据更新事件给父组件
+        this.emitMappingDataUpdate();
+        
+        this.cdr.detectChanges(); // 触发界面更新
+      },
+      error: (error: any) => {
+        console.error('实时分析失败:', error);
+        // 不设置为error状态,因为可能还有其他图片在分析
+        this.isAnalyzing = false;
+      }
+    });
+  }
+
+  // 重新分析剩余图片
+  private restartAnalysisWithRemainingImages(): void {
+    console.log('🔄 重新分析剩余图片,共', this.uploadedFiles.length, '张');
+    
+    this.updateStepStatus('analysis', 'in-progress');
+    this.isAnalyzing = true;
+    this.analysisResult = undefined;
+    
+    // 重新分析所有剩余图片
+    const imageFiles = this.uploadedFiles.filter(f => f.type === 'image');
+    
+    if (imageFiles.length === 0) {
+      this.isAnalyzing = false;
+      this.updateStepStatus('analysis', 'pending');
+      return;
+    }
+    
+    // 使用批量分析
+    this.analyzeBatchImages(imageFiles);
+  }
+
+  // 发送映射数据更新到父组件
+  private emitMappingDataUpdate(): void {
+    this.mappingDataUpdated.emit({
+      uploadedFiles: this.uploadedFiles,
+      analysisResult: this.analysisResult,
+      requirementMapping: this.requirementMapping,
+      testSteps: this.testSteps,
+      isAnalyzing: this.isAnalyzing,
+      isGeneratingMapping: this.isGeneratingMapping
+    });
+  }
 }

+ 34 - 6
src/app/shared/components/upload-success-modal/upload-success-modal.component.html

@@ -450,11 +450,22 @@
                                       <div class="color-mapping-item">
                                         <div class="color-swatch" [style.background-color]="mapping.originalColor"></div>
                                         <span class="mapping-arrow">→</span>
-                                        <span class="target-material">{{ mapping.mappedColor }}</span>
-                                        <span class="confidence">({{ mapping.weight }}%)</span>
+                                        <div class="color-swatch" [style.background-color]="mapping.mappedColor"></div>
+                                        <span class="usage-tag">{{ getColorUsageName(mapping.usage) }}</span>
+                                        <span class="confidence">(权重 {{ mapping.weight }}%)</span>
                                       </div>
                                     }
                                   </div>
+                                  <div class="color-params-info">
+                                    <div class="param-item">
+                                      <span class="label">色彩和谐:</span>
+                                      <span class="value">{{ getColorHarmonyName(requirementMapping.parameterMapping.colorParams.colorHarmony) }}</span>
+                                    </div>
+                                    <div class="param-item">
+                                      <span class="label">色温:</span>
+                                      <span class="value">{{ getTemperatureName(requirementMapping.parameterMapping.colorParams.temperature) }}</span>
+                                    </div>
+                                  </div>
                                 </div>
 
                                 <!-- 空间参数 -->
@@ -463,11 +474,24 @@
                                   <div class="space-info">
                                     <div class="info-item">
                                       <span class="label">空间类型:</span>
-                                      <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout?.type }}</span>
+                                      <span class="value">{{ getLayoutTypeName(requirementMapping.parameterMapping.spaceParams.layout.type) }}</span>
+                                    </div>
+                                    <div class="info-item">
+                                      <span class="label">空间流线:</span>
+                                      <span class="value">{{ getFlowTypeName(requirementMapping.parameterMapping.spaceParams.layout.flow) }}</span>
                                     </div>
                                     <div class="info-item">
                                       <span class="label">开放程度:</span>
-                                      <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale?.openness }}%</span>
+                                      <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.openness }}%</span>
+                                    </div>
+                                    <div class="info-item">
+                                      <span class="label">尺寸:</span>
+                                      <span class="value">
+                                        {{ requirementMapping.parameterMapping.spaceParams.dimensions.width }} × 
+                                        {{ requirementMapping.parameterMapping.spaceParams.dimensions.height }} × 
+                                        {{ requirementMapping.parameterMapping.spaceParams.dimensions.depth }} 
+                                        {{ getDimensionUnitName(requirementMapping.parameterMapping.spaceParams.dimensions.unit) }}
+                                      </span>
                                     </div>
                                   </div>
                                 </div>
@@ -478,10 +502,14 @@
                                   <div class="material-mappings">
                                     @for (mapping of requirementMapping.parameterMapping.materialParams.surfaceMaterials; track mapping.category) {
                                       <div class="material-mapping-item">
-                                        <span class="source-texture">{{ mapping.category }}</span>
+                                        <span class="source-texture">{{ getMaterialName(mapping.category) }}</span>
                                         <span class="mapping-arrow">→</span>
                                         <span class="target-material">{{ mapping.subtype }}</span>
-                                        <span class="properties">{{ mapping.finish }}, {{ mapping.priority }}</span>
+                                        <span class="properties">
+                                          {{ getFinishName(mapping.finish) }}, 
+                                          {{ getPriorityName(mapping.priority) }}
+                                        </span>
+                                        <span class="coverage">({{ mapping.coverage }}%)</span>
                                       </div>
                                     }
                                   </div>

+ 88 - 0
src/app/shared/components/upload-success-modal/upload-success-modal.component.ts

@@ -1311,4 +1311,92 @@ interface RequirementMapping {
     };
     return nameMap[mood] || mood;
   }
+
+  // 获取颜色用途名称的中文翻译
+  getColorUsageName(usage: string): string {
+    const nameMap: { [key: string]: string } = {
+      'primary': '主色',
+      'secondary': '辅色',
+      'accent': '点缀色',
+      'background': '背景色'
+    };
+    return nameMap[usage] || usage;
+  }
+
+  // 获取色彩和谐类型的中文翻译
+  getColorHarmonyName(harmony: string): string {
+    const nameMap: { [key: string]: string } = {
+      'monochromatic': '单色调和',
+      'analogous': '类似色调和',
+      'complementary': '互补色调和',
+      'triadic': '三角色调和',
+      'tetradic': '四角色调和',
+      'split-complementary': '分裂互补色调和'
+    };
+    return nameMap[harmony] || harmony;
+  }
+
+  // 获取色温名称的中文翻译
+  getTemperatureName(temp: string): string {
+    const nameMap: { [key: string]: string } = {
+      'warm': '暖色调',
+      'neutral': '中性色调',
+      'cool': '冷色调'
+    };
+    return nameMap[temp] || temp;
+  }
+
+  // 获取空间布局类型的中文翻译
+  getLayoutTypeName(type: string): string {
+    const nameMap: { [key: string]: string } = {
+      'open': '开放式',
+      'enclosed': '封闭式',
+      'semi-open': '半开放式',
+      'multi-level': '多层次'
+    };
+    return nameMap[type] || type;
+  }
+
+  // 获取空间流线类型的中文翻译
+  getFlowTypeName(flow: string): string {
+    const nameMap: { [key: string]: string } = {
+      'linear': '线性流线',
+      'circular': '环形流线',
+      'grid': '网格流线',
+      'organic': '有机流线'
+    };
+    return nameMap[flow] || flow;
+  }
+
+  // 获取尺寸单位的中文翻译
+  getDimensionUnitName(unit: string): string {
+    const nameMap: { [key: string]: string } = {
+      'meter': '米',
+      'feet': '英尺'
+    };
+    return nameMap[unit] || unit;
+  }
+
+  // 获取表面处理名称的中文翻译
+  getFinishName(finish: string): string {
+    const nameMap: { [key: string]: string } = {
+      'matte': '哑光',
+      'satin': '缎面',
+      'gloss': '光面',
+      'textured': '纹理',
+      'brushed': '拉丝',
+      'polished': '抛光'
+    };
+    return nameMap[finish] || finish;
+  }
+
+  // 获取优先级名称的中文翻译
+  getPriorityName(priority: string): string {
+    const nameMap: { [key: string]: string } = {
+      'primary': '主要',
+      'secondary': '次要',
+      'accent': '点缀'
+    };
+    return nameMap[priority] || priority;
+  }
 }

+ 46 - 26
src/app/shared/services/color-analysis.service.ts

@@ -167,34 +167,54 @@ export class ColorAnalysisService {
     
     // 创建一个File对象用于模拟分析
     return new Observable(observer => {
-      // 如果有预览URL,尝试获取文件
-      if (file.preview || file.url) {
-        fetch(file.preview || file.url)
-          .then(response => response.blob())
-          .then(blob => {
-            const mockFile = new File([blob], file.name, { type: file.type || 'image/jpeg' });
-            this.simulateAnalysis(mockFile).subscribe({
-              next: (result) => observer.next(result),
-              error: (error) => observer.error(error),
-              complete: () => observer.complete()
-            });
-          })
-          .catch(error => {
-            console.warn('无法获取文件内容,使用默认模拟数据:', error);
-            // 如果无法获取文件,直接返回模拟结果
-            this.getDefaultMockResult(file).subscribe({
-              next: (result) => observer.next(result),
-              error: (error) => observer.error(error),
-              complete: () => observer.complete()
+      try {
+        // 如果有预览URL,尝试获取文件
+        if (file.preview || file.url) {
+          fetch(file.preview || file.url)
+            .then(response => response.blob())
+            .then(blob => {
+              const mockFile = new File([blob], file.name, { type: file.type || 'image/jpeg' });
+              this.simulateAnalysis(mockFile).subscribe({
+                next: (result) => {
+                  observer.next(result);
+                  observer.complete();
+                },
+                error: (error) => {
+                  console.error('模拟分析失败:', error);
+                  observer.error(error);
+                }
+              });
+            })
+            .catch(error => {
+              console.warn('无法获取文件内容,使用默认模拟数据:', error);
+              // 如果无法获取文件,直接返回模拟结果
+              this.getDefaultMockResult(file).subscribe({
+                next: (result) => {
+                  observer.next(result);
+                  observer.complete();
+                },
+                error: (error) => {
+                  console.error('获取默认模拟数据失败:', error);
+                  observer.error(error);
+                }
+              });
             });
+        } else {
+          // 没有文件URL,直接返回模拟结果
+          this.getDefaultMockResult(file).subscribe({
+            next: (result) => {
+              observer.next(result);
+              observer.complete();
+            },
+            error: (error) => {
+              console.error('获取模拟结果失败:', error);
+              observer.error(error);
+            }
           });
-      } else {
-        // 没有文件URL,直接返回模拟结果
-        this.getDefaultMockResult(file).subscribe({
-          next: (result) => observer.next(result),
-          error: (error) => observer.error(error),
-          complete: () => observer.complete()
-        });
+        }
+      } catch (error) {
+        console.error('analyzeImage方法异常:', error);
+        observer.error(error);
       }
     });
   }