Browse Source

feat(project-review): 添加项目复盘模块及样式,优化用户体验

新增项目复盘功能,包括:
1. 项目复盘数据展示与分析,支持SOP执行数据、经验复盘和优化建议的Tab切换。
2. 设计复盘模块的样式,提升界面美观性和可用性。
3. 添加经验复盘数据结构,展示客户需求、顾虑、投诉点及项目亮点。
4. 实现优化建议的统计与展示,提供具体可操作的改进方案。

同时,更新相关样式文件以支持新功能的视觉效果。
徐福静0235668 4 days ago
parent
commit
d028b2ed89

+ 0 - 0
src/app/pages/designer/project-detail/PROJECT-REVIEW-README.md


+ 479 - 151
src/app/pages/designer/project-detail/project-detail.html

@@ -662,6 +662,7 @@
               
               <!-- 新增:客户信息右侧 2x2 售后模块布局 -->
               @if (expandedSection === 'aftercare') {
+              <!-- 上方 2x2 网格:前4个模块 -->
               <div class="aftercare-grid">
                 
                 <!-- 1/4:尾款结算 -->
@@ -847,187 +848,514 @@
                     }
                   </div>
                 </div>
-
-                <!-- 5/5:项目复盘 -->
-                <div class="aftercare-module review-module card">
-                  <div class="module-header">
-                    <h2>📊 项目复盘</h2>
-                    <p class="module-description">基于SOP执行数据和经验总结,自动生成复盘报告</p>
+              </div>
+              
+              <!-- 下方横向展示:项目复盘模块 -->
+              <div class="project-review-section">
+                <div class="review-section-header">
+                  <div class="header-left">
+                    <h2 class="section-title">📊 项目复盘</h2>
+                    <p class="section-subtitle">基于SOP执行数据和经验总结,自动生成复盘报告</p>
                   </div>
-                  <div class="review-management-enhanced">
-                    <div class="review-features">
-                      <div class="feature-card">
-                        <div class="feature-icon">📈</div>
-                        <div class="feature-content">
-                          <h4>SOP执行分析</h4>
-                          <p>自动分析各阶段SOP执行情况和时效</p>
-                        </div>
-                      </div>
-                      <div class="feature-card">
-                        <div class="feature-icon">💡</div>
-                        <div class="feature-content">
-                          <h4>经验总结</h4>
-                          <p>提取项目亮点和改进建议</p>
+                  <div class="header-right">
+                    @if (canEditSection('aftercare')) {
+                      <button class="generate-review-btn" (click)="generateReviewReport()" [disabled]="isGeneratingReview">
+                        @if (isGeneratingReview) {
+                          <span class="loading-spinner"></span>
+                          生成中...
+                        } @else {
+                          📊 生成复盘报告
+                        }
+                      </button>
+                      @if (projectReview) {
+                        <button class="export-review-btn" (click)="exportReviewReport()">
+                          📤 导出报告
+                        </button>
+                      }
+                    }
+                  </div>
+                </div>
+
+                <!-- Tab切换导航 -->
+                <div class="review-tabs">
+                  <button 
+                    class="review-tab" 
+                    [class.active]="activeReviewTab === 'sop'"
+                    (click)="activeReviewTab = 'sop'">
+                    <span class="tab-icon">📈</span>
+                    <span class="tab-label">SOP执行数据</span>
+                  </button>
+                  <button 
+                    class="review-tab" 
+                    [class.active]="activeReviewTab === 'experience'"
+                    (click)="activeReviewTab = 'experience'">
+                    <span class="tab-icon">💡</span>
+                    <span class="tab-label">经验复盘</span>
+                  </button>
+                  <button 
+                    class="review-tab" 
+                    [class.active]="activeReviewTab === 'suggestions'"
+                    (click)="activeReviewTab = 'suggestions'">
+                    <span class="tab-icon">🔧</span>
+                    <span class="tab-label">优化建议</span>
+                  </button>
+                </div>
+
+                <!-- Tab内容区域 -->
+                <div class="review-content-area">
+                  <!-- SOP执行数据 -->
+                  @if (activeReviewTab === 'sop') {
+                    <div class="sop-data-content">
+                      <div class="sop-metrics-grid">
+                        <!-- 关键指标卡片 -->
+                        <div class="metric-card">
+                          <div class="metric-header">
+                            <span class="metric-icon">💬</span>
+                            <h4 class="metric-title">需求沟通次数</h4>
+                          </div>
+                          <div class="metric-value-large">{{ sopMetrics?.communicationCount || 0 }}</div>
+                          <div class="metric-footer">
+                            <span class="metric-label">平均水平:</span>
+                            <span class="metric-benchmark">{{ sopMetrics?.avgCommunication || 5 }}次</span>
+                            <span class="metric-status" [class.good]="(sopMetrics?.communicationCount || 0) <= (sopMetrics?.avgCommunication || 5)" [class.warning]="(sopMetrics?.communicationCount || 0) > (sopMetrics?.avgCommunication || 5)">
+                              {{ (sopMetrics?.communicationCount || 0) <= (sopMetrics?.avgCommunication || 5) ? '✓ 良好' : '⚠ 超标' }}
+                            </span>
+                          </div>
                         </div>
-                      </div>
-                      <div class="feature-card">
-                        <div class="feature-icon">📋</div>
-                        <div class="feature-content">
-                          <h4>智能报告</h4>
-                          <p>自动生成结构化复盘报告</p>
+
+                        <div class="metric-card">
+                          <div class="metric-header">
+                            <span class="metric-icon">🔄</span>
+                            <h4 class="metric-title">改图次数</h4>
+                          </div>
+                          <div class="metric-value-large">{{ sopMetrics?.revisionCount || 0 }}</div>
+                          <div class="metric-footer">
+                            <span class="metric-label">平均水平:</span>
+                            <span class="metric-benchmark">{{ sopMetrics?.avgRevision || 2 }}次</span>
+                            <span class="metric-status" [class.good]="(sopMetrics?.revisionCount || 0) <= (sopMetrics?.avgRevision || 2)" [class.warning]="(sopMetrics?.revisionCount || 0) > (sopMetrics?.avgRevision || 2)">
+                              {{ (sopMetrics?.revisionCount || 0) <= (sopMetrics?.avgRevision || 2) ? '✓ 良好' : '⚠ 超标' }}
+                            </span>
+                          </div>
                         </div>
-                      </div>
-                      <div class="feature-card">
-                        <div class="feature-icon">🔄</div>
-                        <div class="feature-content">
-                          <h4>持续优化</h4>
-                          <p>为后续项目提供参考和改进方向</p>
+
+                        <div class="metric-card">
+                          <div class="metric-header">
+                            <span class="metric-icon">⏱️</span>
+                            <h4 class="metric-title">交付周期</h4>
+                          </div>
+                          <div class="metric-value-large">{{ sopMetrics?.deliveryCycle || 0 }}<span class="unit">天</span></div>
+                          <div class="metric-footer">
+                            <span class="metric-label">标准周期:</span>
+                            <span class="metric-benchmark">{{ sopMetrics?.standardCycle || 15 }}天</span>
+                            <span class="metric-status" [class.good]="(sopMetrics?.deliveryCycle || 0) <= (sopMetrics?.standardCycle || 15)" [class.warning]="(sopMetrics?.deliveryCycle || 0) > (sopMetrics?.standardCycle || 15)">
+                              {{ (sopMetrics?.deliveryCycle || 0) <= (sopMetrics?.standardCycle || 15) ? '✓ 达标' : '⚠ 延期' }}
+                            </span>
+                          </div>
                         </div>
-                      </div>
-                    </div>
-                    
-                    <!-- 复盘状态显示 -->
-                    <div class="review-status-card">
-                      <div class="status-header">
-                        <h4>复盘状态</h4>
-                        <span class="status-badge" [class]="getReviewStatus()">
-                          {{ getReviewStatusText() }}
-                        </span>
-                      </div>
-                      <div class="status-content">
-                        @if (projectReview) {
-                          <div class="review-summary">
-                            <div class="summary-item">
-                              <label>生成时间:</label>
-                              <span>{{ formatDateTime(projectReview.generatedAt) }}</span>
-                            </div>
-                            <div class="summary-item">
-                              <label>项目评分:</label>
-                              <span class="score">{{ projectReview.overallScore }}/100</span>
-                            </div>
-                            <div class="summary-item">
-                              <label>关键亮点:</label>
-                              <span>{{ projectReview.keyHighlights.join('、') || '暂无' }}</span>
+
+                        <div class="metric-card">
+                          <div class="metric-header">
+                            <span class="metric-icon">⭐</span>
+                            <h4 class="metric-title">客户满意度</h4>
+                          </div>
+                          <div class="metric-value-large">{{ sopMetrics?.customerSatisfaction || 0 }}<span class="unit">/5</span></div>
+                          <div class="metric-footer">
+                            <div class="satisfaction-stars">
+                              @for (star of [1,2,3,4,5]; track star) {
+                                <span class="star" [class.filled]="star <= (sopMetrics?.customerSatisfaction || 0)">★</span>
+                              }
                             </div>
                           </div>
-                        } @else {
-                          <p class="no-review-text">项目完成后将自动生成复盘报告</p>
-                        }
+                        </div>
                       </div>
-                    </div>
 
-                    <!-- 复盘报告详情 -->
-                    @if (projectReview) {
-                      <div class="review-report-card">
-                        <div class="report-header">
-                          <h4>📊 复盘报告详情</h4>
-                          <button class="export-btn" (click)="exportReviewReport()">
-                            📤 导出报告
-                          </button>
-                        </div>
-                        <div class="report-content">
-                          <!-- SOP执行分析 -->
-                          <div class="report-section">
-                            <h5>SOP执行分析</h5>
-                            <div class="sop-analysis">
-                              @for (stage of projectReview.sopAnalysis; track stage.stageName) {
-                                <div class="stage-analysis">
-                                  <div class="stage-info">
-                                    <span class="stage-name">{{ stage.stageName }}</span>
-                                    <span class="stage-score" [class]="getScoreClass(stage.score)">
-                                      {{ stage.score }}/100
-                                    </span>
+                      <!-- 阶段执行详情 -->
+                      <div class="sop-stages-section">
+                        <h3 class="subsection-title">各阶段执行详情</h3>
+                        <div class="stages-timeline">
+                          @for (stage of sopStagesData; track stage.name) {
+                            <div class="stage-timeline-item" [class.completed]="stage.status === 'completed'" [class.ongoing]="stage.status === 'ongoing'" [class.delayed]="stage.isDelayed">
+                              <div class="stage-indicator">
+                                <div class="stage-dot"></div>
+                                @if (!$last) {
+                                  <div class="stage-line"></div>
+                                }
+                              </div>
+                              <div class="stage-content-card">
+                                <div class="stage-card-header">
+                                  <h4 class="stage-name">{{ stage.name }}</h4>
+                                  <span class="stage-status-badge" [class]="stage.status">
+                                    {{ stage.statusText }}
+                                  </span>
+                                </div>
+                                <div class="stage-metrics">
+                                  <div class="stage-metric-item">
+                                    <span class="label">计划时长:</span>
+                                    <span class="value">{{ stage.plannedDuration }}天</span>
                                   </div>
-                                  <div class="stage-details">
-                                    <div class="detail-item">
-                                      <label>计划时长:</label>
-                                      <span>{{ stage.plannedDuration }}天</span>
-                                    </div>
-                                    <div class="detail-item">
-                                      <label>实际时长:</label>
-                                      <span>{{ stage.actualDuration }}天</span>
-                                    </div>
-                                    <div class="detail-item">
-                                      <label>执行状态:</label>
-                                      <span class="status" [class]="stage.executionStatus">
-                                        {{ getExecutionStatusText(stage.executionStatus) }}
-                                      </span>
-                                    </div>
+                                  <div class="stage-metric-item">
+                                    <span class="label">实际时长:</span>
+                                    <span class="value" [class.warning]="stage.actualDuration > stage.plannedDuration">{{ stage.actualDuration }}天</span>
+                                  </div>
+                                  <div class="stage-metric-item">
+                                    <span class="label">执行评分:</span>
+                                    <span class="value score" [class]="getScoreClass(stage.score)">{{ stage.score }}/100</span>
                                   </div>
                                 </div>
-                              }
+                                @if (stage.issues && stage.issues.length > 0) {
+                                  <div class="stage-issues">
+                                    <span class="issues-label">⚠️ 问题:</span>
+                                    <span class="issues-text">{{ stage.issues.join('、') }}</span>
+                                  </div>
+                                }
+                              </div>
                             </div>
-                          </div>
+                          }
+                        </div>
+                      </div>
 
-                          <!-- 经验总结 -->
-                          <div class="report-section">
-                            <h5>经验总结</h5>
-                            <div class="experience-summary">
-                              <div class="highlights">
-                                <h6>✨ 项目亮点</h6>
-                                <ul>
-                                  @for (highlight of projectReview.keyHighlights; track highlight) {
-                                    <li>{{ highlight }}</li>
-                                  }
-                                </ul>
+                      <!-- 数据图表展示 -->
+                      <div class="sop-charts-section">
+                        <h3 class="subsection-title">数据分析图表</h3>
+                        <div class="charts-grid">
+                          <div class="chart-card">
+                            <h4 class="chart-title">阶段耗时对比</h4>
+                            <div class="chart-placeholder">
+                              <div class="bar-chart">
+                                @for (stage of sopStagesData; track stage.name) {
+                                  <div class="bar-group">
+                                    <div class="bar-label">{{ stage.name }}</div>
+                                    <div class="bars">
+                                      <div class="bar planned" [style.height.%]="(stage.plannedDuration / getMaxDuration()) * 100">
+                                        <span class="bar-value">{{ stage.plannedDuration }}</span>
+                                      </div>
+                                      <div class="bar actual" [style.height.%]="(stage.actualDuration / getMaxDuration()) * 100">
+                                        <span class="bar-value">{{ stage.actualDuration }}</span>
+                                      </div>
+                                    </div>
+                                  </div>
+                                }
                               </div>
-                              <div class="improvements">
-                                <h6>🔧 改进建议</h6>
-                                <ul>
-                                  @for (improvement of projectReview.improvementSuggestions; track improvement) {
-                                    <li>{{ improvement }}</li>
-                                  }
-                                </ul>
+                              <div class="chart-legend">
+                                <span class="legend-item"><span class="legend-color planned"></span>计划时长</span>
+                                <span class="legend-item"><span class="legend-color actual"></span>实际时长</span>
                               </div>
                             </div>
                           </div>
 
-                          <!-- 客户满意度 -->
-                          <div class="report-section">
-                            <h5>客户满意度</h5>
-                            <div class="satisfaction-metrics">
-                              <div class="metric-item">
-                                <label>整体满意度:</label>
-                                <div class="rating">
-                                  @for (star of [1,2,3,4,5]; track star) {
-                                    <span class="star" [class.filled]="star <= projectReview.customerSatisfaction.overallRating">⭐</span>
+                          <div class="chart-card">
+                            <h4 class="chart-title">执行评分雷达图</h4>
+                            <div class="chart-placeholder radar-chart">
+                              <div class="radar-info">
+                                <p>各阶段平均得分:<strong>{{ getAverageScore() }}/100</strong></p>
+                                <div class="score-items">
+                                  @for (stage of sopStagesData.slice(0, 5); track stage.name) {
+                                    <div class="score-item">
+                                      <span class="stage-label">{{ stage.name }}:</span>
+                                      <span class="stage-score" [class]="getScoreClass(stage.score)">{{ stage.score }}</span>
+                                    </div>
                                   }
-                                  <span class="rating-text">({{ projectReview.customerSatisfaction.overallRating }}/5)</span>
                                 </div>
                               </div>
-                              @if (projectReview.customerSatisfaction.feedback) {
-                                <div class="feedback">
-                                  <label>客户反馈:</label>
-                                  <p>"{{ projectReview.customerSatisfaction.feedback }}"</p>
-                                </div>
-                              }
                             </div>
                           </div>
                         </div>
                       </div>
-                    }
+                    </div>
+                  }
 
-                    @if (canEditSection('aftercare')) {
-                      <div class="review-actions">
-                        <button class="primary-btn" (click)="generateReviewReport()" [disabled]="isGeneratingReview">
-                          @if (isGeneratingReview) {
-                            <span class="loading-spinner"></span>
-                            生成中...
+                  <!-- 经验复盘 -->
+                  @if (activeReviewTab === 'experience') {
+                    <div class="experience-content">
+                      <div class="experience-intro">
+                        <p class="intro-text">基于企业微信沟通记录智能提取关键信息,全面复盘项目过程</p>
+                      </div>
+
+                      <div class="experience-grid">
+                        <!-- 客户需求 -->
+                        <div class="experience-card needs-card">
+                          <div class="card-header-custom">
+                            <span class="card-icon">📝</span>
+                            <h3 class="card-title-custom">客户需求</h3>
+                            <span class="count-badge">{{ experienceData?.customerNeeds?.length || 0 }}项</span>
+                          </div>
+                          <div class="card-content-scrollable">
+                            @if (experienceData?.customerNeeds && experienceData.customerNeeds.length > 0) {
+                              <ul class="experience-list">
+                                @for (need of experienceData.customerNeeds; track $index) {
+                                  <li class="experience-item">
+                                    <span class="item-bullet">•</span>
+                                    <div class="item-content">
+                                      <p class="item-text">{{ need.text }}</p>
+                                      <div class="item-meta">
+                                        <span class="meta-time">{{ need.timestamp }}</span>
+                                        <span class="meta-source">来源:{{ need.source }}</span>
+                                      </div>
+                                    </div>
+                                  </li>
+                                }
+                              </ul>
+                            } @else {
+                              <div class="empty-state-small">暂无客户需求记录</div>
+                            }
+                          </div>
+                        </div>
+
+                        <!-- 客户顾虑 -->
+                        <div class="experience-card concerns-card">
+                          <div class="card-header-custom">
+                            <span class="card-icon">🤔</span>
+                            <h3 class="card-title-custom">客户顾虑</h3>
+                            <span class="count-badge">{{ experienceData?.customerConcerns?.length || 0 }}项</span>
+                          </div>
+                          <div class="card-content-scrollable">
+                            @if (experienceData?.customerConcerns && experienceData.customerConcerns.length > 0) {
+                              <ul class="experience-list">
+                                @for (concern of experienceData.customerConcerns; track $index) {
+                                  <li class="experience-item concern">
+                                    <span class="item-bullet">⚠️</span>
+                                    <div class="item-content">
+                                      <p class="item-text">{{ concern.text }}</p>
+                                      <div class="item-meta">
+                                        <span class="meta-time">{{ concern.timestamp }}</span>
+                                        @if (concern.resolved) {
+                                          <span class="meta-resolved">✓ 已解决</span>
+                                        } @else {
+                                          <span class="meta-unresolved">待处理</span>
+                                        }
+                                      </div>
+                                    </div>
+                                  </li>
+                                }
+                              </ul>
+                            } @else {
+                              <div class="empty-state-small">暂无客户顾虑记录</div>
+                            }
+                          </div>
+                        </div>
+
+                        <!-- 投诉点 -->
+                        <div class="experience-card complaints-card">
+                          <div class="card-header-custom">
+                            <span class="card-icon">📢</span>
+                            <h3 class="card-title-custom">投诉点</h3>
+                            <span class="count-badge warning">{{ experienceData?.complaintPoints?.length || 0 }}项</span>
+                          </div>
+                          <div class="card-content-scrollable">
+                            @if (experienceData?.complaintPoints && experienceData.complaintPoints.length > 0) {
+                              <ul class="experience-list">
+                                @for (complaint of experienceData.complaintPoints; track $index) {
+                                  <li class="experience-item complaint">
+                                    <span class="item-bullet">❗</span>
+                                    <div class="item-content">
+                                      <p class="item-text">{{ complaint.text }}</p>
+                                      <div class="item-meta">
+                                        <span class="meta-time">{{ complaint.timestamp }}</span>
+                                        <span class="meta-severity" [class]="complaint.severity">{{ complaint.severityText }}</span>
+                                      </div>
+                                      @if (complaint.resolution) {
+                                        <div class="item-resolution">
+                                          <span class="resolution-label">解决方案:</span>
+                                          <span class="resolution-text">{{ complaint.resolution }}</span>
+                                        </div>
+                                      }
+                                    </div>
+                                  </li>
+                                }
+                              </ul>
+                            } @else {
+                              <div class="empty-state-small">✓ 暂无投诉记录</div>
+                            }
+                          </div>
+                        </div>
+
+                        <!-- 项目亮点 -->
+                        <div class="experience-card highlights-card">
+                          <div class="card-header-custom">
+                            <span class="card-icon">✨</span>
+                            <h3 class="card-title-custom">项目亮点</h3>
+                            <span class="count-badge success">{{ experienceData?.projectHighlights?.length || 0 }}项</span>
+                          </div>
+                          <div class="card-content-scrollable">
+                            @if (experienceData?.projectHighlights && experienceData.projectHighlights.length > 0) {
+                              <ul class="experience-list">
+                                @for (highlight of experienceData.projectHighlights; track $index) {
+                                  <li class="experience-item highlight">
+                                    <span class="item-bullet">⭐</span>
+                                    <div class="item-content">
+                                      <p class="item-text">{{ highlight.text }}</p>
+                                      <div class="item-meta">
+                                        <span class="meta-category">{{ highlight.category }}</span>
+                                        @if (highlight.praised) {
+                                          <span class="meta-praised">👍 客户点赞</span>
+                                        }
+                                      </div>
+                                    </div>
+                                  </li>
+                                }
+                              </ul>
+                            } @else {
+                              <div class="empty-state-small">暂无项目亮点记录</div>
+                            }
+                          </div>
+                        </div>
+                      </div>
+
+                      <!-- 沟通记录摘要 -->
+                      <div class="communication-summary">
+                        <h3 class="subsection-title">关键沟通记录</h3>
+                        <div class="communication-timeline">
+                          @if (experienceData?.communications && experienceData.communications.length > 0) {
+                            @for (comm of experienceData.communications; track $index) {
+                              <div class="communication-item">
+                                <div class="comm-time">{{ comm.timestamp }}</div>
+                                <div class="comm-content">
+                                  <div class="comm-header">
+                                    <span class="comm-participant">{{ comm.participant }}</span>
+                                    <span class="comm-type" [class]="comm.type">{{ comm.typeText }}</span>
+                                  </div>
+                                  <p class="comm-message">{{ comm.message }}</p>
+                                  @if (comm.attachments && comm.attachments.length > 0) {
+                                    <div class="comm-attachments">
+                                      <span class="attachment-icon">📎</span>
+                                      <span class="attachment-count">{{ comm.attachments.length }}个附件</span>
+                                    </div>
+                                  }
+                                </div>
+                              </div>
+                            }
                           } @else {
-                            📊 生成复盘报告
+                            <div class="empty-state-small">暂无沟通记录</div>
                           }
-                        </button>
-                        @if (projectReview) {
-                          <button class="secondary-btn" (click)="regenerateReviewReport()">
-                            🔄 重新生成
-                          </button>
-                          <button class="primary-btn" (click)="shareReviewReport()">
-                            📤 分享报告
-                          </button>
+                        </div>
+                      </div>
+                    </div>
+                  }
+
+                  <!-- 优化建议 -->
+                  @if (activeReviewTab === 'suggestions') {
+                    <div class="suggestions-content">
+                      <div class="suggestions-intro">
+                        <div class="intro-card">
+                          <h3 class="intro-title">智能分析与建议</h3>
+                          <p class="intro-desc">基于项目执行数据和历史经验,为您提供具体可操作的优化建议</p>
+                        </div>
+                      </div>
+
+                      <div class="suggestions-list">
+                        @if (optimizationSuggestions && optimizationSuggestions.length > 0) {
+                          @for (suggestion of optimizationSuggestions; track $index) {
+                            <div class="suggestion-card" [class]="suggestion.priority">
+                              <div class="suggestion-header">
+                                <div class="header-left-content">
+                                  <span class="suggestion-priority-badge" [class]="suggestion.priority">
+                                    {{ suggestion.priorityText }}
+                                  </span>
+                                  <span class="suggestion-category">{{ suggestion.category }}</span>
+                                </div>
+                                <div class="header-right-content">
+                                  <span class="suggestion-impact">预期提升:<strong>{{ suggestion.expectedImprovement }}</strong></span>
+                                </div>
+                              </div>
+                              
+                              <div class="suggestion-body">
+                                <div class="suggestion-problem">
+                                  <h4 class="problem-title">🔍 问题分析</h4>
+                                  <p class="problem-text">{{ suggestion.problem }}</p>
+                                  <div class="problem-data">
+                                    @for (data of suggestion.dataPoints; track $index) {
+                                      <div class="data-point">
+                                        <span class="data-label">{{ data.label }}:</span>
+                                        <span class="data-value" [class.warning]="data.isWarning">{{ data.value }}</span>
+                                      </div>
+                                    }
+                                  </div>
+                                </div>
+
+                                <div class="suggestion-solution">
+                                  <h4 class="solution-title">💡 优化建议</h4>
+                                  <p class="solution-text">{{ suggestion.solution }}</p>
+                                </div>
+
+                                <div class="suggestion-actions-plan">
+                                  <h4 class="actions-title">📋 行动计划</h4>
+                                  <ul class="actions-list">
+                                    @for (action of suggestion.actionPlan; track $index) {
+                                      <li class="action-item">
+                                        <span class="action-step">{{ $index + 1 }}.</span>
+                                        <span class="action-text">{{ action }}</span>
+                                      </li>
+                                    }
+                                  </ul>
+                                </div>
+
+                                @if (suggestion.references && suggestion.references.length > 0) {
+                                  <div class="suggestion-references">
+                                    <span class="references-label">📚 参考案例:</span>
+                                    @for (ref of suggestion.references; track $index) {
+                                      <span class="reference-tag">{{ ref }}</span>
+                                    }
+                                  </div>
+                                }
+                              </div>
+
+                              <div class="suggestion-footer">
+                                <button class="action-btn accept" (click)="acceptSuggestion(suggestion)">
+                                  ✓ 采纳建议
+                                </button>
+                                <button class="action-btn detail" (click)="viewSuggestionDetail(suggestion)">
+                                  查看详情
+                                </button>
+                              </div>
+                            </div>
+                          }
+                        } @else {
+                          <div class="empty-state-large">
+                            <div class="empty-icon">📊</div>
+                            <h3 class="empty-title">暂无优化建议</h3>
+                            <p class="empty-desc">项目数据收集完成后将自动生成智能优化建议</p>
+                          </div>
                         }
                       </div>
-                    }
-                  </div>
+
+                      <!-- 建议统计 -->
+                      @if (optimizationSuggestions && optimizationSuggestions.length > 0) {
+                        <div class="suggestions-stats">
+                          <h3 class="subsection-title">建议统计</h3>
+                          <div class="stats-grid">
+                            <div class="stat-card">
+                              <div class="stat-icon">🔴</div>
+                              <div class="stat-content">
+                                <div class="stat-value">{{ getSuggestionCountByPriority('high') }}</div>
+                                <div class="stat-label">高优先级</div>
+                              </div>
+                            </div>
+                            <div class="stat-card">
+                              <div class="stat-icon">🟡</div>
+                              <div class="stat-content">
+                                <div class="stat-value">{{ getSuggestionCountByPriority('medium') }}</div>
+                                <div class="stat-label">中优先级</div>
+                              </div>
+                            </div>
+                            <div class="stat-card">
+                              <div class="stat-icon">🟢</div>
+                              <div class="stat-content">
+                                <div class="stat-value">{{ getSuggestionCountByPriority('low') }}</div>
+                                <div class="stat-label">低优先级</div>
+                              </div>
+                            </div>
+                            <div class="stat-card">
+                              <div class="stat-icon">📈</div>
+                              <div class="stat-content">
+                                <div class="stat-value">{{ getAverageImprovementPercent() }}%</div>
+                                <div class="stat-label">平均预期提升</div>
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                      }
+                    </div>
+                  }
                 </div>
               </div>
               }

+ 1272 - 1
src/app/pages/designer/project-detail/project-detail.scss

@@ -1,5 +1,7 @@
 @use '../../../shared/styles/_ios-theme.scss' as *;
 @import './order-creation-form.scss';
+@import './project-review-styles.scss';
+@import './project-review-experience-suggestions-styles.scss';
 
 /* 头部导航样式 */
 .header-nav {
@@ -3715,4 +3717,1273 @@
       }
     }
   }
-}
+}
+/* ==================== 项目复盘模块样式 ==================== */
+.project-review-section {
+  width: 100%;
+  background: white;
+  border-radius: 20px;
+  padding: 32px;
+  margin-top: 24px;
+  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
+  
+  .review-section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: 28px;
+    padding-bottom: 20px;
+    border-bottom: 2px solid rgba(0, 122, 255, 0.1);
+    
+    .header-left {
+      flex: 1;
+      
+      .section-title {
+        font-size: 24px;
+        font-weight: 700;
+        color: #1a1a1a;
+        margin: 0 0 8px 0;
+        display: flex;
+        align-items: center;
+        gap: 10px;
+      }
+      
+      .section-subtitle {
+        font-size: 14px;
+        color: #666;
+        margin: 0;
+        line-height: 1.6;
+      }
+    }
+    
+    .header-right {
+      display: flex;
+      gap: 12px;
+      
+      button {
+        padding: 10px 20px;
+        border-radius: 10px;
+        border: none;
+        font-size: 14px;
+        font-weight: 600;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        
+        &.generate-review-btn {
+          background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
+          color: white;
+          
+          &:hover:not(:disabled) {
+            transform: translateY(-2px);
+            box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
+          }
+          
+          &:disabled {
+            opacity: 0.6;
+            cursor: not-allowed;
+          }
+        }
+        
+        &.export-review-btn {
+          background: #f0f0f0;
+          color: #333;
+          
+          &:hover {
+            background: #e0e0e0;
+          }
+        }
+      }
+    }
+  }
+  
+  .review-tabs {
+    display: flex;
+    gap: 12px;
+    margin-bottom: 32px;
+    border-bottom: 2px solid #f0f0f0;
+    
+    .review-tab {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 14px 24px;
+      border: none;
+      background: transparent;
+      color: #666;
+      font-size: 15px;
+      font-weight: 600;
+      cursor: pointer;
+      position: relative;
+      transition: all 0.3s ease;
+      border-bottom: 3px solid transparent;
+      margin-bottom: -2px;
+      
+      .tab-icon {
+        font-size: 18px;
+      }
+      
+      .tab-label {
+        font-weight: 500;
+      }
+      
+      &:hover {
+        color: #007aff;
+        background: rgba(0, 122, 255, 0.05);
+      }
+      
+      &.active {
+        color: #007aff;
+        border-bottom-color: #007aff;
+        background: rgba(0, 122, 255, 0.08);
+      }
+    }
+  }
+  
+  .review-content-area {
+    min-height: 500px;
+    max-height: 800px;
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding-right: 8px;
+    
+    /* 自定义滚动条 */
+    &::-webkit-scrollbar {
+      width: 10px;
+    }
+    
+    &::-webkit-scrollbar-track {
+      background: #f5f5f5;
+      border-radius: 5px;
+    }
+    
+    &::-webkit-scrollbar-thumb {
+      background: #c0c0c0;
+      border-radius: 5px;
+      
+      &:hover {
+        background: #a0a0a0;
+      }
+    }
+  }
+}
+
+/* SOP执行数据样式 */
+.sop-data-content {
+  .sop-metrics-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+    gap: 20px;
+    margin-bottom: 40px;
+    
+    .metric-card {
+      background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+      border: 1px solid #e0e0e0;
+      border-radius: 16px;
+      padding: 24px;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: translateY(-4px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+        border-color: #007aff;
+      }
+      
+      .metric-header {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        margin-bottom: 16px;
+        
+        .metric-icon {
+          font-size: 24px;
+        }
+        
+        .metric-title {
+          font-size: 14px;
+          font-weight: 600;
+          color: #666;
+          margin: 0;
+        }
+      }
+      
+      .metric-value-large {
+        font-size: 36px;
+        font-weight: 700;
+        color: #1a1a1a;
+        margin-bottom: 12px;
+        
+        .unit {
+          font-size: 18px;
+          font-weight: 400;
+          color: #999;
+          margin-left: 4px;
+        }
+      }
+      
+      .metric-footer {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: 8px;
+        font-size: 13px;
+        
+        .metric-label {
+          color: #999;
+        }
+        
+        .metric-benchmark {
+          color: #666;
+          font-weight: 500;
+        }
+        
+        .metric-status {
+          padding: 4px 10px;
+          border-radius: 6px;
+          font-weight: 600;
+          font-size: 12px;
+          
+          &.good {
+            background: #e7f4e4;
+            color: #34c759;
+          }
+          
+          &.warning {
+            background: #fff3cd;
+            color: #ff9500;
+          }
+        }
+        
+        .satisfaction-stars {
+          display: flex;
+          gap: 4px;
+          
+          .star {
+            font-size: 16px;
+            color: #e0e0e0;
+            
+            &.filled {
+              color: #ffc107;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .sop-stages-section {
+    margin-bottom: 40px;
+    
+    .subsection-title {
+      font-size: 18px;
+      font-weight: 700;
+      color: #1a1a1a;
+      margin: 0 0 24px 0;
+      padding-bottom: 12px;
+      border-bottom: 2px solid #f0f0f0;
+    }
+    
+    .stages-timeline {
+      display: flex;
+      flex-direction: column;
+      gap: 0;
+      
+      .stage-timeline-item {
+        display: flex;
+        gap: 20px;
+        position: relative;
+        
+        .stage-indicator {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          padding-top: 8px;
+          
+          .stage-dot {
+            width: 16px;
+            height: 16px;
+            border-radius: 50%;
+            background: #e0e0e0;
+            border: 3px solid #fff;
+            box-shadow: 0 0 0 2px #e0e0e0;
+            flex-shrink: 0;
+            z-index: 1;
+          }
+          
+          .stage-line {
+            width: 2px;
+            flex: 1;
+            background: #e0e0e0;
+            min-height: 40px;
+          }
+        }
+        
+        &.completed .stage-indicator .stage-dot {
+          background: #34c759;
+          box-shadow: 0 0 0 2px #34c759;
+        }
+        
+        &.ongoing .stage-indicator .stage-dot {
+          background: #007aff;
+          box-shadow: 0 0 0 2px #007aff;
+          animation: pulse 2s infinite;
+        }
+        
+        &.delayed .stage-indicator .stage-dot {
+          background: #ff9500;
+          box-shadow: 0 0 0 2px #ff9500;
+        }
+        
+        .stage-content-card {
+          flex: 1;
+          background: white;
+          border: 1px solid #e0e0e0;
+          border-radius: 12px;
+          padding: 20px;
+          margin-bottom: 20px;
+          transition: all 0.3s ease;
+          
+          &:hover {
+            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+            border-color: #007aff;
+          }
+          
+          .stage-card-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 16px;
+            
+            .stage-name {
+              font-size: 16px;
+              font-weight: 700;
+              color: #1a1a1a;
+              margin: 0;
+            }
+            
+            .stage-status-badge {
+              padding: 6px 14px;
+              border-radius: 20px;
+              font-size: 12px;
+              font-weight: 600;
+              
+              &.completed {
+                background: #e7f4e4;
+                color: #34c759;
+              }
+              
+              &.ongoing {
+                background: #e3f2fd;
+                color: #007aff;
+              }
+              
+              &.delayed {
+                background: #fff3cd;
+                color: #ff9500;
+              }
+            }
+          }
+          
+          .stage-metrics {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+            gap: 16px;
+            margin-bottom: 12px;
+            
+            .stage-metric-item {
+              display: flex;
+              flex-direction: column;
+              
+              .label {
+                font-size: 13px;
+                color: #999;
+                margin-bottom: 4px;
+              }
+              
+              .value {
+                font-size: 16px;
+                font-weight: 600;
+                color: #1a1a1a;
+                
+                &.warning {
+                  color: #ff9500;
+                }
+                
+                &.score {
+                  &.excellent {
+                    color: #34c759;
+                  }
+                  
+                  &.good {
+                    color: #007aff;
+                  }
+                  
+                  &.fair {
+                    color: #ff9500;
+                  }
+                  
+                  &.poor {
+                    color: #ff3b30;
+                  }
+                }
+              }
+            }
+          }
+          
+          .stage-issues {
+            background: #fff3cd;
+            border-left: 4px solid #ff9500;
+            padding: 12px;
+            border-radius: 8px;
+            
+            .issues-label {
+              font-weight: 600;
+              color: #ff9500;
+              margin-right: 8px;
+            }
+            
+            .issues-text {
+              font-size: 14px;
+              color: #666;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .sop-charts-section {
+    .charts-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+      gap: 24px;
+      
+      .chart-card {
+        background: white;
+        border: 1px solid #e0e0e0;
+        border-radius: 16px;
+        padding: 24px;
+        
+        .chart-title {
+          font-size: 16px;
+          font-weight: 700;
+          color: #1a1a1a;
+          margin: 0 0 20px 0;
+        }
+        
+        .chart-placeholder {
+          .bar-chart {
+            display: flex;
+            justify-content: space-around;
+            align-items: flex-end;
+            gap: 12px;
+            height: 200px;
+            padding: 0 10px;
+            
+            .bar-group {
+              flex: 1;
+              display: flex;
+              flex-direction: column;
+              align-items: center;
+              gap: 8px;
+              
+              .bar-label {
+                font-size: 12px;
+                color: #666;
+                writing-mode: horizontal-tb;
+                text-align: center;
+                word-wrap: break-word;
+                max-width: 100%;
+              }
+              
+              .bars {
+                display: flex;
+                gap: 4px;
+                align-items: flex-end;
+                flex: 1;
+                width: 100%;
+                
+                .bar {
+                  flex: 1;
+                  min-height: 20px;
+                  border-radius: 6px 6px 0 0;
+                  position: relative;
+                  display: flex;
+                  justify-content: center;
+                  padding-top: 6px;
+                  transition: all 0.3s ease;
+                  
+                  &.planned {
+                    background: linear-gradient(to top, #4caf50, #81c784);
+                  }
+                  
+                  &.actual {
+                    background: linear-gradient(to top, #2196f3, #64b5f6);
+                  }
+                  
+                  .bar-value {
+                    font-size: 11px;
+                    font-weight: 600;
+                    color: white;
+                  }
+                  
+                  &:hover {
+                    transform: scale(1.05);
+                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+                  }
+                }
+              }
+            }
+          }
+          
+          .chart-legend {
+            display: flex;
+            justify-content: center;
+            gap: 24px;
+            margin-top: 20px;
+            
+            .legend-item {
+              display: flex;
+              align-items: center;
+              gap: 8px;
+              font-size: 13px;
+              color: #666;
+              
+              .legend-color {
+                width: 16px;
+                height: 16px;
+                border-radius: 4px;
+                
+                &.planned {
+                  background: linear-gradient(to bottom, #4caf50, #81c784);
+                }
+                
+                &.actual {
+                  background: linear-gradient(to bottom, #2196f3, #64b5f6);
+                }
+              }
+            }
+          }
+          
+          &.radar-chart {
+            height: 200px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            
+            .radar-info {
+              text-align: center;
+              
+              p {
+                font-size: 16px;
+                color: #666;
+                margin-bottom: 16px;
+                
+                strong {
+                  font-size: 20px;
+                  color: #007aff;
+                }
+              }
+              
+              .score-items {
+                display: flex;
+                flex-direction: column;
+                gap: 8px;
+                
+                .score-item {
+                  display: flex;
+                  justify-content: space-between;
+                  font-size: 14px;
+                  
+                  .stage-label {
+                    color: #666;
+                  }
+                  
+                  .stage-score {
+                    font-weight: 600;
+                    
+                    &.excellent { color: #34c759; }
+                    &.good { color: #007aff; }
+                    &.fair { color: #ff9500; }
+                    &.poor { color: #ff3b30; }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/* 经验复盘样式 */
+.experience-content {
+  .experience-intro {
+    background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
+    border-radius: 12px;
+    padding: 20px;
+    margin-bottom: 32px;
+    
+    .intro-text {
+      font-size: 15px;
+      color: #555;
+      text-align: center;
+      margin: 0;
+      line-height: 1.6;
+    }
+  }
+  
+  .experience-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+    gap: 20px;
+    margin-bottom: 40px;
+    
+    .experience-card {
+      background: white;
+      border: 1px solid #e0e0e0;
+      border-radius: 16px;
+      padding: 0;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: translateY(-4px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+      }
+      
+      &.needs-card {
+        border-top: 4px solid #007aff;
+      }
+      
+      &.concerns-card {
+        border-top: 4px solid #ff9500;
+      }
+      
+      &.complaints-card {
+        border-top: 4px solid #ff3b30;
+      }
+      
+      &.highlights-card {
+        border-top: 4px solid #34c759;
+      }
+      
+      .card-header-custom {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        padding: 20px 24px;
+        background: linear-gradient(to bottom, #fafafa, #ffffff);
+        border-bottom: 1px solid #f0f0f0;
+        
+        .card-icon {
+          font-size: 22px;
+        }
+        
+        .card-title-custom {
+          flex: 1;
+          font-size: 16px;
+          font-weight: 700;
+          color: #1a1a1a;
+          margin: 0;
+        }
+        
+        .count-badge {
+          padding: 4px 12px;
+          border-radius: 20px;
+          font-size: 12px;
+          font-weight: 600;
+          background: #f0f0f0;
+          color: #666;
+          
+          &.warning {
+            background: #fff3cd;
+            color: #ff9500;
+          }
+          
+          &.success {
+            background: #e7f4e4;
+            color: #34c759;
+          }
+        }
+      }
+      
+      .card-content-scrollable {
+        max-height: 300px;
+        overflow-y: auto;
+        padding: 20px 24px;
+        
+        &::-webkit-scrollbar {
+          width: 6px;
+        }
+        
+        &::-webkit-scrollbar-track {
+          background: #f5f5f5;
+        }
+        
+        &::-webkit-scrollbar-thumb {
+          background: #c0c0c0;
+          border-radius: 3px;
+        }
+        
+        .experience-list {
+          list-style: none;
+          padding: 0;
+          margin: 0;
+          
+          .experience-item {
+            display: flex;
+            gap: 12px;
+            margin-bottom: 16px;
+            padding-bottom: 16px;
+            border-bottom: 1px solid #f0f0f0;
+            
+            &:last-child {
+              border-bottom: none;
+              margin-bottom: 0;
+              padding-bottom: 0;
+            }
+            
+            .item-bullet {
+              font-size: 16px;
+              flex-shrink: 0;
+              line-height: 1.5;
+            }
+            
+            .item-content {
+              flex: 1;
+              
+              .item-text {
+                font-size: 14px;
+                color: #333;
+                line-height: 1.6;
+                margin: 0 0 8px 0;
+              }
+              
+              .item-meta {
+                display: flex;
+                flex-wrap: wrap;
+                gap: 12px;
+                font-size: 12px;
+                
+                .meta-time,
+                .meta-source {
+                  color: #999;
+                }
+                
+                .meta-resolved {
+                  color: #34c759;
+                  font-weight: 600;
+                }
+                
+                .meta-unresolved {
+                  color: #ff9500;
+                  font-weight: 600;
+                }
+                
+                .meta-severity {
+                  padding: 2px 8px;
+                  border-radius: 4px;
+                  font-weight: 600;
+                  
+                  &.high {
+                    background: #ffe5e5;
+                    color: #ff3b30;
+                  }
+                  
+                  &.medium {
+                    background: #fff3cd;
+                    color: #ff9500;
+                  }
+                  
+                  &.low {
+                    background: #f0f0f0;
+                    color: #666;
+                  }
+                }
+                
+                .meta-category {
+                  color: #007aff;
+                  font-weight: 600;
+                }
+                
+                .meta-praised {
+                  color: #34c759;
+                  font-weight: 600;
+                }
+              }
+              
+              .item-resolution {
+                margin-top: 8px;
+                padding: 10px;
+                background: #e7f4e4;
+                border-radius: 8px;
+                font-size: 13px;
+                
+                .resolution-label {
+                  font-weight: 600;
+                  color: #34c759;
+                  margin-right: 6px;
+                }
+                
+                .resolution-text {
+                  color: #555;
+                }
+              }
+            }
+          }
+        }
+        
+        .empty-state-small {
+          text-align: center;
+          padding: 40px 20px;
+          color: #999;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+  
+  .communication-summary {
+    background: white;
+    border: 1px solid #e0e0e0;
+    border-radius: 16px;
+    padding: 24px;
+    
+    .communication-timeline {
+      display: flex;
+      flex-direction: column;
+      gap: 20px;
+      
+      .communication-item {
+        display: flex;
+        gap: 16px;
+        padding: 16px;
+        background: #fafafa;
+        border-radius: 12px;
+        border-left: 4px solid #007aff;
+        transition: all 0.3s ease;
+        
+        &:hover {
+          background: #f0f0f0;
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+        }
+        
+        .comm-time {
+          font-size: 12px;
+          color: #999;
+          white-space: nowrap;
+          min-width: 120px;
+        }
+        
+        .comm-content {
+          flex: 1;
+          
+          .comm-header {
+            display: flex;
+            align-items: center;
+            gap: 12px;
+            margin-bottom: 8px;
+            
+            .comm-participant {
+              font-weight: 700;
+              color: #1a1a1a;
+            }
+            
+            .comm-type {
+              padding: 3px 10px;
+              border-radius: 12px;
+              font-size: 11px;
+              font-weight: 600;
+              
+              &.requirement {
+                background: #e3f2fd;
+                color: #007aff;
+              }
+              
+              &.response {
+                background: #e7f4e4;
+                color: #34c759;
+              }
+              
+              &.concern {
+                background: #fff3cd;
+                color: #ff9500;
+              }
+            }
+          }
+          
+          .comm-message {
+            font-size: 14px;
+            color: #555;
+            line-height: 1.6;
+            margin: 0 0 8px 0;
+          }
+          
+          .comm-attachments {
+            display: flex;
+            align-items: center;
+            gap: 6px;
+            font-size: 12px;
+            color: #999;
+            
+            .attachment-icon {
+              font-size: 14px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/* 优化建议样式 */
+.suggestions-content {
+  .suggestions-intro {
+    .intro-card {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      color: white;
+      border-radius: 16px;
+      padding: 28px;
+      margin-bottom: 32px;
+      text-align: center;
+      
+      .intro-title {
+        font-size: 22px;
+        font-weight: 700;
+        margin: 0 0 10px 0;
+      }
+      
+      .intro-desc {
+        font-size: 15px;
+        opacity: 0.95;
+        margin: 0;
+        line-height: 1.6;
+      }
+    }
+  }
+  
+  .suggestions-list {
+    display: flex;
+    flex-direction: column;
+    gap: 24px;
+    margin-bottom: 40px;
+    
+    .suggestion-card {
+      background: white;
+      border: 1px solid #e0e0e0;
+      border-radius: 16px;
+      padding: 0;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
+        transform: translateY(-2px);
+      }
+      
+      &.high {
+        border-left: 6px solid #ff3b30;
+      }
+      
+      &.medium {
+        border-left: 6px solid #ff9500;
+      }
+      
+      &.low {
+        border-left: 6px solid #34c759;
+      }
+      
+      .suggestion-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 20px 24px;
+        background: linear-gradient(to bottom, #fafafa, #ffffff);
+        border-bottom: 1px solid #f0f0f0;
+        
+        .header-left-content {
+          display: flex;
+          align-items: center;
+          gap: 12px;
+          
+          .suggestion-priority-badge {
+            padding: 6px 14px;
+            border-radius: 20px;
+            font-size: 12px;
+            font-weight: 700;
+            
+            &.high {
+              background: #ffe5e5;
+              color: #ff3b30;
+            }
+            
+            &.medium {
+              background: #fff3cd;
+              color: #ff9500;
+            }
+            
+            &.low {
+              background: #e7f4e4;
+              color: #34c759;
+            }
+          }
+          
+          .suggestion-category {
+            font-size: 14px;
+            font-weight: 600;
+            color: #666;
+          }
+        }
+        
+        .header-right-content {
+          .suggestion-impact {
+            font-size: 13px;
+            color: #666;
+            
+            strong {
+              color: #007aff;
+              font-weight: 700;
+            }
+          }
+        }
+      }
+      
+      .suggestion-body {
+        padding: 24px;
+        
+        .suggestion-problem,
+        .suggestion-solution,
+        .suggestion-actions-plan {
+          margin-bottom: 24px;
+          
+          &:last-child {
+            margin-bottom: 0;
+          }
+          
+          .problem-title,
+          .solution-title,
+          .actions-title {
+            font-size: 15px;
+            font-weight: 700;
+            color: #1a1a1a;
+            margin: 0 0 12px 0;
+          }
+          
+          .problem-text,
+          .solution-text {
+            font-size: 14px;
+            color: #555;
+            line-height: 1.7;
+            margin: 0 0 16px 0;
+          }
+        }
+        
+        .problem-data {
+          display: grid;
+          grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+          gap: 12px;
+          
+          .data-point {
+            background: #fafafa;
+            border: 1px solid #e0e0e0;
+            border-radius: 10px;
+            padding: 12px;
+            
+            .data-label {
+              font-size: 12px;
+              color: #999;
+              margin-right: 4px;
+            }
+            
+            .data-value {
+              font-size: 15px;
+              font-weight: 700;
+              color: #1a1a1a;
+              
+              &.warning {
+                color: #ff9500;
+              }
+            }
+          }
+        }
+        
+        .actions-list {
+          list-style: none;
+          padding: 0;
+          margin: 0;
+          
+          .action-item {
+            display: flex;
+            gap: 12px;
+            margin-bottom: 12px;
+            padding: 12px;
+            background: #f8f9fa;
+            border-radius: 10px;
+            border-left: 3px solid #007aff;
+            
+            &:last-child {
+              margin-bottom: 0;
+            }
+            
+            .action-step {
+              font-weight: 700;
+              color: #007aff;
+              flex-shrink: 0;
+            }
+            
+            .action-text {
+              font-size: 14px;
+              color: #555;
+              line-height: 1.6;
+            }
+          }
+        }
+        
+        .suggestion-references {
+          margin-top: 16px;
+          padding-top: 16px;
+          border-top: 1px solid #f0f0f0;
+          
+          .references-label {
+            font-size: 13px;
+            color: #666;
+            margin-right: 8px;
+          }
+          
+          .reference-tag {
+            display: inline-block;
+            padding: 4px 10px;
+            background: #e3f2fd;
+            color: #007aff;
+            border-radius: 12px;
+            font-size: 12px;
+            font-weight: 600;
+            margin-right: 8px;
+            margin-top: 6px;
+          }
+        }
+      }
+      
+      .suggestion-footer {
+        display: flex;
+        gap: 12px;
+        padding: 16px 24px;
+        background: #fafafa;
+        border-top: 1px solid #f0f0f0;
+        
+        .action-btn {
+          padding: 10px 20px;
+          border: none;
+          border-radius: 10px;
+          font-size: 14px;
+          font-weight: 600;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          
+          &.accept {
+            background: linear-gradient(135deg, #34c759 0%, #28a745 100%);
+            color: white;
+            
+            &:hover {
+              transform: translateY(-2px);
+              box-shadow: 0 4px 12px rgba(52, 199, 89, 0.4);
+            }
+          }
+          
+          &.detail {
+            background: #f0f0f0;
+            color: #333;
+            
+            &:hover {
+              background: #e0e0e0;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .empty-state-large {
+    text-align: center;
+    padding: 80px 20px;
+    
+    .empty-icon {
+      font-size: 64px;
+      margin-bottom: 20px;
+    }
+    
+    .empty-title {
+      font-size: 20px;
+      font-weight: 700;
+      color: #1a1a1a;
+      margin: 0 0 12px 0;
+    }
+    
+    .empty-desc {
+      font-size: 15px;
+      color: #999;
+      margin: 0;
+    }
+  }
+  
+  .suggestions-stats {
+    background: white;
+    border: 1px solid #e0e0e0;
+    border-radius: 16px;
+    padding: 24px;
+    
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: 20px;
+      
+      .stat-card {
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        padding: 20px;
+        background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+        border: 1px solid #e0e0e0;
+        border-radius: 12px;
+        transition: all 0.3s ease;
+        
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+        }
+        
+        .stat-icon {
+          font-size: 32px;
+        }
+        
+        .stat-content {
+          .stat-value {
+            font-size: 28px;
+            font-weight: 700;
+            color: #1a1a1a;
+            margin: 0 0 4px 0;
+          }
+          
+          .stat-label {
+            font-size: 13px;
+            color: #999;
+            margin: 0;
+          }
+        }
+      }
+    }
+  }
+}
+
+@keyframes pulse {
+  0%, 100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  50% {
+    transform: scale(1.1);
+    opacity: 0.8;
+  }
+}
+

+ 140 - 0
src/app/pages/designer/project-detail/project-detail.ts

@@ -3612,6 +3612,116 @@ export class ProjectDetail implements OnInit, OnDestroy {
   // 新增:项目复盘相关属性
   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;
@@ -4081,6 +4191,36 @@ export class ProjectDetail implements OnInit, OnDestroy {
     });
   }
 
+  // 项目复盘工具方法
+  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 = [

+ 660 - 0
src/app/pages/designer/project-detail/project-review-experience-suggestions-styles.scss

@@ -0,0 +1,660 @@
+/* 经验复盘样式 */
+.experience-content {
+  .experience-intro {
+    background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
+    border-radius: 12px;
+    padding: 20px;
+    margin-bottom: 32px;
+    
+    .intro-text {
+      font-size: 15px;
+      color: #555;
+      text-align: center;
+      margin: 0;
+      line-height: 1.6;
+    }
+  }
+  
+  .experience-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+    gap: 20px;
+    margin-bottom: 40px;
+    
+    .experience-card {
+      background: white;
+      border: 1px solid #e0e0e0;
+      border-radius: 16px;
+      padding: 0;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: translateY(-4px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+      }
+      
+      &.needs-card {
+        border-top: 4px solid #007aff;
+      }
+      
+      &.concerns-card {
+        border-top: 4px solid #ff9500;
+      }
+      
+      &.complaints-card {
+        border-top: 4px solid #ff3b30;
+      }
+      
+      &.highlights-card {
+        border-top: 4px solid #34c759;
+      }
+      
+      .card-header-custom {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        padding: 20px 24px;
+        background: linear-gradient(to bottom, #fafafa, #ffffff);
+        border-bottom: 1px solid #f0f0f0;
+        
+        .card-icon {
+          font-size: 22px;
+        }
+        
+        .card-title-custom {
+          flex: 1;
+          font-size: 16px;
+          font-weight: 700;
+          color: #1a1a1a;
+          margin: 0;
+        }
+        
+        .count-badge {
+          padding: 4px 12px;
+          border-radius: 20px;
+          font-size: 12px;
+          font-weight: 600;
+          background: #f0f0f0;
+          color: #666;
+          
+          &.warning {
+            background: #fff3cd;
+            color: #ff9500;
+          }
+          
+          &.success {
+            background: #e7f4e4;
+            color: #34c759;
+          }
+        }
+      }
+      
+      .card-content-scrollable {
+        max-height: 300px;
+        overflow-y: auto;
+        padding: 20px 24px;
+        
+        &::-webkit-scrollbar {
+          width: 6px;
+        }
+        
+        &::-webkit-scrollbar-track {
+          background: #f5f5f5;
+        }
+        
+        &::-webkit-scrollbar-thumb {
+          background: #c0c0c0;
+          border-radius: 3px;
+        }
+        
+        .experience-list {
+          list-style: none;
+          padding: 0;
+          margin: 0;
+          
+          .experience-item {
+            display: flex;
+            gap: 12px;
+            margin-bottom: 16px;
+            padding-bottom: 16px;
+            border-bottom: 1px solid #f0f0f0;
+            
+            &:last-child {
+              border-bottom: none;
+              margin-bottom: 0;
+              padding-bottom: 0;
+            }
+            
+            .item-bullet {
+              font-size: 16px;
+              flex-shrink: 0;
+              line-height: 1.5;
+            }
+            
+            .item-content {
+              flex: 1;
+              
+              .item-text {
+                font-size: 14px;
+                color: #333;
+                line-height: 1.6;
+                margin: 0 0 8px 0;
+              }
+              
+              .item-meta {
+                display: flex;
+                flex-wrap: wrap;
+                gap: 12px;
+                font-size: 12px;
+                
+                .meta-time,
+                .meta-source {
+                  color: #999;
+                }
+                
+                .meta-resolved {
+                  color: #34c759;
+                  font-weight: 600;
+                }
+                
+                .meta-unresolved {
+                  color: #ff9500;
+                  font-weight: 600;
+                }
+                
+                .meta-severity {
+                  padding: 2px 8px;
+                  border-radius: 4px;
+                  font-weight: 600;
+                  
+                  &.high {
+                    background: #ffe5e5;
+                    color: #ff3b30;
+                  }
+                  
+                  &.medium {
+                    background: #fff3cd;
+                    color: #ff9500;
+                  }
+                  
+                  &.low {
+                    background: #f0f0f0;
+                    color: #666;
+                  }
+                }
+                
+                .meta-category {
+                  color: #007aff;
+                  font-weight: 600;
+                }
+                
+                .meta-praised {
+                  color: #34c759;
+                  font-weight: 600;
+                }
+              }
+              
+              .item-resolution {
+                margin-top: 8px;
+                padding: 10px;
+                background: #e7f4e4;
+                border-radius: 8px;
+                font-size: 13px;
+                
+                .resolution-label {
+                  font-weight: 600;
+                  color: #34c759;
+                  margin-right: 6px;
+                }
+                
+                .resolution-text {
+                  color: #555;
+                }
+              }
+            }
+          }
+        }
+        
+        .empty-state-small {
+          text-align: center;
+          padding: 40px 20px;
+          color: #999;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+  
+  .communication-summary {
+    background: white;
+    border: 1px solid #e0e0e0;
+    border-radius: 16px;
+    padding: 24px;
+    
+    .communication-timeline {
+      display: flex;
+      flex-direction: column;
+      gap: 20px;
+      
+      .communication-item {
+        display: flex;
+        gap: 16px;
+        padding: 16px;
+        background: #fafafa;
+        border-radius: 12px;
+        border-left: 4px solid #007aff;
+        transition: all 0.3s ease;
+        
+        &:hover {
+          background: #f0f0f0;
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+        }
+        
+        .comm-time {
+          font-size: 12px;
+          color: #999;
+          white-space: nowrap;
+          min-width: 120px;
+        }
+        
+        .comm-content {
+          flex: 1;
+          
+          .comm-header {
+            display: flex;
+            align-items: center;
+            gap: 12px;
+            margin-bottom: 8px;
+            
+            .comm-participant {
+              font-weight: 700;
+              color: #1a1a1a;
+            }
+            
+            .comm-type {
+              padding: 3px 10px;
+              border-radius: 12px;
+              font-size: 11px;
+              font-weight: 600;
+              
+              &.requirement {
+                background: #e3f2fd;
+                color: #007aff;
+              }
+              
+              &.response {
+                background: #e7f4e4;
+                color: #34c759;
+              }
+              
+              &.concern {
+                background: #fff3cd;
+                color: #ff9500;
+              }
+            }
+          }
+          
+          .comm-message {
+            font-size: 14px;
+            color: #555;
+            line-height: 1.6;
+            margin: 0 0 8px 0;
+          }
+          
+          .comm-attachments {
+            display: flex;
+            align-items: center;
+            gap: 6px;
+            font-size: 12px;
+            color: #999;
+            
+            .attachment-icon {
+              font-size: 14px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/* 优化建议样式 */
+.suggestions-content {
+  .suggestions-intro {
+    .intro-card {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      color: white;
+      border-radius: 16px;
+      padding: 28px;
+      margin-bottom: 32px;
+      text-align: center;
+      
+      .intro-title {
+        font-size: 22px;
+        font-weight: 700;
+        margin: 0 0 10px 0;
+      }
+      
+      .intro-desc {
+        font-size: 15px;
+        opacity: 0.95;
+        margin: 0;
+        line-height: 1.6;
+      }
+    }
+  }
+  
+  .suggestions-list {
+    display: flex;
+    flex-direction: column;
+    gap: 24px;
+    margin-bottom: 40px;
+    
+    .suggestion-card {
+      background: white;
+      border: 1px solid #e0e0e0;
+      border-radius: 16px;
+      padding: 0;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
+        transform: translateY(-2px);
+      }
+      
+      &.high {
+        border-left: 6px solid #ff3b30;
+      }
+      
+      &.medium {
+        border-left: 6px solid #ff9500;
+      }
+      
+      &.low {
+        border-left: 6px solid #34c759;
+      }
+      
+      .suggestion-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 20px 24px;
+        background: linear-gradient(to bottom, #fafafa, #ffffff);
+        border-bottom: 1px solid #f0f0f0;
+        
+        .header-left-content {
+          display: flex;
+          align-items: center;
+          gap: 12px;
+          
+          .suggestion-priority-badge {
+            padding: 6px 14px;
+            border-radius: 20px;
+            font-size: 12px;
+            font-weight: 700;
+            
+            &.high {
+              background: #ffe5e5;
+              color: #ff3b30;
+            }
+            
+            &.medium {
+              background: #fff3cd;
+              color: #ff9500;
+            }
+            
+            &.low {
+              background: #e7f4e4;
+              color: #34c759;
+            }
+          }
+          
+          .suggestion-category {
+            font-size: 14px;
+            font-weight: 600;
+            color: #666;
+          }
+        }
+        
+        .header-right-content {
+          .suggestion-impact {
+            font-size: 13px;
+            color: #666;
+            
+            strong {
+              color: #007aff;
+              font-weight: 700;
+            }
+          }
+        }
+      }
+      
+      .suggestion-body {
+        padding: 24px;
+        
+        .suggestion-problem,
+        .suggestion-solution,
+        .suggestion-actions-plan {
+          margin-bottom: 24px;
+          
+          &:last-child {
+            margin-bottom: 0;
+          }
+          
+          .problem-title,
+          .solution-title,
+          .actions-title {
+            font-size: 15px;
+            font-weight: 700;
+            color: #1a1a1a;
+            margin: 0 0 12px 0;
+          }
+          
+          .problem-text,
+          .solution-text {
+            font-size: 14px;
+            color: #555;
+            line-height: 1.7;
+            margin: 0 0 16px 0;
+          }
+        }
+        
+        .problem-data {
+          display: grid;
+          grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+          gap: 12px;
+          
+          .data-point {
+            background: #fafafa;
+            border: 1px solid #e0e0e0;
+            border-radius: 10px;
+            padding: 12px;
+            
+            .data-label {
+              font-size: 12px;
+              color: #999;
+              margin-right: 4px;
+            }
+            
+            .data-value {
+              font-size: 15px;
+              font-weight: 700;
+              color: #1a1a1a;
+              
+              &.warning {
+                color: #ff9500;
+              }
+            }
+          }
+        }
+        
+        .actions-list {
+          list-style: none;
+          padding: 0;
+          margin: 0;
+          
+          .action-item {
+            display: flex;
+            gap: 12px;
+            margin-bottom: 12px;
+            padding: 12px;
+            background: #f8f9fa;
+            border-radius: 10px;
+            border-left: 3px solid #007aff;
+            
+            &:last-child {
+              margin-bottom: 0;
+            }
+            
+            .action-step {
+              font-weight: 700;
+              color: #007aff;
+              flex-shrink: 0;
+            }
+            
+            .action-text {
+              font-size: 14px;
+              color: #555;
+              line-height: 1.6;
+            }
+          }
+        }
+        
+        .suggestion-references {
+          margin-top: 16px;
+          padding-top: 16px;
+          border-top: 1px solid #f0f0f0;
+          
+          .references-label {
+            font-size: 13px;
+            color: #666;
+            margin-right: 8px;
+          }
+          
+          .reference-tag {
+            display: inline-block;
+            padding: 4px 10px;
+            background: #e3f2fd;
+            color: #007aff;
+            border-radius: 12px;
+            font-size: 12px;
+            font-weight: 600;
+            margin-right: 8px;
+            margin-top: 6px;
+          }
+        }
+      }
+      
+      .suggestion-footer {
+        display: flex;
+        gap: 12px;
+        padding: 16px 24px;
+        background: #fafafa;
+        border-top: 1px solid #f0f0f0;
+        
+        .action-btn {
+          padding: 10px 20px;
+          border: none;
+          border-radius: 10px;
+          font-size: 14px;
+          font-weight: 600;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          
+          &.accept {
+            background: linear-gradient(135deg, #34c759 0%, #28a745 100%);
+            color: white;
+            
+            &:hover {
+              transform: translateY(-2px);
+              box-shadow: 0 4px 12px rgba(52, 199, 89, 0.4);
+            }
+          }
+          
+          &.detail {
+            background: #f0f0f0;
+            color: #333;
+            
+            &:hover {
+              background: #e0e0e0;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .empty-state-large {
+    text-align: center;
+    padding: 80px 20px;
+    
+    .empty-icon {
+      font-size: 64px;
+      margin-bottom: 20px;
+    }
+    
+    .empty-title {
+      font-size: 20px;
+      font-weight: 700;
+      color: #1a1a1a;
+      margin: 0 0 12px 0;
+    }
+    
+    .empty-desc {
+      font-size: 15px;
+      color: #999;
+      margin: 0;
+    }
+  }
+  
+  .suggestions-stats {
+    background: white;
+    border: 1px solid #e0e0e0;
+    border-radius: 16px;
+    padding: 24px;
+    
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: 20px;
+      
+      .stat-card {
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        padding: 20px;
+        background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+        border: 1px solid #e0e0e0;
+        border-radius: 12px;
+        transition: all 0.3s ease;
+        
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+        }
+        
+        .stat-icon {
+          font-size: 32px;
+        }
+        
+        .stat-content {
+          .stat-value {
+            font-size: 28px;
+            font-weight: 700;
+            color: #1a1a1a;
+            margin: 0 0 4px 0;
+          }
+          
+          .stat-label {
+            font-size: 13px;
+            color: #999;
+            margin: 0;
+          }
+        }
+      }
+    }
+  }
+}
+

+ 608 - 0
src/app/pages/designer/project-detail/project-review-styles.scss

@@ -0,0 +1,608 @@
+/* ==================== 项目复盘模块样式 ==================== */
+.project-review-section {
+  width: 100%;
+  background: white;
+  border-radius: 20px;
+  padding: 32px;
+  margin-top: 24px;
+  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
+  
+  .review-section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: 28px;
+    padding-bottom: 20px;
+    border-bottom: 2px solid rgba(0, 122, 255, 0.1);
+    
+    .header-left {
+      flex: 1;
+      
+      .section-title {
+        font-size: 24px;
+        font-weight: 700;
+        color: #1a1a1a;
+        margin: 0 0 8px 0;
+        display: flex;
+        align-items: center;
+        gap: 10px;
+      }
+      
+      .section-subtitle {
+        font-size: 14px;
+        color: #666;
+        margin: 0;
+        line-height: 1.6;
+      }
+    }
+    
+    .header-right {
+      display: flex;
+      gap: 12px;
+      
+      button {
+        padding: 10px 20px;
+        border-radius: 10px;
+        border: none;
+        font-size: 14px;
+        font-weight: 600;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        
+        &.generate-review-btn {
+          background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
+          color: white;
+          
+          &:hover:not(:disabled) {
+            transform: translateY(-2px);
+            box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
+          }
+          
+          &:disabled {
+            opacity: 0.6;
+            cursor: not-allowed;
+          }
+        }
+        
+        &.export-review-btn {
+          background: #f0f0f0;
+          color: #333;
+          
+          &:hover {
+            background: #e0e0e0;
+          }
+        }
+      }
+    }
+  }
+  
+  .review-tabs {
+    display: flex;
+    gap: 12px;
+    margin-bottom: 32px;
+    border-bottom: 2px solid #f0f0f0;
+    
+    .review-tab {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 14px 24px;
+      border: none;
+      background: transparent;
+      color: #666;
+      font-size: 15px;
+      font-weight: 600;
+      cursor: pointer;
+      position: relative;
+      transition: all 0.3s ease;
+      border-bottom: 3px solid transparent;
+      margin-bottom: -2px;
+      
+      .tab-icon {
+        font-size: 18px;
+      }
+      
+      .tab-label {
+        font-weight: 500;
+      }
+      
+      &:hover {
+        color: #007aff;
+        background: rgba(0, 122, 255, 0.05);
+      }
+      
+      &.active {
+        color: #007aff;
+        border-bottom-color: #007aff;
+        background: rgba(0, 122, 255, 0.08);
+      }
+    }
+  }
+  
+  .review-content-area {
+    min-height: 500px;
+    max-height: 800px;
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding-right: 8px;
+    
+    /* 自定义滚动条 */
+    &::-webkit-scrollbar {
+      width: 10px;
+    }
+    
+    &::-webkit-scrollbar-track {
+      background: #f5f5f5;
+      border-radius: 5px;
+    }
+    
+    &::-webkit-scrollbar-thumb {
+      background: #c0c0c0;
+      border-radius: 5px;
+      
+      &:hover {
+        background: #a0a0a0;
+      }
+    }
+  }
+}
+
+/* SOP执行数据样式 */
+.sop-data-content {
+  .sop-metrics-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+    gap: 20px;
+    margin-bottom: 40px;
+    
+    .metric-card {
+      background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+      border: 1px solid #e0e0e0;
+      border-radius: 16px;
+      padding: 24px;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: translateY(-4px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+        border-color: #007aff;
+      }
+      
+      .metric-header {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        margin-bottom: 16px;
+        
+        .metric-icon {
+          font-size: 24px;
+        }
+        
+        .metric-title {
+          font-size: 14px;
+          font-weight: 600;
+          color: #666;
+          margin: 0;
+        }
+      }
+      
+      .metric-value-large {
+        font-size: 36px;
+        font-weight: 700;
+        color: #1a1a1a;
+        margin-bottom: 12px;
+        
+        .unit {
+          font-size: 18px;
+          font-weight: 400;
+          color: #999;
+          margin-left: 4px;
+        }
+      }
+      
+      .metric-footer {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: 8px;
+        font-size: 13px;
+        
+        .metric-label {
+          color: #999;
+        }
+        
+        .metric-benchmark {
+          color: #666;
+          font-weight: 500;
+        }
+        
+        .metric-status {
+          padding: 4px 10px;
+          border-radius: 6px;
+          font-weight: 600;
+          font-size: 12px;
+          
+          &.good {
+            background: #e7f4e4;
+            color: #34c759;
+          }
+          
+          &.warning {
+            background: #fff3cd;
+            color: #ff9500;
+          }
+        }
+        
+        .satisfaction-stars {
+          display: flex;
+          gap: 4px;
+          
+          .star {
+            font-size: 16px;
+            color: #e0e0e0;
+            
+            &.filled {
+              color: #ffc107;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .sop-stages-section {
+    margin-bottom: 40px;
+    
+    .subsection-title {
+      font-size: 18px;
+      font-weight: 700;
+      color: #1a1a1a;
+      margin: 0 0 24px 0;
+      padding-bottom: 12px;
+      border-bottom: 2px solid #f0f0f0;
+    }
+    
+    .stages-timeline {
+      display: flex;
+      flex-direction: column;
+      gap: 0;
+      
+      .stage-timeline-item {
+        display: flex;
+        gap: 20px;
+        position: relative;
+        
+        .stage-indicator {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          padding-top: 8px;
+          
+          .stage-dot {
+            width: 16px;
+            height: 16px;
+            border-radius: 50%;
+            background: #e0e0e0;
+            border: 3px solid #fff;
+            box-shadow: 0 0 0 2px #e0e0e0;
+            flex-shrink: 0;
+            z-index: 1;
+          }
+          
+          .stage-line {
+            width: 2px;
+            flex: 1;
+            background: #e0e0e0;
+            min-height: 40px;
+          }
+        }
+        
+        &.completed .stage-indicator .stage-dot {
+          background: #34c759;
+          box-shadow: 0 0 0 2px #34c759;
+        }
+        
+        &.ongoing .stage-indicator .stage-dot {
+          background: #007aff;
+          box-shadow: 0 0 0 2px #007aff;
+          animation: pulse 2s infinite;
+        }
+        
+        &.delayed .stage-indicator .stage-dot {
+          background: #ff9500;
+          box-shadow: 0 0 0 2px #ff9500;
+        }
+        
+        .stage-content-card {
+          flex: 1;
+          background: white;
+          border: 1px solid #e0e0e0;
+          border-radius: 12px;
+          padding: 20px;
+          margin-bottom: 20px;
+          transition: all 0.3s ease;
+          
+          &:hover {
+            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+            border-color: #007aff;
+          }
+          
+          .stage-card-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 16px;
+            
+            .stage-name {
+              font-size: 16px;
+              font-weight: 700;
+              color: #1a1a1a;
+              margin: 0;
+            }
+            
+            .stage-status-badge {
+              padding: 6px 14px;
+              border-radius: 20px;
+              font-size: 12px;
+              font-weight: 600;
+              
+              &.completed {
+                background: #e7f4e4;
+                color: #34c759;
+              }
+              
+              &.ongoing {
+                background: #e3f2fd;
+                color: #007aff;
+              }
+              
+              &.delayed {
+                background: #fff3cd;
+                color: #ff9500;
+              }
+            }
+          }
+          
+          .stage-metrics {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+            gap: 16px;
+            margin-bottom: 12px;
+            
+            .stage-metric-item {
+              display: flex;
+              flex-direction: column;
+              
+              .label {
+                font-size: 13px;
+                color: #999;
+                margin-bottom: 4px;
+              }
+              
+              .value {
+                font-size: 16px;
+                font-weight: 600;
+                color: #1a1a1a;
+                
+                &.warning {
+                  color: #ff9500;
+                }
+                
+                &.score {
+                  &.excellent {
+                    color: #34c759;
+                  }
+                  
+                  &.good {
+                    color: #007aff;
+                  }
+                  
+                  &.fair {
+                    color: #ff9500;
+                  }
+                  
+                  &.poor {
+                    color: #ff3b30;
+                  }
+                }
+              }
+            }
+          }
+          
+          .stage-issues {
+            background: #fff3cd;
+            border-left: 4px solid #ff9500;
+            padding: 12px;
+            border-radius: 8px;
+            
+            .issues-label {
+              font-weight: 600;
+              color: #ff9500;
+              margin-right: 8px;
+            }
+            
+            .issues-text {
+              font-size: 14px;
+              color: #666;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .sop-charts-section {
+    .charts-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+      gap: 24px;
+      
+      .chart-card {
+        background: white;
+        border: 1px solid #e0e0e0;
+        border-radius: 16px;
+        padding: 24px;
+        
+        .chart-title {
+          font-size: 16px;
+          font-weight: 700;
+          color: #1a1a1a;
+          margin: 0 0 20px 0;
+        }
+        
+        .chart-placeholder {
+          .bar-chart {
+            display: flex;
+            justify-content: space-around;
+            align-items: flex-end;
+            gap: 12px;
+            height: 200px;
+            padding: 0 10px;
+            
+            .bar-group {
+              flex: 1;
+              display: flex;
+              flex-direction: column;
+              align-items: center;
+              gap: 8px;
+              
+              .bar-label {
+                font-size: 12px;
+                color: #666;
+                text-align: center;
+                word-wrap: break-word;
+                max-width: 100%;
+              }
+              
+              .bars {
+                display: flex;
+                gap: 4px;
+                align-items: flex-end;
+                flex: 1;
+                width: 100%;
+                
+                .bar {
+                  flex: 1;
+                  min-height: 20px;
+                  border-radius: 6px 6px 0 0;
+                  position: relative;
+                  display: flex;
+                  justify-content: center;
+                  padding-top: 6px;
+                  transition: all 0.3s ease;
+                  
+                  &.planned {
+                    background: linear-gradient(to top, #4caf50, #81c784);
+                  }
+                  
+                  &.actual {
+                    background: linear-gradient(to top, #2196f3, #64b5f6);
+                  }
+                  
+                  .bar-value {
+                    font-size: 11px;
+                    font-weight: 600;
+                    color: white;
+                  }
+                  
+                  &:hover {
+                    transform: scale(1.05);
+                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+                  }
+                }
+              }
+            }
+          }
+          
+          .chart-legend {
+            display: flex;
+            justify-content: center;
+            gap: 24px;
+            margin-top: 20px;
+            
+            .legend-item {
+              display: flex;
+              align-items: center;
+              gap: 8px;
+              font-size: 13px;
+              color: #666;
+              
+              .legend-color {
+                width: 16px;
+                height: 16px;
+                border-radius: 4px;
+                
+                &.planned {
+                  background: linear-gradient(to bottom, #4caf50, #81c784);
+                }
+                
+                &.actual {
+                  background: linear-gradient(to bottom, #2196f3, #64b5f6);
+                }
+              }
+            }
+          }
+          
+          &.radar-chart {
+            height: 200px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            
+            .radar-info {
+              text-align: center;
+              
+              p {
+                font-size: 16px;
+                color: #666;
+                margin-bottom: 16px;
+                
+                strong {
+                  font-size: 20px;
+                  color: #007aff;
+                }
+              }
+              
+              .score-items {
+                display: flex;
+                flex-direction: column;
+                gap: 8px;
+                
+                .score-item {
+                  display: flex;
+                  justify-content: space-between;
+                  font-size: 14px;
+                  
+                  .stage-label {
+                    color: #666;
+                  }
+                  
+                  .stage-score {
+                    font-weight: 600;
+                    
+                    &.excellent { color: #34c759; }
+                    &.good { color: #007aff; }
+                    &.fair { color: #ff9500; }
+                    &.poor { color: #ff3b30; }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+@keyframes pulse {
+  0%, 100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  50% {
+    transform: scale(1.1);
+    opacity: 0.8;
+  }
+}
+