Răsfoiți Sursa

feat:shouhouandanli

徐福静0235668 1 zi în urmă
părinte
comite
19d7e75991
41 a modificat fișierele cu 10089 adăugiri și 2262 ștergeri
  1. 53 39
      src/app/pages/customer-service/customer-service-layout/customer-service-layout.scss
  2. 32 3
      src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.scss
  3. 104 37
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.scss
  4. 32 3
      src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.scss
  5. 323 254
      src/app/pages/customer-service/project-detail/project-detail.html
  6. 258 0
      src/app/pages/customer-service/project-detail/project-detail.scss
  7. 295 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav-styles.scss
  8. 11 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.html
  9. 145 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.scss
  10. 34 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.ts
  11. 20 0
      src/app/pages/designer/project-detail/debug-styles.scss
  12. 461 338
      src/app/pages/designer/project-detail/project-detail.html
  13. 1163 1379
      src/app/pages/designer/project-detail/project-detail.scss
  14. 306 116
      src/app/pages/designer/project-detail/project-detail.ts
  15. 15 7
      src/app/pages/finance/project-records/project-records.html
  16. 85 24
      src/app/pages/finance/project-records/project-records.scss
  17. 18 9
      src/app/pages/finance/reconciliation/reconciliation.scss
  18. 13 8
      src/app/pages/finance/reports/reports.scss
  19. 20 6
      src/app/pages/hr/dashboard/dashboard.scss
  20. 22 0
      src/app/shared/components/complaint-card/complaint-card.html
  21. 16 0
      src/app/shared/components/complaint-card/complaint-card.scss
  22. 22 0
      src/app/shared/components/complaint-card/complaint-card.ts
  23. 19 0
      src/app/shared/components/customer-review-card/customer-review-card.html
  24. 15 0
      src/app/shared/components/customer-review-card/customer-review-card.scss
  25. 14 0
      src/app/shared/components/customer-review-card/customer-review-card.ts
  26. 53 39
      src/app/shared/components/designer-nav/designer-nav.scss
  27. 105 0
      src/app/shared/components/order-creation-card/order-creation-card.html
  28. 7 0
      src/app/shared/components/order-creation-card/order-creation-card.scss
  29. 73 0
      src/app/shared/components/order-creation-card/order-creation-card.ts
  30. 884 0
      src/app/shared/components/proposal-confirm-card/proposal-confirm-card.html
  31. 1676 0
      src/app/shared/components/proposal-confirm-card/proposal-confirm-card.scss
  32. 1252 0
      src/app/shared/components/proposal-confirm-card/proposal-confirm-card.ts
  33. 600 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card.html
  34. 1063 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card.scss
  35. 799 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card.ts
  36. 12 0
      src/app/shared/components/requirements-talk-card/requirements-talk-card.html
  37. 11 0
      src/app/shared/components/requirements-talk-card/requirements-talk-card.scss
  38. 13 0
      src/app/shared/components/requirements-talk-card/requirements-talk-card.ts
  39. 17 0
      src/app/shared/components/settlement-card/settlement-card.html
  40. 14 0
      src/app/shared/components/settlement-card/settlement-card.scss
  41. 14 0
      src/app/shared/components/settlement-card/settlement-card.ts

+ 53 - 39
src/app/pages/customer-service/customer-service-layout/customer-service-layout.scss

@@ -104,52 +104,66 @@ $transition: all 0.3s ease;
     display: flex;
     align-items: center;
     gap: 16px;
-  }
 
-  .notification-btn {
-    position: relative;
-    background: none;
-    border: none;
-    cursor: pointer;
-    color: $text-secondary;
-    padding: 8px;
-    transition: $transition;
+    .notification-btn {
+      position: relative;
+      background: none;
+      border: none;
+      cursor: pointer;
+      color: $text-secondary;
+      padding: 8px;
+      border-radius: 8px;
+      transition: all 0.2s ease-in-out;
+      white-space: nowrap;
 
-    &:hover {
-      color: $primary-color;
-    }
+      &:hover {
+        color: $primary-color;
+        background-color: rgba(22, 93, 255, 0.08);
+        transform: translateY(-1px);
+      }
 
-    .notification-badge {
-      position: absolute;
-      top: 2px;
-      right: 2px;
-      background-color: $danger-color;
-      color: white;
-      font-size: 10px;
-      font-weight: 500;
-      padding: 2px 6px;
-      border-radius: 10px;
-      min-width: 18px;
-      text-align: center;
-    }
-  }
+      &:active {
+        transform: translateY(0);
+        background-color: rgba(22, 93, 255, 0.12);
+      }
 
-  .user-profile {
-    display: flex;
-    align-items: center;
-    gap: 8px;
+      &:focus {
+        outline: 2px solid rgba(22, 93, 255, 0.3);
+        outline-offset: 2px;
+      }
 
-    .user-avatar {
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      object-fit: cover;
+      .notification-badge {
+        position: absolute;
+        top: 0;
+        right: 0;
+        background-color: $danger-color;
+        color: white;
+        font-size: 12px;
+        padding: 2px 6px;
+        border-radius: 10px;
+        min-width: 18px;
+        text-align: center;
+        box-shadow: 0 2px 4px rgba(245, 63, 63, 0.3);
+      }
     }
 
-    .user-name {
-      font-size: 14px;
-      font-weight: 500;
-      color: $text-primary;
+    .user-profile {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+
+      .user-avatar {
+        width: 36px;
+        height: 36px;
+        border-radius: 50%;
+        object-fit: cover;
+      }
+
+      .user-name {
+        font-size: 14px;
+        font-weight: 500;
+        color: $text-primary;
+      }
     }
   }
 }

+ 32 - 3
src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.scss

@@ -25,10 +25,39 @@
 }
 
 .ios-back-btn {
-  background: none;
-  border: none;
-  padding: 8px;
+  background-color: #F2F3F5;
+  color: #1D2129;
+  border: 1px solid #E5E6EB;
+  border-radius: 8px;
+  padding: 8px 16px;
+  font-size: 14px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.2s ease-in-out;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 40px;
+  white-space: nowrap;
   z-index: 11;
+  
+  &:hover {
+    background-color: #F7F8FA;
+    transform: translateY(-1px);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    border-color: rgba(22, 93, 255, 0.3);
+  }
+  
+  &:active {
+    transform: translateY(0);
+    background-color: #F2F3F5;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  }
+  
+  &:focus {
+    outline: 2px solid rgba(22, 93, 255, 0.3);
+    outline-offset: 2px;
+  }
 }
 
 .ios-content {

+ 104 - 37
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.scss

@@ -30,29 +30,37 @@
   -webkit-backdrop-filter: blur(20px);
 
   .ios-back-btn {
-    display: flex;
+    background-color: #F2F3F5;
+    color: #1D2129;
+    border: 1px solid #E5E6EB;
+    border-radius: 8px;
+    padding: 8px 16px;
+    font-size: 14px;
+    font-weight: 500;
+    cursor: pointer;
+    transition: all 0.2s ease-in-out;
+    display: inline-flex;
     align-items: center;
     justify-content: center;
-    width: 40px;
-    height: 40px;
-    border: none;
-    background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
-    border-radius: 12px;
-    color: #6c757d;
-    cursor: pointer;
-    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1), inset 0 1px 2px rgba(255, 255, 255, 0.8);
-
+    min-height: 40px;
+    white-space: nowrap;
+    
     &:hover {
-      background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
-      color: #495057;
-      transform: translateX(-2px);
-      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(255, 255, 255, 0.8);
+      background-color: #F7F8FA;
+      transform: translateY(-1px);
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      border-color: rgba(22, 93, 255, 0.3);
     }
-
+    
     &:active {
-      transform: translateX(-1px) scale(0.95);
-      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1), inset 0 1px 2px rgba(255, 255, 255, 0.8);
+      transform: translateY(0);
+      background-color: #F2F3F5;
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    }
+    
+    &:focus {
+      outline: 2px solid rgba(22, 93, 255, 0.3);
+      outline-offset: 2px;
     }
   }
 
@@ -88,29 +96,37 @@
   }
 
   .back-btn {
-    display: flex;
+    background-color: #F2F3F5;
+    color: #1D2129;
+    border: 1px solid #E5E6EB;
+    border-radius: 8px;
+    padding: 8px 16px;
+    font-size: 14px;
+    font-weight: 500;
+    cursor: pointer;
+    transition: all 0.2s ease-in-out;
+    display: inline-flex;
     align-items: center;
     justify-content: center;
-    width: 44px;
-    height: 44px;
-    border: none;
-    background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
-    border-radius: 12px;
-    color: #6c757d;
-    cursor: pointer;
-    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1), inset 0 1px 2px rgba(255, 255, 255, 0.8);
-
+    min-height: 40px;
+    white-space: nowrap;
+    
     &:hover {
-      background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
-      color: #495057;
-      transform: translateX(-2px);
-      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), inset 0 1px 2px rgba(255, 255, 255, 0.8);
+      background-color: #F7F8FA;
+      transform: translateY(-1px);
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      border-color: rgba(22, 93, 255, 0.3);
     }
-
+    
     &:active {
-      transform: translateX(-1px) scale(0.95);
-      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1), inset 0 1px 2px rgba(255, 255, 255, 0.8);
+      transform: translateY(0);
+      background-color: #F2F3F5;
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    }
+    
+    &:focus {
+      outline: 2px solid rgba(22, 93, 255, 0.3);
+      outline-offset: 2px;
     }
   }
 
@@ -328,9 +344,60 @@
       }
     }
   }
+.filter-chips {
+  display: flex;
+  gap: 12px;
+  flex-wrap: wrap;
+
+  .filter-chip {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 12px 18px;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+    border-radius: 20px;
+    color: #6c757d;
+    font-size: 14px;
+    font-weight: 500;
+    cursor: pointer;
+    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+    position: relative;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05), inset 0 1px 2px rgba(255, 255, 255, 0.8);
+
+    &:hover {
+      border-color: rgba(0, 122, 255, 0.3);
+      background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+      transform: translateY(-1px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.8);
+    }
+
+    &.active {
+      border-color: #007AFF;
+      background: linear-gradient(135deg, #007AFF 0%, #0062CC 100%);
+      color: #ffffff;
+      box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.2);
+
+      .chip-count {
+        background: rgba(255, 255, 255, 0.2);
+        color: #ffffff;
+      }
+    }
+
+    .chip-count {
+      background: #e2e8f0;
+      color: #64748b;
+      padding: 2px 8px;
+      border-radius: 10px;
+      font-size: 12px;
+      font-weight: 600;
+      min-width: 20px;
+      text-align: center;
+    }
+  }
 }
 
-// 咨询内容区域
+/* 咨询内容区域 */
 .consultation-content {
   padding: 24px;
   min-height: 400px;

+ 32 - 3
src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.scss

@@ -25,10 +25,39 @@
 }
 
 .ios-back-btn {
-  background: none;
-  border: none;
-  padding: 8px;
+  background-color: #F2F3F5;
+  color: #1D2129;
+  border: 1px solid #E5E6EB;
+  border-radius: 8px;
+  padding: 8px 16px;
+  font-size: 14px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.2s ease-in-out;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 40px;
+  white-space: nowrap;
   z-index: 11;
+  
+  &:hover {
+    background-color: #F7F8FA;
+    transform: translateY(-1px);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    border-color: rgba(22, 93, 255, 0.3);
+  }
+  
+  &:active {
+    transform: translateY(0);
+    background-color: #F2F3F5;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  }
+  
+  &:focus {
+    outline: 2px solid rgba(22, 93, 255, 0.3);
+    outline-offset: 2px;
+  }
 }
 
 .ios-content {

+ 323 - 254
src/app/pages/customer-service/project-detail/project-detail.html

@@ -2,7 +2,7 @@
 <div class="project-detail-container ios-style">
   <!-- 顶部导航/Header -->
     <header class="project-header ios-header">
-      <div class="header-content">
+      <div class="header-left">
         <div class="project-info">
           <h1 class="project-title">{{ project()?.name || '现代简约风格三居室设计' }}</h1>
           <div class="project-meta">
@@ -13,8 +13,11 @@
             <span class="project-date">最后更新:{{ formatDate(currentDate()) }}</span>
           </div>
         </div>
+      </div>
+      
+      <div class="header-right">
         <div class="header-actions">
-          <button class="secondary-btn btn-hover-effect">
+          <button class="action-btn secondary-btn btn-hover-effect">
             <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
               <polyline points="14 2 14 8 20 8"></polyline>
@@ -24,13 +27,20 @@
             </svg>
             <span>导出报告</span>
           </button>
-          <button class="primary-btn btn-hover-effect">
+          <button class="action-btn primary-btn btn-hover-effect">
             <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
               <polyline points="22 4 12 14.01 9 11.01"></polyline>
             </svg>
             <span>联系客户</span>
           </button>
+          <button class="action-btn back-btn" routerLink="/customer-service/dashboard">
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M19 12H5"></path>
+              <polyline points="12 19 5 12 12 5"></polyline>
+            </svg>
+            返回工作台
+          </button>
         </div>
       </div>
     </header>
@@ -64,15 +74,18 @@
         <div class="record-section">
           <h4>过往咨询记录</h4>
           <div class="consultation-list">
-            <div class="consultation-item" *ngFor="let record of consultationRecords()">
-              <div class="consultation-date">{{ formatDate(record.date) }}</div>
-              <div class="consultation-content">{{ record.content }}</div>
-              <div class="consultation-status"
-                   [class.status-processed]="record.status === '已解决' || record.status === '成功'"
-                   [class.status-processing]="record.status === '处理中'"
-                   [class.status-pending]="record.status === '待处理'">
-                {{ record.status }}
+            @for (record of consultationRecords(); track record.id || $index) {
+              <div class="consultation-item">
+                <div class="consultation-date">{{ formatDate(record.date) }}</div>
+                <div class="consultation-content">{{ record.content }}</div>
+                <div class="consultation-status"
+                     [class.status-processed]="record.status === '已解决' || record.status === '成功'"
+                     [class.status-processing]="record.status === '处理中'"
+                     [class.status-pending]="record.status === '待处理'">
+                  {{ record.status }}
+                </div>
               </div>
+            }
             </div>
           </div>
         </div>
@@ -81,11 +94,14 @@
         <div class="record-section">
           <h4>合作项目</h4>
           <div class="projects-list">
-            <div class="project-item" *ngFor="let proj of cooperationProjects()">
-              <div class="project-name">{{ proj.name }}</div>
-              <div class="project-period">{{ formatDate(proj.startDate) }} - {{ formatDate(proj.endDate) }}</div>
-              <div class="project-description">{{ proj.description }}</div>
-              <div class="project-status">{{ proj.status }}</div>
+            @for (proj of cooperationProjects(); track proj.id || $index) {
+              <div class="project-item">
+                <div class="project-name">{{ proj.name }}</div>
+                <div class="project-period">{{ formatDate(proj.startDate) }} - {{ formatDate(proj.endDate) }}</div>
+                <div class="project-description">{{ proj.description }}</div>
+                <div class="project-status">{{ proj.status }}</div>
+              </div>
+            }
             </div>
           </div>
         </div>
@@ -94,18 +110,24 @@
         <div class="record-section">
           <h4>历史反馈/评价</h4>
           <div class="feedback-list">
-            <div class="feedback-item" *ngFor="let feedback of historicalFeedbacks()">
-              <div class="feedback-date">{{ formatDate(feedback.date) }}</div>
-              <div class="feedback-rating">
-                <span *ngFor="let star of [1,2,3,4,5]">
-                  <i class="fa" [ngClass]="{ 'fa-star': star <= feedback.rating, 'fa-star-o': star > feedback.rating }"></i>
-                </span>
-              </div>
-              <div class="feedback-content">{{ feedback.content }}</div>
-              <div class="feedback-response" *ngIf="feedback.response">
-                <strong>回复:</strong>{{ feedback.response }}
+            @for (feedback of historicalFeedbacks(); track feedback.id || $index) {
+              <div class="feedback-item">
+                <div class="feedback-date">{{ formatDate(feedback.date) }}</div>
+                <div class="feedback-rating">
+                  @for (star of [1,2,3,4,5]; track star) {
+                    <span>
+                      <i class="fa" [ngClass]="{ 'fa-star': star <= feedback.rating, 'fa-star-o': star > feedback.rating }"></i>
+                    </span>
+                  }
+                </div>
+                <div class="feedback-content">{{ feedback.content }}</div>
+                @if (feedback.response) {
+                  <div class="feedback-response">
+                    <strong>回复:</strong>{{ feedback.response }}
+                  </div>
+                }
               </div>
-            </div>
+            }
           </div>
         </div>
       </div>
@@ -114,34 +136,48 @@
       <div class="card timeline-card">
         <h3 class="card-title">项目阶段时间轴</h3>
         <div class="project-timeline">
-          <div *ngFor="let stage of projectStages; index as i" class="timeline-item" [class.stage-completed]="stage.completed" [class.stage-in-progress]="stage.inProgress">
-            <div class="timeline-icon" [class.icon-completed]="stage.completed" [class.icon-in-progress]="stage.inProgress">
-              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                <circle cx="12" cy="12" r="9"></circle>
-                <path *ngIf="stage.completed" d="m5 12 5 5 10-10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
-                <circle *ngIf="stage.inProgress" cx="12" cy="12" r="5"></circle>
-              </svg>
-            </div>
-            <div class="timeline-line" *ngIf="i < projectStages.length - 1" [class.line-completed]="stage.completed && projectStages[i+1].completed"></div>
-            <div class="timeline-content">
-              <div class="timeline-header">
-                <h4 class="stage-title">{{ stage.name }}</h4>
-                <span class="stage-status">
-                  {{ stage.completed ? '已完成' : stage.inProgress ? '进行中' : '未开始' }}
-                </span>
+          @for (stage of projectStages; track $index; let i = $index) {
+            <div class="timeline-item" [class.stage-completed]="stage.completed" [class.stage-in-progress]="stage.inProgress">
+              <div class="timeline-icon" [class.icon-completed]="stage.completed" [class.icon-in-progress]="stage.inProgress">
+                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                  <circle cx="12" cy="12" r="9"></circle>
+                  @if (stage.completed) {
+                    <path d="m5 12 5 5 10-10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
+                  }
+                  @if (stage.inProgress) {
+                    <circle cx="12" cy="12" r="5"></circle>
+                  }
+                </svg>
               </div>
-              <div class="timeline-meta">
-                <div class="stage-dates">
-                  <span *ngIf="stage.startDate" class="date-item">开始:{{ formatDate(stage.startDate) }}</span>
-                  <span *ngIf="stage.endDate" class="date-item">完成:{{ formatDate(stage.endDate) }}</span>
+              @if (i < projectStages.length - 1) {
+                <div class="timeline-line" [class.line-completed]="stage.completed && projectStages[i+1].completed"></div>
+              }
+              <div class="timeline-content">
+                <div class="timeline-header">
+                  <h4 class="stage-title">{{ stage.name }}</h4>
+                  <span class="stage-status">
+                    {{ stage.completed ? '已完成' : stage.inProgress ? '进行中' : '未开始' }}
+                  </span>
                 </div>
-                <div class="stage-responsible">负责人:{{ stage.responsible || '未分配' }}</div>
-              </div>
-              <div class="stage-details" *ngIf="stage.details">
-                <p>{{ stage.details }}</p>
+                <div class="timeline-meta">
+                  <div class="stage-dates">
+                    @if (stage.startDate) {
+                      <span class="date-item">开始:{{ formatDate(stage.startDate) }}</span>
+                    }
+                    @if (stage.endDate) {
+                      <span class="date-item">完成:{{ formatDate(stage.endDate) }}</span>
+                    }
+                  </div>
+                  <div class="stage-responsible">负责人:{{ stage.responsible || '未分配' }}</div>
+                </div>
+                @if (stage.details) {
+                  <div class="stage-details">
+                    <p>{{ stage.details }}</p>
+                  </div>
+                }
               </div>
             </div>
-          </div>
+          }
         </div>
       </div>
 
@@ -185,21 +221,25 @@
         </div>
 
         <!-- 消息标签内容 -->
-        <div *ngIf="activeTab() === 'messages'" class="tab-content">
+        @if (activeTab() === 'messages') {
+        }
+          <div class="tab-content">
           <div class="messages-container">
             <div class="messages-list">
-              <div *ngFor="let message of messages()" class="message-item">
-                <div class="message-avatar">
-                  {{ message.sender.charAt(0) }}
-                </div>
-                <div class="message-content">
-                  <div class="message-header">
-                    <span class="message-sender">{{ message.sender }}</span>
-                    <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
+              @for (message of messages(); track $index) {
+                <div class="message-item">
+                  <div class="message-avatar">
+                    {{ message.sender.charAt(0) }}
+                  </div>
+                  <div class="message-content">
+                    <div class="message-header">
+                      <span class="message-sender">{{ message.sender }}</span>
+                      <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
+                    </div>
+                    <div class="message-text">{{ message.content }}</div>
                   </div>
-                  <div class="message-text">{{ message.content }}</div>
                 </div>
-              </div>
+              }
             </div>
             <div class="message-input-area">
               <textarea 
@@ -231,210 +271,235 @@
         </div>
 
         <!-- 概览标签内容 -->
-        <div *ngIf="activeTab() === 'overview'" class="tab-content">
-          <div class="overview-grid">
-            <!-- 客户信息卡片 -->
-            <div class="info-card">
-              <h4 class="card-title">客户信息</h4>
-              <div class="customer-info">
-                <div class="info-item">
-                  <label>客户姓名</label>
-                  <span>{{ project()?.customerName || '王先生' }}</span>
-                </div>
-                <div class="info-item">
-                  <label>联系方式</label>
-                  <span>138****5678</span>
-                </div>
-                <div class="info-item">
-                  <label>标签</label>
-                  <div class="tag-container">
-                    <span class="tag">朋友圈</span>
-                    <span class="tag">软装</span>
-                    <span class="tag">现代风格</span>
+        @if (activeTab() === 'overview') {
+          <div class="tab-content">
+            <div class="overview-grid">
+              <!-- 客户信息卡片 -->
+              <div class="info-card">
+                <h4 class="card-title">客户信息</h4>
+                <div class="customer-info">
+                  <div class="info-item">
+                    <label>客户姓名</label>
+                    <span>{{ project()?.customerName || '王先生' }}</span>
+                  </div>
+                  <div class="info-item">
+                    <label>联系方式</label>
+                    <span>138****5678</span>
+                  </div>
+                  <div class="info-item">
+                    <label>标签</label>
+                    <div class="tag-container">
+                      <span class="tag">朋友圈</span>
+                      <span class="tag">软装</span>
+                      <span class="tag">现代风格</span>
+                    </div>
+                  </div>
+                  <div class="info-item">
+                    <label>高优先级需求</label>
+                    <ul class="need-list">
+                      @for (need of project()?.highPriorityNeeds || ['客厅光线充足', '储物空间充足', '环保材料']; track $index) {
+                        <li>
+                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                            <polyline points="20 6 9 17 4 12"></polyline>
+                          </svg>
+                          {{ need }}
+                        </li>
+                      }
+                    </ul>
                   </div>
-                </div>
-                <div class="info-item">
-                  <label>高优先级需求</label>
-                  <ul class="need-list">
-                    <li *ngFor="let need of project()?.highPriorityNeeds || ['客厅光线充足', '储物空间充足', '环保材料']">
-                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                        <polyline points="20 6 9 17 4 12"></polyline>
-                      </svg>
-                      {{ need }}
-                    </li>
-                  </ul>
                 </div>
               </div>
-            </div>
 
-            <!-- 项目团队卡片 -->
-            <div class="info-card">
-              <h4 class="card-title">项目团队</h4>
-              <div class="team-info">
-                <div class="team-member">
-                  <div class="member-avatar" title="客服小李">IMG</div>
-                  <div class="member-details">
-                    <div class="member-name">客服小李</div>
-                    <div class="member-role">客户经理</div>
+              <!-- 项目团队卡片 -->
+              <div class="info-card">
+                <h4 class="card-title">项目团队</h4>
+                <div class="team-info">
+                  <div class="team-member">
+                    <div class="member-avatar" title="客服小李">IMG</div>
+                    <div class="member-details">
+                      <div class="member-name">客服小李</div>
+                      <div class="member-role">客户经理</div>
+                    </div>
+                    <button class="message-btn">
+                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                        <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
+                      </svg>
+                    </button>
                   </div>
-                  <button class="message-btn">
-                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                      <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-                    </svg>
-                  </button>
-                </div>
-                <div class="team-member">
-                  <div class="member-avatar" title="张设计师">IMG</div>
-                  <div class="member-details">
-                    <div class="member-name">张设计师</div>
-                    <div class="member-role">主设计师</div>
+                  <div class="team-member">
+                    <div class="member-avatar" title="张设计师">IMG</div>
+                    <div class="member-details">
+                      <div class="member-name">张设计师</div>
+                      <div class="member-role">主设计师</div>
+                    </div>
+                    <button class="message-btn">
+                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                        <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
+                      </svg>
+                    </button>
                   </div>
-                  <button class="message-btn">
-                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                      <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-                    </svg>
-                  </button>
                 </div>
               </div>
-            </div>
 
-            <!-- 最近反馈卡片 -->
-            <div class="info-card">
-              <h4 class="card-title">客户反馈</h4>
-              <div class="feedback-list">
-                <div *ngFor="let feedback of feedbacks()" class="feedback-item">
-                  <div class="feedback-item">
-                    <div class="feedback-header">
-                      <div class="feedback-author">{{ getFeedbackCustomerName(feedback) }}</div>
-                      <div class="feedback-rating">
-                        <span class="rating-stars">★★★★☆</span>
-                        <span class="rating-number">{{ getFeedbackRating(feedback) }}.0</span>
+              <!-- 最近反馈卡片 -->
+              <div class="info-card">
+                <h4 class="card-title">客户反馈</h4>
+                <div class="feedback-list">
+                  @for (feedback of feedbacks(); track feedback.id || $index) {
+                    <div class="feedback-item">
+                      <div class="feedback-header">
+                        <div class="feedback-author">{{ getFeedbackCustomerName(feedback) }}</div>
+                        <div class="feedback-rating">
+                          <span class="rating-stars">★★★★☆</span>
+                          <span class="rating-number">{{ getFeedbackRating(feedback) }}.0</span>
+                        </div>
+                      </div>
+                      <div class="feedback-content">{{ feedback?.content || '' }}</div>
+                      @if (feedback?.response) {
+                        <div class="feedback-response">
+                          <div class="response-label">客服回复:</div>
+                          <div class="response-text">{{ feedback.response }}</div>
+                        </div>
+                      }
+                      <div class="feedback-meta">
+                        <span class="feedback-date">{{ formatDate(feedback?.createdAt) }}</span>
+                        <span class="feedback-status" [class.status-processed]="feedback?.status === '已解决'" [class.status-pending]="feedback?.status === '待处理'" [class.status-processing]="feedback?.status === '处理中'">
+                          {{ feedback?.status || '未知状态' }}
+                        </span>
                       </div>
                     </div>
-                    <div class="feedback-content">{{ feedback?.content || '' }}</div>
-                    <div class="feedback-response" *ngIf="feedback?.response">
-                      <div class="response-label">客服回复:</div>
-                      <div class="response-text">{{ feedback.response }}</div>
-                    </div>
-                    <div class="feedback-meta">
-                      <span class="feedback-date">{{ formatDate(feedback?.createdAt) }}</span>
-                      <span class="feedback-status" [class.status-processed]="feedback?.status === '已解决'" [class.status-pending]="feedback?.status === '待处理'" [class.status-processing]="feedback?.status === '处理中'">
-                        {{ feedback?.status || '未知状态' }}
-                      </span>
-                    </div>
-                  </div>
+                  }
+                  @if (feedbacks().length > 0) {
+                    <button class="view-all-btn btn-hover-effect">查看全部反馈</button>
+                  }
                 </div>
-                <button class="view-all-btn btn-hover-effect" *ngIf="feedbacks().length > 0">查看全部反馈</button>
               </div>
             </div>
           </div>
-        </div>
+        }
 
         <!-- 里程碑标签内容 -->
-        <div *ngIf="activeTab() === 'milestones'" class="tab-content">
-          <div class="milestones-timeline">
-            <div *ngFor="let milestone of milestones(); index as i" class="milestone-item">
-              <div class="milestone-dot" [class.completed]="milestone.isCompleted"></div>
-              <div class="milestone-line" *ngIf="i < milestones().length - 1" [class.completed]="milestone.isCompleted && milestones()[i+1].isCompleted"></div>
-              <div class="milestone-content">
-                <div class="milestone-header">
-                  <h4 class="milestone-title">{{ milestone.title }}</h4>
-                  <span class="milestone-status" [class.status-completed]="milestone.isCompleted" [class.status-pending]="!milestone.isCompleted">
-                    {{ milestone.isCompleted ? '已完成' : '进行中' }}
-                  </span>
-                </div>
-                <p class="milestone-description">{{ milestone.description }}</p>
-                <div class="milestone-dates">
-                  <div class="date-item">
-                    <label>截止日期</label>
-                    <span>{{ formatDate(milestone.dueDate) }}</span>
-                  </div>
-                  <div class="date-item" *ngIf="milestone.completedDate">
-                    <label>完成日期</label>
-                    <span>{{ formatDate(milestone.completedDate) }}</span>
+        @if (activeTab() === 'milestones') {
+          <div class="tab-content">
+            <div class="milestones-timeline">
+              @for (milestone of milestones(); track milestone.id || $index; let i = $index) {
+                <div class="milestone-item">
+                  <div class="milestone-dot" [class.completed]="milestone.isCompleted"></div>
+                  @if (i < milestones().length - 1) {
+                    <div class="milestone-line" [class.completed]="milestone.isCompleted && milestones()[i+1].isCompleted"></div>
+                  }
+                  <div class="milestone-content">
+                    <div class="milestone-header">
+                      <h4 class="milestone-title">{{ milestone.title }}</h4>
+                      <span class="milestone-status" [class.status-completed]="milestone.isCompleted" [class.status-pending]="!milestone.isCompleted">
+                        {{ milestone.isCompleted ? '已完成' : '进行中' }}
+                      </span>
+                    </div>
+                    <p class="milestone-description">{{ milestone.description }}</p>
+                    <div class="milestone-dates">
+                      <div class="date-item">
+                        <label>截止日期</label>
+                        <span>{{ formatDate(milestone.dueDate) }}</span>
+                      </div>
+                      @if (milestone.completedDate) {
+                        <div class="date-item">
+                          <label>完成日期</label>
+                          <span>{{ formatDate(milestone.completedDate) }}</span>
+                        </div>
+                      }
+                    </div>
+                    @if (!milestone.isCompleted) {
+                      <div class="milestone-actions">
+                        <button class="primary-btn small btn-hover-effect" (click)="completeMilestone(milestone.id)">
+                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                            <polyline points="20 6 9 17 4 12"></polyline>
+                          </svg>
+                          <span>标记完成</span>
+                        </button>
+                      </div>
+                    }
                   </div>
                 </div>
-                <div class="milestone-actions" *ngIf="!milestone.isCompleted">
-                  <button class="primary-btn small btn-hover-effect" (click)="completeMilestone(milestone.id)">
-                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                      <polyline points="20 6 9 17 4 12"></polyline>
-                    </svg>
-                    <span>标记完成</span>
-                  </button>
-                </div>
-              </div>
+              }
             </div>
           </div>
-        </div>
+        }
 
         <!-- 任务标签内容 -->
-        <div *ngIf="activeTab() === 'tasks'" class="tab-content">
-          <div class="tasks-filter">
-            <div class="filter-options">
-              <button class="filter-btn active">全部任务</button>
-              <button class="filter-btn">进行中</button>
-              <button class="filter-btn">已完成</button>
-              <button class="filter-btn">逾期</button>
-            </div>
-            <div class="search-box">
-              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                <circle cx="11" cy="11" r="8"></circle>
-                <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
-              </svg>
-              <input type="text" placeholder="搜索任务...">
-            </div>
-          </div>
-          <div class="tasks-list">
-            <!-- 修复任务列表中的状态显示,确保安全访问 -->
-            <div *ngFor="let task of tasks()" class="task-item">
-              <div class="task-checkbox">
-                <input type="checkbox" [checked]="task.isCompleted" (change)="task.isCompleted ? null : completeTask(task.id)">
+        @if (activeTab() === 'tasks') {
+          <div class="tab-content">
+            <div class="tasks-filter">
+              <div class="filter-options">
+                <button class="filter-btn active">全部任务</button>
+                <button class="filter-btn">进行中</button>
+                <button class="filter-btn">已完成</button>
+                <button class="filter-btn">逾期</button>
               </div>
-              <div class="task-content">
-                <h4 class="task-title" [class.completed]="task.isCompleted">{{ task.title || '未命名任务' }}</h4>
-                <p class="task-description">{{ task.description || '' }}</p>
-                <div class="task-meta">
-                  <span class="task-assignee">
-                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                      <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
-                      <circle cx="9" cy="7" r="4"></circle>
-                      <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
-                      <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
-                    </svg>
-                    {{ task.assignee || '未分配' }}
-                  </span>
-                  <span class="task-deadline" [class.overdue]="task.isOverdue">
-                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                      <circle cx="12" cy="12" r="10"></circle>
-                      <polyline points="12 6 12 12 16 14"></polyline>
-                    </svg>
-                    {{ formatDate(task.deadline) }}
-                  </span>
-                  <span class="task-priority" [class.priority-high]="task.priority === 'high'" [class.priority-medium]="task.priority === 'medium'" [class.priority-low]="task.priority === 'low'">
-                    {{ task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低' }}
-                  </span>
-                </div>
+              <div class="search-box">
+                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                  <circle cx="11" cy="11" r="8"></circle>
+                  <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
+                </svg>
+                <input type="text" placeholder="搜索任务...">
               </div>
             </div>
+            <div class="tasks-list">
+              <!-- 修复任务列表中的状态显示,确保安全访问 -->
+              @for (task of tasks(); track task.id || $index) {
+                <div class="task-item">
+                  <div class="task-checkbox">
+                    <input type="checkbox" [checked]="task.isCompleted" (change)="task.isCompleted ? null : completeTask(task.id)">
+                  </div>
+                  <div class="task-content">
+                    <h4 class="task-title" [class.completed]="task.isCompleted">{{ task.title || '未命名任务' }}</h4>
+                    <p class="task-description">{{ task.description || '' }}</p>
+                    <div class="task-meta">
+                      <span class="task-assignee">
+                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                          <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+                          <circle cx="9" cy="7" r="4"></circle>
+                          <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
+                          <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
+                        </svg>
+                        {{ task.assignee || '未分配' }}
+                      </span>
+                      <span class="task-deadline" [class.overdue]="task.isOverdue">
+                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                          <circle cx="12" cy="12" r="10"></circle>
+                          <polyline points="12 6 12 12 16 14"></polyline>
+                        </svg>
+                        {{ formatDate(task.deadline) }}
+                      </span>
+                      <span class="task-priority" [class.priority-high]="task.priority === 'high'" [class.priority-medium]="task.priority === 'medium'" [class.priority-low]="task.priority === 'low'">
+                        {{ task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低' }}
+                      </span>
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
           </div>
-        </div>
+        }
 
         <!-- 消息标签内容 -->
-        <div *ngIf="activeTab() === 'messages'" class="tab-content">
+        @if (activeTab() === 'messages') {
+          <div class="tab-content">
           <div class="messages-container">
             <div class="messages-list">
-              <div *ngFor="let message of messages()" class="message-item">
-                <div class="message-avatar">
-                  {{ message.sender.charAt(0) }}
-                </div>
-                <div class="message-content">
-                  <div class="message-header">
-                    <span class="message-sender">{{ message.sender }}</span>
-                    <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
+              @for (message of messages(); track $index) {
+                <div class="message-item">
+                  <div class="message-avatar">
+                    {{ message.sender.charAt(0) }}
+                  </div>
+                  <div class="message-content">
+                    <div class="message-header">
+                      <span class="message-sender">{{ message.sender }}</span>
+                      <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
+                    </div>
+                    <div class="message-text">{{ message.content }}</div>
                   </div>
-                  <div class="message-text">{{ message.content }}</div>
                 </div>
-              </div>
+              }
             </div>
       </div>
     </div>
@@ -461,18 +526,20 @@
       
       <!-- 聊天消息列表 -->
       <div class="wechat-messages" #wechatMessages>
-        <div *ngFor="let msg of wechatMessagesList" class="wechat-message-item">
-          <div class="message-avatar">
-            {{ msg.sender.charAt(0) }}
-          </div>
-          <div class="message-content">
-            <div class="message-header">
-              <span class="message-sender">{{ msg.sender }}</span>
-              <span class="message-time">{{ formatTime(msg.timestamp) }}</span>
+        @for (msg of wechatMessagesList; track $index) {
+          <div class="wechat-message-item">
+            <div class="message-avatar">
+              {{ msg.sender.charAt(0) }}
+            </div>
+            <div class="message-content">
+              <div class="message-header">
+                <span class="message-sender">{{ msg.sender }}</span>
+                <span class="message-time">{{ formatTime(msg.timestamp) }}</span>
+              </div>
+              <div class="message-text">{{ msg.content }}</div>
             </div>
-            <div class="message-text">{{ msg.content }}</div>
           </div>
-        </div>
+        }
       </div>
       
       <!-- 消息输入框 -->
@@ -504,7 +571,7 @@
         </button>
       </div>
     </div>
-  </div>
+  }
 
   <!-- 售后处理入口 (固定在底部) -->
   <div class="after-sales-actions ios-actions">
@@ -551,17 +618,19 @@
       </div>
     </div>
     <div class="files-list">
-      <div class="file-item" *ngFor="let f of files()">
-        <div class="file-icon">{{ f.type === 'image' ? 'IMG' : 'DOC' }}</div>
-        <div class="file-info">
-          <div class="file-name">{{ f.name }}</div>
-          <div class="file-meta">{{ f.size }} · 由 {{ f.uploadedBy }} 于 {{ formatDate(f.uploadedAt) }} 上传</div>
-        </div>
-        <div class="file-actions">
-          <button class="link" (click)="previewFile(f)">预览</button>
-          <button class="link" (click)="downloadFile(f)">下载</button>
+      @for (f of files(); track f.id || $index) {
+        <div class="file-item">
+          <div class="file-icon">{{ f.type === 'image' ? 'IMG' : 'DOC' }}</div>
+          <div class="file-info">
+            <div class="file-name">{{ f.name }}</div>
+            <div class="file-meta">{{ f.size }} · 由 {{ f.uploadedBy }} 于 {{ formatDate(f.uploadedAt) }} 上传</div>
+          </div>
+          <div class="file-actions">
+            <button class="link" (click)="previewFile(f)">预览</button>
+            <button class="link" (click)="downloadFile(f)">下载</button>
+          </div>
         </div>
-      </div>
+      }
     </div>
   </div>
 }

+ 258 - 0
src/app/pages/customer-service/project-detail/project-detail.scss

@@ -356,6 +356,156 @@ $bg-white: $background-primary;
   }
 }
 
+// iOS风格项目头部样式
+.project-header {
+  background: rgba(255, 255, 255, 0.95);
+  backdrop-filter: blur(20px);
+  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+  padding: 16px 24px;
+  position: sticky;
+  top: 0;
+  z-index: 100;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  min-height: 80px;
+
+  .header-left {
+    flex: 1;
+    
+    .project-info {
+      .project-title {
+        font-size: 24px;
+        font-weight: 600;
+        color: #1a1a1a;
+        margin: 0 0 8px 0;
+        line-height: 1.2;
+      }
+
+      .project-meta {
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        font-size: 14px;
+        color: #666;
+
+        .project-status {
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          font-size: 12px;
+
+          &.active { background: #e8f5e8; color: #2d7d32; }
+          &.pending { background: #fff3e0; color: #f57c00; }
+          &.completed { background: #e3f2fd; color: #1976d2; }
+        }
+      }
+    }
+  }
+
+  .header-right {
+    .header-actions {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+
+      .action-btn {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 10px 16px;
+        border: none;
+        border-radius: 8px;
+        font-size: 14px;
+        font-weight: 500;
+        cursor: pointer;
+        transition: all 0.2s ease;
+        text-decoration: none;
+        white-space: nowrap;
+
+        svg {
+          width: 16px;
+          height: 16px;
+          flex-shrink: 0;
+        }
+
+        &:hover {
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+        }
+
+        &:active {
+          transform: translateY(0);
+        }
+
+        &.primary-btn {
+          background: #007AFF;
+          color: white;
+          
+          &:hover {
+            background: #0056CC;
+          }
+        }
+
+        &.secondary-btn {
+          background: #f8f9fa;
+          color: #495057;
+          border: 1px solid #dee2e6;
+          
+          &:hover {
+            background: #e9ecef;
+            border-color: #adb5bd;
+          }
+        }
+
+        &.back-btn {
+          background: #28a745;
+          color: white;
+          
+          &:hover {
+            background: #218838;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .project-header {
+    flex-direction: column;
+    align-items: stretch;
+    gap: 16px;
+    padding: 16px;
+
+    .header-left {
+      .project-info {
+        .project-title {
+          font-size: 20px;
+        }
+
+        .project-meta {
+          flex-wrap: wrap;
+          gap: 8px;
+        }
+      }
+    }
+
+    .header-right {
+      .header-actions {
+        justify-content: flex-end;
+        flex-wrap: wrap;
+
+        .action-btn {
+          padding: 8px 12px;
+          font-size: 13px;
+        }
+      }
+    }
+  }
+}
+
 // 主要内容区域
 .main-content-area {
   display: flex;
@@ -2703,4 +2853,112 @@ $text-light: $text-tertiary;
       margin-top: 2px;
     }
   }
+}
+
+// 优化交付执行模块中的"选择图片"按钮样式
+.project-detail-container {
+  .upload-actions {
+    .secondary-btn {
+      // 增大按钮尺寸
+      padding: 12px 24px !important;
+      min-height: 44px;
+      font-size: 15px !important;
+      font-weight: 600 !important;
+      
+      // 高对比度配色方案
+      background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%) !important;
+      color: white !important;
+      border: 2px solid #357ABD !important;
+      border-radius: 12px !important;
+      
+      // 视觉层次感
+      box-shadow: 0 4px 12px rgba(74, 144, 226, 0.25), 
+                  0 2px 4px rgba(0, 0, 0, 0.1) !important;
+      
+      // 悬停效果
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
+      position: relative;
+      overflow: hidden;
+      
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+        transition: left 0.5s;
+      }
+      
+      &:hover {
+        background: linear-gradient(135deg, #5BA0F2 0%, #4A8ACD 100%) !important;
+        border-color: #4A8ACD !important;
+        box-shadow: 0 6px 20px rgba(74, 144, 226, 0.35), 
+                    0 4px 8px rgba(0, 0, 0, 0.15) !important;
+        transform: translateY(-2px) !important;
+        
+        &::before {
+          left: 100%;
+        }
+      }
+      
+      &:active {
+        transform: translateY(-1px) !important;
+        box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3), 
+                    0 2px 4px rgba(0, 0, 0, 0.1) !important;
+      }
+      
+      // 添加图标效果
+      &::after {
+        content: '📁';
+        margin-left: 8px;
+        font-size: 16px;
+      }
+    }
+  }
+}
+
+// 响应式设计优化
+@media (max-width: 768px) {
+  .project-detail-container {
+    .upload-actions {
+      flex-direction: column;
+      gap: 12px;
+      
+      .secondary-btn {
+        width: 100% !important;
+        padding: 16px 20px !important;
+        font-size: 16px !important;
+        min-height: 48px;
+        
+        &::after {
+          font-size: 18px;
+        }
+      }
+      
+      .primary-btn {
+        width: 100%;
+        padding: 16px 20px;
+        font-size: 16px;
+        min-height: 48px;
+      }
+    }
+  }
+}
+
+@media (max-width: 480px) {
+  .project-detail-container {
+    .upload-actions {
+      .secondary-btn {
+        padding: 18px 24px !important;
+        font-size: 17px !important;
+        min-height: 52px;
+        
+        &::after {
+          font-size: 20px;
+        }
+      }
+    }
+  }
 }

+ 295 - 0
src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav-styles.scss

@@ -0,0 +1,295 @@
+@use '../../../../../shared/styles/_ios-theme.scss' as *;
+
+// 项目人员标签页样式
+.members-tab-content {
+  .main-content-layout {
+    display: grid;
+    grid-template-columns: auto 1fr;
+    gap: $ios-spacing-lg;
+    align-items: start;
+  }
+
+  .members-content {
+    background: $ios-background;
+    border-radius: $ios-radius-lg;
+    padding: $ios-spacing-lg;
+    box-shadow: $ios-shadow-sm;
+  }
+
+  .members-header {
+    margin-bottom: $ios-spacing-lg;
+    
+    h2 {
+      color: $ios-text-primary;
+      font-size: $ios-font-size-lg;
+      font-weight: 600;
+      margin: 0 0 $ios-spacing-xs 0;
+    }
+
+    .members-count {
+      color: $ios-text-secondary;
+      font-size: $ios-font-size-sm;
+      margin: 0;
+    }
+  }
+
+  .members-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+    gap: $ios-spacing-md;
+  }
+
+  .member-card {
+    background: $ios-background;
+    border: 1px solid $ios-border;
+    border-radius: $ios-radius-md;
+    padding: $ios-spacing-md;
+    transition: all 0.2s ease;
+
+    &:hover {
+      box-shadow: $ios-shadow-md;
+      transform: translateY(-2px);
+    }
+
+    .member-avatar {
+      width: 60px;
+      height: 60px;
+      border-radius: 50%;
+      margin-bottom: $ios-spacing-md;
+      object-fit: cover;
+      border: 2px solid $ios-border;
+    }
+
+    .member-info {
+      text-align: center;
+
+      .member-name {
+        color: $ios-text-primary;
+        font-size: $ios-font-size-md;
+        font-weight: 600;
+        margin: 0 0 $ios-spacing-xs 0;
+      }
+
+      .member-role {
+        color: $ios-text-secondary;
+        font-size: $ios-font-size-sm;
+        margin: 0 0 $ios-spacing-sm 0;
+      }
+
+      .member-stats {
+        display: flex;
+        justify-content: space-between;
+        margin-top: $ios-spacing-sm;
+
+        .stat-item {
+          text-align: center;
+          flex: 1;
+
+          .stat-value {
+            color: $ios-text-secondary;
+            font-size: $ios-font-size-xs;
+            margin: 0;
+          }
+
+          .stat-label {
+            background: $ios-border;
+            color: white;
+            font-size: $ios-font-size-xs;
+            padding: 2px 6px;
+            border-radius: 10px;
+            margin-top: 2px;
+          }
+
+          &.active .stat-label {
+            background: $ios-primary;
+          }
+        }
+      }
+    }
+
+    .member-actions {
+      margin-top: $ios-spacing-md;
+      display: flex;
+      gap: $ios-spacing-sm;
+
+      .action-btn {
+        flex: 1;
+        padding: $ios-spacing-xs $ios-spacing-sm;
+        border: none;
+        border-radius: $ios-radius-sm;
+        font-size: $ios-font-size-xs;
+        cursor: pointer;
+        transition: all 0.2s ease;
+
+        &.primary {
+          background: $ios-primary;
+          color: white;
+
+          &:hover {
+            opacity: 0.9;
+          }
+        }
+
+        &.secondary {
+          background: $ios-background;
+          border: 1px solid $ios-border;
+          color: $ios-text-primary;
+
+          &:hover {
+            background: $ios-background-secondary;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 垂直导航栏样式
+.vertical-nav-container {
+  .nav-section {
+    margin-bottom: $ios-spacing-md; // 减小节间距
+
+    .section-title {
+      color: $ios-text-secondary;
+      font-size: 10px; // 减小标题字体
+      font-weight: 600;
+      text-transform: uppercase;
+      letter-spacing: 0.3px; // 减小字母间距
+      margin: 0 0 $ios-spacing-xs 0; // 减小下边距
+      padding: 0 $ios-spacing-sm; // 减小内边距
+    }
+
+    .nav-items {
+      list-style: none;
+      padding: 0;
+      margin: 0;
+
+      .nav-item {
+        margin-bottom: 2px; // 减小项间距
+
+        .nav-link {
+          display: flex;
+          align-items: center;
+          padding: $ios-spacing-xs $ios-spacing-sm; // 减小内边距
+          color: $ios-text-primary;
+          text-decoration: none;
+          border-radius: $ios-radius-sm; // 减小圆角
+          transition: all 0.2s ease;
+          font-size: $ios-font-size-xs; // 减小字体
+          line-height: 1.2; // 减小行高
+
+          &:hover {
+            background: $ios-background-secondary;
+          }
+
+          &.active {
+            background: $ios-primary;
+            color: white;
+            font-weight: 500;
+
+            .nav-icon {
+              color: white;
+            }
+          }
+
+          .nav-icon {
+            margin-right: $ios-spacing-xs; // 减小图标间距
+            font-size: 12px; // 减小图标尺寸
+            width: 12px;
+            text-align: center;
+            flex-shrink: 0;
+          }
+
+          .nav-text {
+            flex: 1;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+
+          .nav-badge {
+            background: $ios-border;
+            color: white;
+            font-size: 9px; // 减小徽标字体
+            padding: 1px 4px; // 减小徽标内边距
+            border-radius: 6px; // 减小徽标圆角
+            margin-left: auto;
+            min-width: 14px;
+            text-align: center;
+          }
+
+          &.active .nav-badge {
+            background: rgba(255, 255, 255, 0.2);
+          }
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .members-tab-content {
+    .main-content-layout {
+      grid-template-columns: 1fr;
+      gap: $ios-spacing-md;
+    }
+
+    .members-grid {
+      grid-template-columns: 1fr;
+    }
+  }
+
+  .vertical-nav-container {
+    .nav-section {
+      .nav-items {
+        .nav-item {
+          .nav-link {
+            padding: $ios-spacing-md;
+            font-size: $ios-font-size-md;
+
+            .nav-icon {
+              font-size: 18px;
+              width: 18px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// 加载状态
+.loading-state {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 200px;
+  color: $ios-text-secondary;
+  font-size: $ios-font-size-sm;
+}
+
+// 空状态
+.empty-state {
+  text-align: center;
+  padding: $ios-spacing-xxl;
+  color: $ios-text-secondary;
+
+  .empty-icon {
+    font-size: 48px;
+    margin-bottom: $ios-spacing-md;
+    opacity: 0.5;
+  }
+
+  .empty-title {
+    font-size: $ios-font-size-lg;
+    font-weight: 600;
+    margin: 0 0 $ios-spacing-sm 0;
+  }
+
+  .empty-description {
+    font-size: $ios-font-size-sm;
+    margin: 0;
+    line-height: 1.5;
+  }
+}

+ 11 - 0
src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.html

@@ -0,0 +1,11 @@
+<div class="vertical-nav">
+  @for (item of navItems; track item.id) {
+    <button 
+      class="nav-item"
+      [class.active]="isActive(item.id)"
+      (click)="onTabClick(item.id)"
+      type="button">
+      <span class="nav-text">{{ item.name }}</span>
+    </button>
+  }
+</div>

+ 145 - 0
src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.scss

@@ -0,0 +1,145 @@
+@use '../../../../../shared/styles/_ios-theme.scss' as *;
+
+.vertical-nav {
+  display: flex;
+  flex-direction: column;
+  gap: $ios-spacing-xs; // 减小间距
+  padding: $ios-spacing-sm; // 减小内边距
+  background: $ios-background;
+  border-radius: $ios-radius-md; // 减小圆角
+  border: 1px solid $ios-border;
+  box-shadow: $ios-shadow-sm; // 减小阴影
+  min-width: 160px; // 减小最小宽度
+  max-width: 200px; // 减小最大宽度
+  width: 100%;
+
+  // 当父容器有 horizontal-nav 类时,改为横向布局
+  :host(.horizontal-nav) & {
+    flex-direction: row;
+    gap: 0;
+    padding: 0;
+    background: transparent;
+    border: none;
+    box-shadow: none;
+    min-width: unset;
+    max-width: unset;
+    width: 100%;
+  }
+}
+
+.nav-item {
+  display: flex;
+  align-items: center;
+  padding: $ios-spacing-xs $ios-spacing-sm; // 减小内边距
+  border-radius: $ios-radius-sm; // 减小圆角
+  cursor: pointer;
+  transition: all 0.2s ease;
+  color: $ios-text-primary;
+  text-decoration: none;
+  font-size: $ios-font-size-sm; // 减小字体
+  font-weight: $ios-font-weight-regular;
+  line-height: 1.2; // 减小行高
+  background: transparent;
+  border: none;
+
+  &:hover {
+    background-color: $ios-background-secondary;
+  }
+
+  &.active {
+    background-color: $ios-primary;
+    color: white;
+    font-weight: $ios-font-weight-medium;
+  }
+
+  // 水平布局时的样式
+  :host(.horizontal-nav) & {
+    flex: 1;
+    justify-content: center;
+    padding: $ios-spacing-md $ios-spacing-lg;
+    border-radius: 0;
+    font-size: $ios-font-size-md;
+    font-weight: $ios-font-weight-medium;
+    line-height: 1.4;
+    border-bottom: 3px solid transparent;
+
+    &:first-child {
+      border-top-left-radius: $ios-radius-md;
+      border-bottom-left-radius: $ios-radius-md;
+    }
+
+    &:last-child {
+      border-top-right-radius: $ios-radius-md;
+      border-bottom-right-radius: $ios-radius-md;
+    }
+
+    &.active {
+      background-color: rgba(0, 122, 255, 0.1);
+      color: $ios-primary;
+      font-weight: $ios-font-weight-semibold;
+      border-bottom-color: $ios-primary;
+    }
+  }
+
+  .nav-icon {
+    margin-right: $ios-spacing-xs; // 减小图标间距
+    font-size: 14px; // 减小图标尺寸
+    width: 14px;
+    flex-shrink: 0;
+
+    // 水平布局时隐藏图标
+    :host(.horizontal-nav) & {
+      display: none;
+    }
+  }
+  
+  .nav-text {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    flex: 1;
+
+    // 水平布局时居中对齐
+    :host(.horizontal-nav) & {
+      text-align: center;
+    }
+  }
+}
+
+// 响应式适配
+@media (max-width: 1200px) {
+  .vertical-nav {
+    min-width: 140px;
+    max-width: 180px;
+  }
+  
+  .nav-item {
+    font-size: $ios-font-size-xs;
+    padding: 6px $ios-spacing-xs;
+    
+    .nav-icon {
+      font-size: 12px;
+      width: 12px;
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .vertical-nav {
+    min-width: 120px;
+    max-width: 160px;
+    padding: $ios-spacing-xs;
+    gap: 2px;
+  }
+
+  .nav-item {
+    padding: 4px $ios-spacing-xs;
+    font-size: 11px;
+    
+    .nav-icon {
+      font-size: 10px;
+      width: 10px;
+      margin-right: 4px;
+    }
+  }
+}

+ 34 - 0
src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.ts

@@ -0,0 +1,34 @@
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+export interface NavItem {
+  id: 'progress' | 'members' | 'files';
+  name: string;
+  icon?: string;
+}
+
+@Component({
+  selector: 'app-vertical-nav',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './vertical-nav.component.html',
+  styleUrls: ['./vertical-nav.component.scss']
+})
+export class VerticalNavComponent {
+  @Input() activeTab: 'progress' | 'members' | 'files' = 'progress';
+  @Input() navItems: NavItem[] = [
+    { id: 'progress', name: '项目进度' },
+    { id: 'members', name: '项目人员' },
+    { id: 'files', name: '项目文件' }
+  ];
+  
+  @Output() tabChange = new EventEmitter<'progress' | 'members' | 'files'>();
+
+  onTabClick(tabId: 'progress' | 'members' | 'files'): void {
+    this.tabChange.emit(tabId);
+  }
+
+  isActive(tabId: 'progress' | 'members' | 'files'): boolean {
+    return this.activeTab === tabId;
+  }
+}

+ 20 - 0
src/app/pages/designer/project-detail/debug-styles.scss

@@ -275,4 +275,24 @@
   line-height: 1.4;
   font-weight: 600;
   box-shadow: 0 4px 10px rgba(255, 77, 79, 0.25);
+}
+
+/* 阶段卡片横向排列(按板块的阶段数量自适应列数) */
+.stage-progress-container {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
+  gap: $ios-spacing-lg;
+  align-items: stretch; // 保证同一行的卡片等高
+}
+
+.vertical-stage-block {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+.vertical-stage-body {
+  display: flex;
+  flex-direction: column;
+  gap: $ios-spacing-md;
+  flex: 1 1 auto;
 }

+ 461 - 338
src/app/pages/designer/project-detail/project-detail.html

@@ -1,48 +1,115 @@
-<!-- 只展示修改处,未变更部分用占位注释表示 -->
+<!-- 只展示修改处,未变更部分用占位注释表示 -->
 <div class="project-detail-container designer-page">
   <!-- 项目标题栏 -->
   <div class="project-header card">
-    <div class="header-content">
-      <h1>项目详情</h1>
-      <div class="project-meta">
-        <span class="project-id">项目ID: {{ projectId }}</span>
-        @if (project) { <span class="project-status">{{ project.status }}</span> }
-        <!-- 紧急与异常徽标(使用控制流指令) -->
-        <!-- 保持已有@if 徽标逻辑不变 -->
+    <div class="header-left">
+      <div class="header-content">
+        <h1>项目详情</h1>
+        <div class="project-meta">
+          <span class="project-id">项目ID: {{ projectId }}</span>
+          @if (project) { <span class="project-status">{{ project.status }}</span> }
+          <!-- 紧急与异常徽标(使用控制流指令) -->
+          <!-- 保持已有@if 徽标逻辑不变 -->
+        </div>
       </div>
     </div>
-    <div class="header-actions">
-      <!-- 返回工作台按钮 -->
-      <button (click)="backToWorkbench()" class="back-btn">返回工作台</button>
-      
-      <!-- 切换项目下拉菜单 -->
-      <div class="project-switcher">
-        <button (click)="showDropdown = !showDropdown" class="switch-btn">切换项目</button>
-        @if (showDropdown) {
-          <div class="switch-dropdown" (click)="$event.stopPropagation()">
-            @for (p of projects; track p.id) {
-              <div (click)="switchProject(p.id); showDropdown = false" 
-                   [class.active]="p.id === projectId" 
-                   class="project-item">
-                <span class="project-name">{{ p.name }}</span>
-                <span class="project-status-badge" 
-                      [class.ongoing]="p.status === '进行中'" 
-                      [class.completed]="p.status === '已完成'" 
-                      [class.pending]="p.status === '待处理'">
-                  {{ p.status }}
-                </span>
-              </div>
-            }
-          </div>
-        }
+    
+    <div class="header-right">
+      <div class="header-actions">
+        <!-- 导出阶段报告 -->
+        <button (click)="exportProjectReport()" class="action-btn secondary-btn">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+            <polyline points="14 2 14 8 20 8"></polyline>
+            <line x1="16" y1="13" x2="8" y2="13"></line>
+            <line x1="16" y1="17" x2="8" y2="17"></line>
+          </svg>
+          导出报告
+        </button>
+        
+        <button (click)="generateReminderMessage()" class="action-btn stagnation-btn">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+            <circle cx="12" cy="12" r="10"></circle>
+            <polyline points="12 6 12 12 16 14"></polyline>
+          </svg>
+          设置停滞
+        </button>
+        
+        <!-- 切换项目下拉菜单 -->
+        <div class="project-switcher">
+          <button (click)="showDropdown = !showDropdown" class="action-btn switch-btn">
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
+              <polyline points="9 22 9 12 15 12 15 22"></polyline>
+            </svg>
+            切换项目
+            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="dropdown-icon">
+              <polyline points="6 9 12 15 18 9"></polyline>
+            </svg>
+          </button>
+          @if (showDropdown) {
+            <div class="switch-dropdown" (click)="$event.stopPropagation()">
+              @for (p of projects; track p.id) {
+                <div (click)="switchProject(p.id); showDropdown = false" 
+                     [class.active]="p.id === projectId" 
+                     class="project-item">
+                  <span class="project-name">{{ p.name }}</span>
+                  <span class="project-status-badge" 
+                        [class.ongoing]="p.status === '进行中'" 
+                        [class.completed]="p.status === '已完成'" 
+                        [class.pending]="p.status === '待处理'">
+                    {{ p.status }}
+                  </span>
+                </div>
+              }
+            </div>
+          }
+        </div>
+
+        <!-- 返回工作台按钮 -->
+        <button (click)="backToWorkbench()" class="action-btn back-btn primary">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+            <path d="M19 12H5"></path>
+            <polyline points="12 19 5 12 12 5"></polyline>
+          </svg>
+          返回工作台
+        </button>
       </div>
+    </div>
+</div>
 
-      <!-- 导出阶段报告 -->
-      <button (click)="exportProjectReport()" class="secondary-btn">导出阶段报告</button>
-      
-      <button (click)="generateReminderMessage()" class="stagnation-btn">设置停滞</button>
+<!-- 图片预览模态框 -->
+@if (showImagePreview) {
+  <div class="image-preview-modal" (click)="closeImagePreview()">
+    <div class="modal-backdrop"></div>
+    <div class="modal-content" (click)="$event.stopPropagation()">
+      <div class="modal-header">
+        <h3>{{ previewImageData?.name }}</h3>
+        <button class="close-btn" (click)="closeImagePreview()">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <line x1="18" y1="6" x2="6" y2="18"></line>
+            <line x1="6" y1="6" x2="18" y2="18"></line>
+          </svg>
+        </button>
+      </div>
+      <div class="modal-body">
+        <img [src]="previewImageData?.url" [alt]="previewImageData?.name" />
+      </div>
+      <div class="modal-footer">
+        <div class="image-info">
+          <span>文件大小: {{ previewImageData?.size }}</span>
+          @if (previewImageData?.reviewStatus) {
+            <span class="status-badge">{{ getImageReviewStatusText(previewImageData) }}</span>
+          }
+        </div>
+        <div class="modal-actions">
+          <button class="secondary-btn" (click)="downloadImage(previewImageData)">下载</button>
+          <button class="danger-btn" (click)="removeImageFromPreview()">删除</button>
+        </div>
+      </div>
     </div>
   </div>
+}
 
   <!-- 提醒消息弹窗 -->
   @if (reminderMessage) {
@@ -57,6 +124,15 @@
   <!-- 顶部导航标签页 -->
   <!-- 原有代码保留 -->
 
+  <!-- 水平导航栏 - 位于顶部导航栏下方 -->
+  <div class="horizontal-nav-container">
+    <app-vertical-nav 
+      [activeTab]="activeTab" 
+      (tabChange)="switchTab($event)"
+      class="horizontal-nav">
+    </app-vertical-nav>
+  </div>
+
   <div class="tab-content">
     <!-- 项目进度标签页 -->
     @if (isActiveTab('progress')) {
@@ -122,202 +198,160 @@
 
               <!-- 串式流程:10个阶段横向排列(保持) -->
               <div class="stage-progress-container">
-                <!-- ... existing code ... -->
-              </div>
-
-              <!-- 新增:板块内容区(仅展开一个,其余收起) -->
-              <div class="sections-content">
-                @for (sec of sections; track sec.key) {
-                  @if (expandedSection === sec.key) {
-                    <div class="section-panel" [attr.data-key]="sec.key">
-                      <!-- 交付执行:三阶段横向排列、内容竖向展开、空间平分 -->
-                      @if (sec.key === 'delivery') {
-                        <div class="delivery-grid">
-                          @for (stage of sec.stages; track stage) {
-                            <div class="delivery-col" [class.active]="getStageStatus(stage) === 'active'">
-                              <div class="delivery-stage-header">
-                                <span class="dot" [class.completed]="getStageStatus(stage) === 'completed'" [class.active]="getStageStatus(stage) === 'active'"></span>
-                                <h3>{{ stage }}</h3>
-                              </div>
-                              <!-- 直接复用原阶段内容卡片:按stage匹配显示 -->
-                              <div class="delivery-stage-body">
-                                @if (stage === '建模') { <!-- 引用原建模块 -->
-                                  <!-- BEGIN: reuse 建模阶段内容 -->
-                                  @if (shouldShowCard('modelCheck')) {
-                                    <div class="model-check-section">
-                                      <h4>模型误差检查清单</h4>
-                                      <div class="checklist">
-                                        @for (item of modelCheckItems; track item.id) {
-                                          <div class="checklist-item">
-                                            <label class="checklist-label">
-                                              <input type="checkbox" class="custom-checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, $any($event.target).checked)" [disabled]="!isDesignerView()">
-                                              <span class="checklist-text">{{ item.name }}</span>
-                                            </label>
-                                            <span class="check-status">{{ item.isPassed ? '已通过' : '待处理' }}</span>
-                                          </div>
-                                        }
-                                      </div>
-                                    </div>
-                                  }
-                                  <div class="upload-section">
-                                    <div class="upload-header">
-                                      <h4>上传白模图片</h4>
-                                      <span class="hint">支持:JPG/PNG;不强制4K</span>
-                                    </div>
-                                    <div class="upload-actions">
-                                      @if (isDesignerView()) {
-                                        <label class="secondary-btn">
-                                          选择图片
-                                          <input type="file" accept="{{allowedImageTypes}}" multiple (change)="onWhiteModelSelected($event)" style="display:none" />
-                                        </label>
-                                        <button class="primary-btn" [disabled]="whiteModelImages.length===0" (click)="confirmWhiteModelUpload()">确认上传</button>
-                                      }
-                                      @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('white')">同步图片信息</button> }
-                                      @if (isCustomerServiceView()) { <span class="desc">只读</span> }
-                                    </div>
-                                    @if (whiteModelImages.length > 0) {
-                                      <div class="thumb-list">
-                                        @for (img of whiteModelImages; track img.id) {
-                                          <div class="thumb-item">
-                                            <img [src]="img.url" [alt]="img.name" />
-                                            <div class="thumb-meta">
-                                              <span class="name" [title]="img.name">{{ img.name }}</span>
-                                              <span class="size">{{ img.size }}</span>
-                                            </div>
-                                            <div class="review-meta" style="display:flex;gap:8px;align-items:center;">
-                                              <span class="status-badge">{{ getImageReviewStatusText(img) }}</span>
-                                              @if (isTeamLeaderView()) {
-                                                <button class="link success" (click)="reviewImage(img.id, 'white', 'approved')">通过</button>
-                                                <button class="link warning" (click)="reviewImage(img.id, 'white', 'rejected')">驳回</button>
-                                              }
-                                            </div>
-                                            @if (isDesignerView()) { <button class="link danger" (click)="removeWhiteModelImage(img.id)">移除</button> }
-                                          </div>
-                                        }
-                                      </div>
-                                    }
-                                  </div>
-                                  <!-- END: reuse 建模阶段内容 -->
-                                }
-
-                                @if (stage === '软装') { <!-- 引用原软装块 -->
-                                  <!-- BEGIN: reuse 软装阶段内容 -->
-                                  <div class="softdecor-section">
-                                    <h4>软装补充资料</h4>
-                                    <div class="upload-section">
-                                      <div class="upload-header">
-                                        <h4>上传软装小图</h4>
-                                        <span class="hint">建议 ≤ 1MB 的 JPG/PNG 小图</span>
-                                      </div>
-                                      <div class="upload-actions">
-                                        @if (isDesignerView()) {
-                                          <label class="secondary-btn">
-                                            选择图片
-                                            <input type="file" accept="{{allowedImageTypes}}" multiple (change)="onSoftDecorSmallPicsSelected($event)" style="display:none" />
-                                          </label>
-                                          <button class="primary-btn" [disabled]="softDecorImages.length===0" (click)="confirmSoftDecorUpload()">确认上传</button>
-                                        }
-                                        @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('soft')">同步图片信息</button> }
-                                        @if (isCustomerServiceView()) { <span class="desc">只读</span> }
-                                      </div>
-                                      @if (softDecorImages.length > 0) {
-                                        <div class="thumb-list">
-                                          @for (img of softDecorImages; track img.id) {
-                                            <div class="thumb-item">
-                                              <img [src]="img.url" [alt]="img.name" />
-                                              <div class="thumb-meta">
-                                                <span class="name" [title]="img.name">{{ img.name }}</span>
-                                                <span class="size">{{ img.size }}</span>
-                                              </div>
-                                              <div class="review-meta" style="display:flex;gap:8px;align-items:center;">
-                                                <span class="status-badge">{{ getImageReviewStatusText(img) }}</span>
-                                                @if (isTeamLeaderView()) {
-                                                  <button class="link success" (click)="reviewImage(img.id, 'soft', 'approved')">通过</button>
-                                                  <button class="link warning" (click)="reviewImage(img.id, 'soft', 'rejected')">驳回</button>
-                                                }
-                                              </div>
-                                              @if (isDesignerView()) { <button class="link danger" (click)="removeSoftDecorImage(img.id)">移除</button> }
-                                            </div>
-                                          }
+                @for (stage of getVisibleStages(); track stage) {
+                  <div class="vertical-stage-block" [attr.id]="stageToAnchor(stage)" [class.active]="getStageStatus(stage) === 'active'">
+                    <div class="vertical-stage-header">
+                      <span class="dot" [class.completed]="getStageStatus(stage) === 'completed'" [class.active]="getStageStatus(stage) === 'active'"></span>
+                      <h3>{{ stage }}</h3>
+                    </div>
+                    <!-- 直接复用原阶段内容卡片:按stage匹配显示 -->
+                    <div class="vertical-stage-body">
+                      @if (stage === '订单创建') {
+                        <app-order-creation-card
+                          [orderCreationMethod]="orderCreationMethod"
+                          (orderCreationMethodChange)="orderCreationMethod = $event"
+                          [isSyncing]="isSyncing"
+                          [orderTime]="orderTime"
+                          [customerSearchKeyword]="customerSearchKeyword"
+                          (customerSearchKeywordChange)="customerSearchKeyword = $event"
+                          [customerSearchResults]="customerSearchResults"
+                          [selectedCustomer]="selectedOrderCustomer"
+                          (searchCustomer)="searchCustomer()"
+                          (selectCustomer)="selectCustomer($event)"
+                          (clearSelectedCustomer)="clearSelectedCustomer()"
+                          (syncMiniprogram)="syncMiniprogramCustomerInfo()"
+                          [customerForm]="customerForm"
+                          [demandTypes]="demandTypes"
+                          [followUpStatus]="followUpStatus"
+                        ></app-order-creation-card>
+                      } @else if (stage === '需求沟通') {
+                        <app-requirements-confirm-card></app-requirements-confirm-card>
+                      } @else if (stage === '方案确认') {
+                        <!-- 方案确认阶段暂时显示占位内容 -->
+                        <div class="stage-placeholder">
+                          <h4>方案确认阶段</h4>
+                          <p>此阶段功能正在开发中...</p>
+                        </div>
+                      } @else if (stage === '建模') {
+                        <div class="upload-section">
+                          <div class="upload-header">
+                            <h4>上传白模图片</h4>
+                            <span class="hint">支持:JPG/PNG,不强制4K</span>
+                          </div>
+                          @if (isDesignerView()) {
+                            <div class="upload-dropzone" 
+                                 (click)="whiteModelImages.length === 0 ? triggerFileInput('whiteModel') : null"
+                                 (dragover)="whiteModelImages.length === 0 ? onDragOver($event) : null"
+                                 (dragleave)="whiteModelImages.length === 0 ? onDragLeave($event) : null"
+                                 (drop)="whiteModelImages.length === 0 ? onFileDrop($event, 'whiteModel') : null"
+                                 [class.drag-over]="isDragOver && whiteModelImages.length === 0"
+                                 [class.has-images]="whiteModelImages.length > 0">
+                              @if (whiteModelImages.length === 0) {
+                                <div class="upload-icon"></div>
+                                <div class="upload-text">点击此处或拖拽文件到此处上传</div>
+                                <div class="upload-hint">支持 JPG、PNG 格式,单个文件最大 10MB</div>
+                              } @else {
+                                <div class="uploaded-images-grid">
+                                  @for (img of whiteModelImages; track img.id) {
+                                    <div class="uploaded-image-item" (click)="previewImage(img)">
+                                      <img [src]="img.url" [alt]="img.name" />
+                                      <div class="image-overlay">
+                                        <div class="image-name">{{ img.name }}</div>
+                                        <div class="image-actions">
+                                          <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
+                                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                                              <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
+                                              <circle cx="12" cy="12" r="3"></circle>
+                                            </svg>
+                                          </button>
+                                          <button class="remove-btn" (click)="$event.stopPropagation(); removeWhiteModelImage(img.id)">
+                                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                                              <line x1="18" y1="6" x2="6" y2="18"></line>
+                                              <line x1="6" y1="6" x2="18" y2="18"></line>
+                                            </svg>
+                                          </button>
                                         </div>
-                                      }
-                                    </div>
-                                  </div>
-                                  <!-- END: reuse 软装阶段内容 -->
-                                }
-
-                                @if (stage === '渲染') { <!-- 引用原渲染块 -->
-                                  <!-- BEGIN: reuse 渲染阶段内容(简化容器) -->
-                                  <div class="render-progress-section">
-                                    @if (isLoadingRenderProgress) {
-                                      <div class="loading-state">
-                                        <div class="loading-spinner"></div>
-                                        <div>正在加载渲染进度...</div>
                                       </div>
-                                    }
-                                    @if (errorLoadingRenderProgress) {
-                                      <div class="error-state">
-                                        <div>渲染进度加载失败</div>
-                                        <button class="secondary-btn" (click)="retryLoadRenderProgress()">重试</button>
-                                      </div>
-                                    }
-                                    @if (!isLoadingRenderProgress && !errorLoadingRenderProgress && renderProgress) {
-                                      <div class="progress-info" style="display:flex;gap:16px;align-items:center;margin:12px 0;">
-                                        <span>状态:{{ renderProgress.status }}</span>
-                                        <span>完成度:{{ renderProgress.completionRate }}%</span>
-                                        <span>预计剩余:{{ renderProgress.estimatedTimeRemaining }} 小时</span>
-                                      </div>
-                                    }
-                                    <div class="upload-section">
-                                      <div class="upload-header">
-                                        <h4>上传渲染大图</h4>
-                                        <span class="hint">需满足4K标准(最大边≥4000px)</span>
-                                      </div>
-                                      <div class="upload-actions" style="display:flex;gap:12px;align-items:center;">
-                                        @if (isDesignerView()) { <button class="primary-btn" (click)="openRenderUploadModal()">选择并上传</button> }
-                                        @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('render')">同步图片信息</button> }
-                                        @if (renderLargeImages.length>0) { <span class="desc">已上传 {{renderLargeImages.length}} 张</span> }
-                                      </div>
-                                      @if (renderLargeImages.length > 0) {
-                                        <div class="thumb-list">
-                                          @for (img of renderLargeImages; track img.id) {
-                                            <div class="thumb-item">
-                                              <img [src]="img.url" [alt]="img.name" />
-                                              <div class="thumb-meta">
-                                                <span class="name" [title]="img.name">{{ img.name }}</span>
-                                                <span class="size">{{ img.size }}</span>
-                                              </div>
-                                              <div class="review-meta" style="display:flex;gap:8px;align-items:center;">
-                                                <span class="status-badge">{{ getImageReviewStatusText(img) }}</span>
-                                                @if (isTeamLeaderView()) {
-                                                  <button class="link success" (click)="reviewImage(img.id, 'render', 'approved')">通过</button>
-                                                  <button class="link warning" (click)="reviewImage(img.id, 'render', 'rejected')">驳回</button>
-                                                }
-                                              </div>
-                                              @if (isDesignerView()) { <button class="link danger" (click)="removeRenderLargeImage(img.id)">移除</button> }
-                                            </div>
-                                          }
-                                        </div>
-                                      }
                                     </div>
+                                  }
+                                  <div class="add-more-btn" (click)="triggerFileInput('whiteModel')">
+                                    <div class="add-icon">+</div>
+                                    <div class="add-text">添加更多</div>
                                   </div>
-                                  <!-- END: reuse 渲染阶段内容 -->
-                                }
-                              </div>
+                                </div>
+                              }
+                              <input type="file" 
+                                     id="whiteModelFileInput"
+                                     accept="{{allowedImageTypes}}" 
+                                     multiple 
+                                     (change)="onWhiteModelSelected($event)" 
+                                     style="display: none;" />
                             </div>
                           }
+                          <div class="upload-actions">
+                            @if (isDesignerView()) {
+                              <button class="primary-btn" [disabled]="whiteModelImages.length===0" (click)="confirmWhiteModelUpload()">确认上传</button>
+                            }
+                            @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('white')">同步图片信息</button> }
+                            @if (isCustomerServiceView()) { <span class="desc">只读</span> }
+                          </div>
                         </div>
-                      }
-
-                      <!-- 订单创建、确认需求、售后:统一纵向内容容器(保留原功能区) -->
-                      @if (sec.key !== 'delivery') {
-                        <div class="section-vertical">
-                          @for (stage of sec.stages; track stage) {
-                            <div class="vertical-stage-block" [class.active]="getStageStatus(stage) === 'active'">
-                              <div class="vertical-stage-header">
-                                <span class="dot" [class.completed]="getStageStatus(stage) === 'completed'" [class.active]="getStageStatus(stage) === 'active'"></span>
-                                <h3>{{ stage }}</h3>
+                        <div class="model-check-section">
+                          <h4>模型差异检查清单</h4>
+                          <div class="checklist">
+                            @for (item of modelCheckItems; track item.id) {
+                              <div class="checklist-item">
+                                <label class="checklist-label">
+                                  <input type="checkbox" class="custom-checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, $any($event.target).checked)" [disabled]="!isDesignerView()">
+                                  <span class="checklist-text">{{ item.name }}</span>
+                                </label>
+                                <span class="check-status">{{ item.isPassed ? '已通过' : '待处理' }}</span>
                               </div>
+<<<<<<< HEAD
+                            }
+                          </div>
+                        </div>
+                      } @else if (stage === '软装') {
+                        <div class="softdecor-section">
+                          <h4>软装清单素材</h4>
+                          <div class="upload-section">
+                            <div class="upload-header">
+                              <h4>上传软装小图</h4>
+                              <span class="hint">建议 ≤1MB 的 JPG/PNG 小图</span>
+                            </div>
+                            @if (isDesignerView()) {
+                              <div class="upload-dropzone" 
+                                   (click)="softDecorImages.length === 0 ? triggerFileInput('softDecor') : null"
+                                   (dragover)="softDecorImages.length === 0 ? onDragOver($event) : null"
+                                   (dragleave)="softDecorImages.length === 0 ? onDragLeave($event) : null"
+                                   (drop)="softDecorImages.length === 0 ? onFileDrop($event, 'softDecor') : null"
+                                   [class.drag-over]="isDragOver && softDecorImages.length === 0"
+                                   [class.has-images]="softDecorImages.length > 0">
+                                @if (softDecorImages.length === 0) {
+                                  <div class="upload-icon"></div>
+                                  <div class="upload-text">点击此处或拖拽文件到此处上传</div>
+                                  <div class="upload-hint">支持 JPG、PNG 格式,建议文件大小 ≤1MB</div>
+                                } @else {
+                                  <div class="uploaded-images-grid">
+                                    @for (img of softDecorImages; track img.id) {
+                                      <div class="uploaded-image-item" (click)="previewImage(img)">
+                                        <img [src]="img.url" [alt]="img.name" />
+                                        <div class="image-overlay">
+                                          <div class="image-name">{{ img.name }}</div>
+                                          <div class="image-actions">
+                                            <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
+                                              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                                                <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
+                                                <circle cx="12" cy="12" r="3"></circle>
+                                              </svg>
+                                            </button>
+                                            <button class="remove-btn" (click)="$event.stopPropagation(); removeSoftDecorImage(img.id)">
+                                              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                                                <line x1="18" y1="6" x2="6" y2="18"></line>
+                                                <line x1="6" y1="6" x2="18" y2="18"></line>
+                                              </svg>
+                                            </button>
+=======
                               <div class="vertical-stage-body">
                                 <!-- 复用原模板各阶段的具体内容(示例:尾款结算等) -->
                                 @if (stage === '尾款结算') {
@@ -378,7 +412,7 @@
                                       <button class="secondary-btn" (click)="searchCustomer()">搜索</button>
                                       @if (selectedOrderCustomer) {
                                         <div style="display:flex; gap:8px; align-items:center; background:#f3f4f6; padding:6px 10px; border-radius:9999px;">
-                                          <span>已选客户:{{ selectedOrderCustomer?.name }}({{ selectedOrderCustomer?.phone }})</span>
+                                          <span>已选客户:{{ selectedOrderCustomer.name }}({{ selectedOrderCustomer.phone }})</span>
                                           <button class="link danger" (click)="clearSelectedCustomer()">清除</button>
                                         </div>
                                       }
@@ -394,141 +428,230 @@
                                               @if (c.wechat) { <span style="color:#9ca3af;">wx: {{ c.wechat }}</span> }
                                             </div>
                                             <button class="link" (click)="selectCustomer(c)">选择</button>
+>>>>>>> 0648016df9d2fd422344c75eef9d0e437f9c8f12
                                           </div>
-                                        }
+                                        </div>
                                       </div>
                                     }
-
-                                    <form [formGroup]="customerForm" novalidate>
-                                      <div class="form-group">
-                                        <label>客户姓名</label>
-                                        <input type="text" formControlName="name" placeholder="请输入客户姓名" />
-                                        @if (customerForm.get('name')?.touched && customerForm.get('name')?.invalid) {
-                                          <div style="color:#ef4444; font-size:12px; margin-top:4px;">请填写客户姓名</div>
-                                        }
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>手机号</label>
-                                        <input type="text" formControlName="phone" placeholder="请输入11位手机号" />
-                                        @if (customerForm.get('phone')?.touched && customerForm.get('phone')?.errors?.['required']) {
-                                          <div style="color:#ef4444; font-size:12px; margin-top:4px;">请填写手机号</div>
-                                        }
-                                        @if (customerForm.get('phone')?.touched && customerForm.get('phone')?.errors?.['pattern']) {
-                                          <div style="color:#ef4444; font-size:12px; margin-top:4px;">手机号格式不正确</div>
-                                        }
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>微信号</label>
-                                        <input type="text" formControlName="wechat" placeholder="可选" />
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>客户类型</label>
-                                        <select formControlName="customerType">
-                                          <option value="新客户">新客户</option>
-                                          <option value="老客户">老客户</option>
-                                          <option value="VIP客户">VIP客户</option>
-                                        </select>
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>需求类型</label>
-                                        <select formControlName="demandType">
-                                          <option value="">未指定</option>
-                                          @for (d of demandTypes; track d.value) {
-                                            <option [value]="d.value">{{ d.label }}</option>
-                                          }
-                                        </select>
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>跟进状态</label>
-                                        <select formControlName="followUpStatus">
-                                          <option value="">未指定</option>
-                                          @for (s of followUpStatus; track s.value) {
-                                            <option [value]="s.value">{{ s.label }}</option>
-                                          }
-                                        </select>
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>来源渠道</label>
-                                        <input type="text" formControlName="source" placeholder="如:抖音/官网/转介绍/线下" />
-                                      </div>
-
-                                      <div class="form-group">
-                                        <label>备注</label>
-                                        <textarea formControlName="remark" rows="3" placeholder="可填写特殊要求或沟通记录"></textarea>
-                                      </div>
-                                    </form>
+                                    <div class="add-more-btn" (click)="triggerFileInput('softDecor')">
+                                      <div class="add-icon">+</div>
+                                      <div class="add-text">添加更多</div>
+                                    </div>
                                   </div>
                                 }
-                                @if (stage === '需求沟通' || stage === '方案确认') {
-                                  <div class="empty">该阶段的详细表单与内容保持与现有功能一致,后续可按需接入</div>
-                                }
+                                <input type="file" 
+                                       id="softDecorFileInput"
+                                       accept="{{allowedImageTypes}}" 
+                                       multiple 
+                                       (change)="onSoftDecorSmallPicsSelected($event)" 
+                                       style="display: none;" />
                               </div>
+                            }
+                            <div class="upload-actions">
+                              @if (isDesignerView()) {
+                                <button class="primary-btn" [disabled]="softDecorImages.length===0" (click)="confirmSoftDecorUpload()">确认上传</button>
+                              }
+                              @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('soft')">同步图片信息</button> }
+                              @if (isCustomerServiceView()) { <span class="desc">只读</span> }
+                            </div>
+                          </div>
+                        </div>
+                      } @else if (stage === '渲染') {
+                        <div class="render-progress-section">
+                          @if (isLoadingRenderProgress) {
+                            <div class="loading-state">
+                              <div class="loading-spinner"></div>
+                              <div>正在加载渲染进度...</div>
+                            </div>
+                          }
+                          @if (errorLoadingRenderProgress) {
+                            <div class="error-state">
+                              <div>渲染进度加载失败</div>
+                              <button class="secondary-btn" (click)="retryLoadRenderProgress()">重试</button>
+                            </div>
+                          }
+                          @if (!isLoadingRenderProgress && !errorLoadingRenderProgress && renderProgress) {
+                            <div class="progress-info" style="display:flex;gap:16px;align-items:center;margin:12px 0;">
+                              <span>状态:{{ renderProgress.status }}</span>
+                              <span>完成度:{{ renderProgress.completionRate }}%</span>
+                              <span>预计剩余:{{ renderProgress.estimatedTimeRemaining }} 小时</span>
                             </div>
                           }
+                          <div class="upload-section">
+                            <div class="upload-header">
+                              <h4>上传渲染大图</h4>
+                              <span class="hint">需满足4K标准(最长边 ≥ 4000px)</span>
+                            </div>
+                            @if (isDesignerView()) {
+                              <div class="upload-dropzone" 
+                                   (click)="renderLargeImages.length === 0 ? triggerFileInput('render') : null"
+                                   (dragover)="renderLargeImages.length === 0 ? onDragOver($event) : null"
+                                   (dragleave)="renderLargeImages.length === 0 ? onDragLeave($event) : null"
+                                   (drop)="renderLargeImages.length === 0 ? onFileDrop($event, 'render') : null"
+                                   [class.drag-over]="isDragOver && renderLargeImages.length === 0"
+                                   [class.has-images]="renderLargeImages.length > 0">
+                                @if (renderLargeImages.length === 0) {
+                                  <div class="upload-icon"></div>
+                                  <div class="upload-text">点击此处或拖拽文件到此处上传</div>
+                                  <div class="upload-hint">支持 JPG、PNG 格式,需满足4K标准(最长边 ≥ 4000px)</div>
+                                } @else {
+                                  <div class="uploaded-images-grid">
+                                    @for (img of renderLargeImages; track img.id) {
+                                      <div class="uploaded-image-item" (click)="previewImage(img)">
+                                        <img [src]="img.url" [alt]="img.name" />
+                                        <div class="image-overlay">
+                                          <div class="image-name">{{ img.name }}</div>
+                                          <div class="image-actions">
+                                            <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
+                                              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                                                <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
+                                                <circle cx="12" cy="12" r="3"></circle>
+                                              </svg>
+                                            </button>
+                                            <button class="remove-btn" (click)="$event.stopPropagation(); removeRenderLargeImage(img.id)">
+                                              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                                                <line x1="18" y1="6" x2="6" y2="18"></line>
+                                                <line x1="6" y1="6" x2="18" y2="18"></line>
+                                              </svg>
+                                            </button>
+                                          </div>
+                                        </div>
+                                      </div>
+                                    }
+                                    <div class="add-more-btn" (click)="triggerFileInput('render')">
+                                      <div class="add-icon">+</div>
+                                      <div class="add-text">添加更多</div>
+                                    </div>
+                                  </div>
+                                }
+                                <input type="file" 
+                                       id="renderFileInput"
+                                       accept="{{allowedImageTypes}}" 
+                                       multiple 
+                                       (change)="onRenderLargePicsSelected($event)" 
+                                       style="display: none;" />
+                              </div>
+                            }
+                            <div class="upload-actions" style="display:flex;gap:12px;align-items:center;">
+                              @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('render')">同步图片信息</button> }
+                              @if (renderLargeImages.length>0) { <span class="desc">已上传 {{renderLargeImages.length}} 张</span> }
+                            </div>
+                          </div>
                         </div>
+                      } @else if (stage === '尾款结算') {
+                        <app-settlement-card [settlements]="settlements"></app-settlement-card>
+                      } @else if (stage === '客户评价') {
+                        <app-customer-review-card [feedbacks]="feedbacks"></app-customer-review-card>
+                      } @else if (stage === '投诉处理') {
+                        <app-complaint-card [complaints]="exceptionHistories"></app-complaint-card>
                       }
                     </div>
-                  }
+                  </div>
                 }
               </div>
-
-              <!-- 保留原下方按当前激活阶段显示的旧详情网格(可逐步淘汰) -->
-              <!-- ... existing code ... -->
-
             </div>
+        </div>
+      </div>
+      </div>
+    }
+    
+    <!-- 项目人员标签页 -->
+    @if (isActiveTab('members')) {
+      <div class="members-tab-content">
+        <div class="main-content-layout">
+          <!-- 项目人员内容 -->
+          <div class="members-content">
+            <div class="members-header">
+              <h2>项目成员</h2>
+              <p class="members-count">共 {{ projectMembers.length }} 名成员</p>
+            </div>
+            
+            <div class="members-grid">
+              @for (member of projectMembers; track member.id) {
+                <div class="member-card">
+                  <div class="member-avatar">
+                    <img [src]="member.avatar" [alt]="member.name">
+                  </div>
+                  <div class="member-info">
+                    <h3 class="member-name">{{ member.name }}</h3>
+                    <p class="member-role">{{ member.role }}</p>
+                    <div class="member-stats">
+                      <div class="stat-item">
+                        <span class="stat-label">技能匹配度</span>
+                        <div class="progress-bar">
+                          <div class="progress-fill" [style.width.%]="member.skillMatch"></div>
+                        </div>
+                        <span class="stat-value">{{ member.skillMatch }}%</span>
+                      </div>
+                      <div class="stat-item">
+                        <span class="stat-label">项目进度</span>
+                        <div class="progress-bar">
+                          <div class="progress-fill" [style.width.%]="member.progress"></div>
+                        </div>
+                        <span class="stat-value">{{ member.progress }}%</span>
+                      </div>
+                      <div class="stat-item">
+                        <span class="stat-label">贡献度</span>
+                        <div class="progress-bar">
+                          <div class="progress-fill" [style.width.%]="member.contribution"></div>
+                        </div>
+                        <span class="stat-value">{{ member.contribution }}%</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+            
+            @if (projectMembers.length === 0) {
+              <div class="empty-state">
+                <p>暂无项目成员信息</p>
+              </div>
+            }
           </div>
         </div>
       </div>
     }
-
-    <!-- 项目成员标签页 -->
-    <!-- ... existing code ... -->
-
+    
     <!-- 项目文件标签页 -->
-    <!-- ... existing code ... -->
-  </div>
-</div>
-
-<!-- 渲染阶段:上传大图 弹窗(基于 @if 控制显示) -->
-@if (showRenderUploadModal) {
-  <div class="modal-backdrop" (click)="closeRenderUploadModal()">
-    <div class="modal" (click)="$event.stopPropagation()">
-      <div class="modal-header">
-        <h3>上传渲染大图</h3>
-        <button class="close-button" (click)="closeRenderUploadModal()">×</button>
-      </div>
-      <div class="modal-body">
-        <div style="display:flex; gap:12px; align-items:center; margin-bottom:12px;">
-          <input #renderFileInput type="file" accept="{{allowedImageTypes}}" multiple (change)="onRenderLargePicsSelected($event)" style="display:none" />
-          <button class="secondary-btn" (click)="renderFileInput.click()">选择文件</button>
-          <span class="hint">支持 {{allowedImageTypes}};将校验4K标准(最大边≥4000px)</span>
-        </div>
-        @if (pendingRenderLargeItems.length > 0) {
-          <div class="thumb-list" style="max-height:320px; overflow:auto;">
-            @for (item of pendingRenderLargeItems; track item.id) {
-              <div class="thumb-item">
-                <img [src]="item.url" [alt]="item.name" />
-                <div class="thumb-meta">
-                  <span class="name" [title]="item.name">{{ item.name }}</span>
+    @if (isActiveTab('files')) {
+      <div class="files-tab-content">
+        <div class="main-content-layout">
+          <!-- 项目文件内容 -->
+          <div class="files-content">
+            <div class="files-header">
+              <h2>项目文件</h2>
+              <p class="files-count">共 {{ projectFiles.length }} 个文件</p>
+            </div>
+            
+            <div class="files-list">
+              @for (file of projectFiles; track file.id) {
+                <div class="file-item">
+                  <div class="file-icon">
+                    <span class="file-type">{{ file.type.toUpperCase() }}</span>
+                  </div>
+                  <div class="file-info">
+                    <h3 class="file-name">{{ file.name }}</h3>
+                    <div class="file-meta">
+                      <span class="file-size">{{ file.size }}</span>
+                      <span class="file-date">{{ file.date }}</span>
+                    </div>
+                  </div>
+                  <div class="file-actions">
+                    <button class="btn-download" (click)="downloadFile(file)">下载</button>
+                    <button class="btn-preview" (click)="previewFile(file)">预览</button>
+                  </div>
                 </div>
+              }
+            </div>
+            
+            @if (projectFiles.length === 0) {
+              <div class="empty-state">
+                <p>暂无项目文件</p>
               </div>
             }
           </div>
-        }
-        @if (pendingRenderLargeItems.length === 0) {
-          <div class="empty-state" style="padding:12px;color:#6b7280;">尚未选择文件</div>
-        }
-      </div>
-      <div class="modal-footer">
-        <button class="secondary-btn" (click)="closeRenderUploadModal()">取消</button>
-        <button class="primary-btn" [disabled]="pendingRenderLargeItems.length === 0" (click)="confirmRenderUpload()">确认上传</button>
+        </div>
       </div>
-    </div>
+    }
   </div>
-}

+ 1163 - 1379
src/app/pages/designer/project-detail/project-detail.scss

@@ -1,1446 +1,1230 @@
-/* 项目详情页面样式文件 - 简化版本,直接针对HTML元素 */
-@use '../ios-theme.scss' as *;
-
-/* 重置所有可能冲突的样式 */
-* {
-  box-sizing: border-box;
-}
-
-/* 全局侧边栏彻底隐藏 - 高优先级规则 */
-.sidebar {
-  display: none !important;
-  width: 0 !important;
-  visibility: hidden !important;
-}
-
-/* 主容器样式 - 确保内容区域占满整个屏幕 */
-.content-wrapper {
-  width: 100% !important;
-  margin-left: 0 !important;
-  padding: 0 !important;
-}
-
-/* 主内容区域样式 - 重置为默认值 */
-.main-content {
-  padding: 0 !important;
-  margin: 0 !important;
-}
+@use '../../../shared/styles/_ios-theme.scss' as *;
+@import './components/vertical-nav/vertical-nav-styles';
 
 /* 项目详情容器 */
 .project-detail-container {
-  padding: 20px;
+  padding: $ios-spacing-lg; // 使用统一的间距变量
   background-color: #f5f7fa;
   color: #333;
   min-height: 100vh;
-}
-
-/* 项目标题栏 */
-.project-header {
+  box-sizing: border-box; // 确保内边距计算正确
+  overflow-x: auto; // 允许水平滚动以防内容过宽
+}
+
+// 图片预览模态框样式
+.image-preview-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 1000;
   display: flex;
-  justify-content: space-between;
   align-items: center;
-  margin-bottom: 20px;
-  background-color: white;
-  padding: 20px;
-  border-radius: 8px;
-  width: 100% !important;
-}
-
-/* 强制覆盖主内容布局样式 - 使用最高优先级 */
-.progress-tab-content > .main-content-layout {
-  display: flex !important;
-  gap: 20px !important;
-  margin-top: 20px !important;
-  width: 100% !important;
-  background-color: rgba(200, 200, 255, 0.5) !important; /* 明显的背景色 */
-}
-
-/* 强制覆盖左侧列样式 - 使用最高优先级 */
-.progress-tab-content > .main-content-layout > .left-column {
-  width: 33.333% !important;
-  display: flex !important;
-  flex-direction: column !important;
-  gap: 20px !important;
-  background-color: rgba(255, 200, 200, 0.3) !important; /* 左侧列背景色 */
-}
-
-/* 强制覆盖右侧列样式 - 使用最高优先级 */
-.progress-tab-content > .main-content-layout > .right-column {
-  width: 66.667% !important;
-  display: flex !important;
-  flex-direction: column !important;
-  gap: 20px !important;
-  background-color: rgba(200, 255, 200, 0.3) !important; /* 右侧列背景色 */
-}
-
-/* 确保响应式布局正常工作 */
-@media (max-width: 1024px) {
-  .progress-tab-content > .main-content-layout {
-    flex-direction: column !important;
+  justify-content: center;
+  
+  .modal-backdrop {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.8);
+    backdrop-filter: blur(4px);
   }
   
-  .progress-tab-content > .main-content-layout > .left-column,
-  .progress-tab-content > .main-content-layout > .right-column {
-    width: 100% !important;
+  .modal-content {
+    position: relative;
+    background: white;
+    border-radius: 12px;
+    max-width: 90vw;
+    max-height: 90vh;
+    overflow: hidden;
+    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
+    animation: modalFadeIn 0.3s ease-out;
+    
+    .modal-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 16px 20px;
+      border-bottom: 1px solid #e9ecef;
+      
+      h3 {
+        margin: 0;
+        font-size: 18px;
+        font-weight: 600;
+        color: #333;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        max-width: 400px;
+      }
+      
+      .close-btn {
+        width: 40px;
+        height: 40px;
+        border: none;
+        background: #f8f9fa;
+        border-radius: 8px;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        transition: all 0.2s ease;
+        
+        &:hover {
+          background: #e9ecef;
+          color: #dc3545;
+        }
+      }
+    }
+    
+    .modal-body {
+      padding: 20px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      max-height: 60vh;
+      
+      img {
+        max-width: 100%;
+        max-height: 100%;
+        object-fit: contain;
+        border-radius: 8px;
+      }
+    }
+    
+    .modal-footer {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 16px 20px;
+      border-top: 1px solid #e9ecef;
+      background: #f8f9fa;
+      
+      .image-info {
+        display: flex;
+        gap: 12px;
+        align-items: center;
+        font-size: 14px;
+        color: #666;
+        
+        .status-badge {
+          padding: 4px 8px;
+          border-radius: 4px;
+          font-size: 12px;
+          font-weight: 500;
+          background: #e9ecef;
+          color: #495057;
+        }
+      }
+      
+      .modal-actions {
+        display: flex;
+        gap: 12px;
+        
+        button {
+          padding: 8px 16px;
+          border: none;
+          border-radius: 6px;
+          font-size: 14px;
+          font-weight: 500;
+          cursor: pointer;
+          transition: all 0.2s ease;
+          
+          &.secondary-btn {
+            background: #6c757d;
+            color: white;
+            
+            &:hover {
+              background: #5a6268;
+            }
+          }
+          
+          &.danger-btn {
+            background: #dc3545;
+            color: white;
+            
+            &:hover {
+              background: #c82333;
+            }
+          }
+        }
+      }
+    }
   }
 }
-.header-content{display:flex;flex-direction:column;gap:$ios-spacing-sm}
-.project-header h1{font-size:$ios-font-size-xl;font-weight:$ios-font-weight-bold;color:$ios-text-primary;margin:0}
-.project-meta{display:flex;align-items:center;gap:$ios-spacing-lg;font-size:$ios-font-size-sm;color:$ios-text-secondary}
-.project-id{font-weight:$ios-font-weight-medium}
-.project-status{padding:$ios-spacing-xs $ios-spacing-md;border-radius:$ios-radius-full;background-color:#e8f0fe;color:$ios-primary}
-
-/* 按钮样式 */
-.header-actions{display:flex;gap:$ios-spacing-md;align-items:center}
-.back-btn{background-color:$ios-primary;color:white;border:none;padding:$ios-spacing-sm $ios-spacing-lg;border-radius:$ios-radius-md;font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;cursor:pointer;white-space:nowrap}
-.back-btn:hover{background-color:#0056b3;transform:translateY(-1px)}
-.project-switcher{position:relative}
-.switch-btn{background-color:$ios-background-tertiary;color:$ios-text-primary;border:none;padding:$ios-spacing-sm $ios-spacing-lg;border-radius:$ios-radius-md;font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;cursor:pointer;white-space:nowrap}
-.switch-btn:hover{background-color:#f0f0f0;transform:translateY(-1px)}
-.switch-dropdown{position:absolute;top:100%;right:0;margin-top:$ios-spacing-xs;background-color:$ios-background-secondary;border-radius:$ios-radius-md;overflow:hidden;min-width:200px;max-height:300px;overflow-y:auto;z-index:1000}
-.project-item{display:flex;justify-content:space-between;align-items:center;padding:$ios-spacing-md;cursor:pointer;border-bottom:1px solid $ios-border}
-.project-item:last-child{border-bottom:none}
-.project-item:hover{background-color:$ios-background}
-.project-item.active{background-color:#e8f0fe}
-.project-name{font-size:$ios-font-size-sm;color:$ios-text-primary;font-weight:$ios-font-weight-medium}
-.project-status-badge{font-size:$ios-font-size-xs;padding:$ios-spacing-xs $ios-spacing-sm;border-radius:$ios-radius-full;background-color:$ios-background-tertiary;color:$ios-text-secondary}
-.project-status-badge.ongoing{background-color:#e8f0fe;color:$ios-primary}
-.project-status-badge.completed{background-color:#e6f7e6;color:$ios-success}
-.project-status-badge.pending{background-color:#fff3cd;color:$ios-warning}
-
-.stagnation-btn{background-color:$ios-warning;color:white;border:none;padding:$ios-spacing-sm $ios-spacing-md;border-radius:$ios-radius-md;font-size:$ios-font-size-sm;cursor:pointer;font-weight:$ios-font-weight-medium}
-.stagnation-btn:hover{background-color:#d4a72c;transform:translateY(-1px)}
-
-/* 顶部导航标签页样式 */
-.project-tabs{
-  display:flex;
-  margin-bottom:$ios-spacing-xl;
-  border-bottom:1px solid $ios-border;
-  background-color:$ios-background-secondary;
-  border-radius:$ios-radius-lg $ios-radius-lg 0 0;
-  padding:$ios-spacing-xs 0;
-}
-
-.tab-item{
-  padding:$ios-spacing-md $ios-spacing-xl;
-  font-size:$ios-font-size-base;
-  font-weight:$ios-font-weight-medium;
-  color:$ios-text-secondary;
-  cursor:pointer;
-  transition:all 0.2s ease;
-  border-bottom:2px solid transparent;
-  background-color:transparent;
-  border:none;
-}
-
-.tab-item:hover{
-  color:$ios-text-primary;
-  background-color:$ios-background;
-}
-
-.tab-item.active{
-  color:$ios-primary;
-  border-bottom-color:$ios-primary;
-  background-color:$ios-background;
-}
-
-/* 标签页内容容器样式 */
-.tab-content{
-  background-color:$ios-background;
-  border-radius:0 0 $ios-radius-lg $ios-radius-lg;
-  padding:$ios-spacing-xl;
-  min-height:calc(100vh - 320px);
-}
-
-.tab-content.hidden{
-  display:none;
-}
-
-/* 项目成员列表样式 */
-.member-list{
-  display:grid;
-  grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));
-  gap:$ios-spacing-lg;
-  margin-bottom:$ios-spacing-xl;
-}
-
-.member-card{
-  background-color:$ios-background-secondary;
-  border-radius:$ios-radius-lg;
-  padding:$ios-spacing-lg;
-  display:flex;
-  align-items:center;
-  gap:$ios-spacing-lg;
-  transition:transform 0.2s ease, box-shadow 0.2s ease;
-}
-
-.member-card:hover{
-  transform:translateY(-2px);
-  box-shadow:0 4px 12px rgba(0,0,0,0.05);
-}
-
-.member-avatar{
-  width:64px;
-  height:64px;
-  border-radius:50%;
-  background-color:$ios-primary;
-  color:white;
-  display:flex;
-  align-items:center;
-  justify-content:center;
-  font-size:$ios-font-size-lg;
-  font-weight:$ios-font-weight-semibold;
-  flex-shrink:0;
-}
-
-.member-info{
-  flex:1;
-  min-width:0;
-}
-
-.member-name{
-  font-size:$ios-font-size-base;
-  font-weight:$ios-font-weight-semibold;
-  color:$ios-text-primary;
-  margin-bottom:$ios-spacing-xs;
-}
-
-.member-role{
-  font-size:$ios-font-size-sm;
-  color:$ios-text-secondary;
-  margin-bottom:$ios-spacing-md;
-}
-
-.member-metrics{
-  display:flex;
-  gap:$ios-spacing-md;
-  flex-wrap:wrap;
-}
-
-.metric-item{
-  display:flex;
-  flex-direction:column;
-  gap:$ios-spacing-xs;
-}
-
-.metric-label{
-  font-size:$ios-font-size-xs;
-  color:$ios-text-secondary;
-}
-
-.metric-value{
-  font-size:$ios-font-size-sm;
-  color:$ios-text-primary;
-  font-weight:$ios-font-weight-medium;
-}
-
-/* 团队协作时间轴样式 */
-.team-timeline{
-  position:relative;
-  padding-left:$ios-spacing-xxl;
-  margin-bottom:$ios-spacing-xl;
-}
-
-.timeline-item{
-  position:relative;
-  padding-bottom:$ios-spacing-xl;
-}
-
-.timeline-item:last-child{
-  padding-bottom:0;
-}
-
-.timeline-item::before{
-  content:'';
-  position:absolute;
-  left: -$ios-spacing-lg;
-  top:$ios-spacing-sm;
-  width:12px;
-  height:12px;
-  border-radius:50%;
-  background-color:$ios-primary;
-  border:3px solid #e8f0fe;
-}
-
-.timeline-item:not(:last-child)::after{
-  content:'';
-  position:absolute;
-  left:-$ios-spacing-lg + 4px;
-  top:24px;
-  bottom:0;
-  width:2px;
-  background-color:$ios-border;
-}
-
-.timeline-time{
-  font-size:$ios-font-size-sm;
-  color:$ios-text-secondary;
-  margin-bottom:$ios-spacing-xs;
-}
-
-.timeline-content{
-  background-color:$ios-background-secondary;
-  border-radius:$ios-radius-md;
-  padding:$ios-spacing-md;
-}
-
-.timeline-header{
-  display:flex;
-  justify-content:space-between;
-  align-items:center;
-  margin-bottom:$ios-spacing-sm;
-}
-
-.timeline-title{
-  font-size:$ios-font-size-base;
-  font-weight:$ios-font-weight-medium;
-  color:$ios-text-primary;
-}
-
-.timeline-action{
-  font-size:$ios-font-size-xs;
-  background-color:$ios-primary;
-  color:white;
-  padding:$ios-spacing-xs $ios-spacing-sm;
-  border-radius:$ios-radius-full;
-}
-
-.timeline-description{
-  font-size:$ios-font-size-sm;
-  color:$ios-text-secondary;
-  line-height:1.5;
-}
-
-/* 项目文件列表样式 */
-.file-list{
-  background-color:$ios-background-secondary;
-  border-radius:$ios-radius-lg;
-  overflow:hidden;
-  margin-bottom:$ios-spacing-xl;
-}
-
-.file-header{
-  display:flex;
-  justify-content:space-between;
-  align-items:center;
-  padding:$ios-spacing-lg;
-  border-bottom:1px solid $ios-border;
-  background-color:$ios-background-tertiary;
-}
 
-.file-header h3{
-  margin:0;
-  font-size:$ios-font-size-base;
-}
-
-.file-upload-btn{
-  background-color:$ios-primary;
-  color:white;
-  border:none;
-  padding:$ios-spacing-sm $ios-spacing-lg;
-  border-radius:$ios-radius-md;
-  font-size:$ios-font-size-sm;
-  font-weight:$ios-font-weight-medium;
-  cursor:pointer;
-  display:flex;
-  align-items:center;
-  gap:$ios-spacing-xs;
-}
-
-.file-upload-btn:hover{
-  background-color:#0056b3;
-  transform:translateY(-1px);
-}
-
-.file-grid{
-  display:grid;
-  grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));
-  gap:$ios-spacing-md;
-  padding:$ios-spacing-lg;
-}
-
-.file-item{
-  background-color:$ios-background;
-  border-radius:$ios-radius-md;
-  padding:$ios-spacing-md;
-  text-align:center;
-  transition:transform 0.2s ease, box-shadow 0.2s ease;
-  cursor:pointer;
-}
-
-.file-item:hover{
-  transform:translateY(-2px);
-  box-shadow:0 4px 12px rgba(0,0,0,0.05);
-}
-
-.file-icon{
-  font-size:$ios-font-size-xl;
-  color:$ios-primary;
-  margin-bottom:$ios-spacing-sm;
-}
-
-.file-name{
-  font-size:$ios-font-size-sm;
-  color:$ios-text-primary;
-  margin-bottom:$ios-spacing-xs;
-  white-space:nowrap;
-  overflow:hidden;
-  text-overflow:ellipsis;
-}
-
-.file-meta{
-  font-size:$ios-font-size-xs;
-  color:$ios-text-secondary;
-}
-
-/* 任务卡片样式优化 */
-.stage-specific-card{
-  background-color:$ios-background-secondary;
-  border-radius:$ios-radius-lg;
-  padding:$ios-spacing-lg;
-  margin-bottom:$ios-spacing-xl;
-  border-left:4px solid $ios-primary;
-}
-
-.stage-specific-card.warning{
-  border-left-color:$ios-warning;
-}
-
-.stage-specific-card.danger{
-  border-left-color:$ios-danger;
-}
-
-.stage-specific-card.success{
-  border-left-color:$ios-success;
-}
-
-/* 整理按钮样式 */
-.organize-btn{
-  background-color:$ios-background-tertiary;
-  color:$ios-text-primary;
-  border:none;
-  padding:$ios-spacing-sm $ios-spacing-lg;
-  border-radius:$ios-radius-md;
-  font-size:$ios-font-size-sm;
-  font-weight:$ios-font-weight-medium;
-  cursor:pointer;
-  margin-right:$ios-spacing-md;
-  display:flex;
-  align-items:center;
-  gap:$ios-spacing-xs;
-}
-
-.organize-btn:hover{
-  background-color:#f0f0f0;
-  transform:translateY(-1px);
-}
-
-/* 技能匹配度样式 */
-.skills-match-bar{
-  width:100%;
-  height:8px;
-  background-color:$ios-background-tertiary;
-  border-radius:$ios-radius-full;
-  overflow:hidden;
-  margin-bottom:$ios-spacing-sm;
-}
-
-.skills-match-fill{
-  height:100%;
-  background-color:$ios-primary;
-  border-radius:$ios-radius-full;
-}
-
-.skills-match-text{
-  font-size:$ios-font-size-sm;
-  color:$ios-text-secondary;
-  text-align:right;
-}
-
-/* 主内容布局样式 - 强制使用主容器内的布局定义 */
-.progress-tab-content > .main-content-layout {
-  display: flex !important;
-  gap: $ios-spacing-xl !important;
-  margin-top: $ios-spacing-xl;
-  width: 100% !important;
-  background-color: rgba(200, 200, 255, 0.2) !important; /* 临时背景色用于调试 */
-}
-
-.progress-tab-content > .main-content-layout > .left-column {
-  width: 33.333% !important;
-  display: flex !important;
-  flex-direction: column !important;
-  gap: $ios-spacing-xl !important;
-}
-
-.progress-tab-content > .main-content-layout > .right-column {
-  width: 66.667% !important;
-  display: flex !important;
-  flex-direction: column !important;
-  gap: $ios-spacing-xl !important;
-}
-
-/* 标题样式 */
-h2{font-size:$ios-font-size-lg;font-weight:$ios-font-weight-semibold;color:$ios-text-primary;margin-top:0;margin-bottom:$ios-spacing-lg;padding-bottom:$ios-spacing-sm;border-bottom:1px solid $ios-border}
-h3{font-size:$ios-font-size-base;font-weight:$ios-font-weight-medium;color:$ios-text-primary;margin-top:$ios-spacing-lg;margin-bottom:$ios-spacing-md}
-h4{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-text-primary;margin-top:$ios-spacing-md;margin-bottom:$ios-spacing-sm}
-
-/* 项目基本信息卡片 */
-.project-info-card .info-grid{display:grid;grid-template-columns:1fr 1fr;gap:$ios-spacing-md}
-.info-item{display:flex;flex-direction:column;gap:$ios-spacing-xs}
-.info-item label{font-size:$ios-font-size-sm;color:$ios-text-secondary;font-weight:$ios-font-weight-medium}
-.info-item span{font-size:$ios-font-size-base;color:$ios-text-primary;font-weight:$ios-font-weight-regular}
-.stage-tag{display:inline-block;padding:$ios-spacing-xs $ios-spacing-md;border-radius:$ios-radius-full;background-color:#e6f7e6;color:$ios-success;font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium}
-
-/* 客户画像卡片 */
-.warning-banner{background-color:#ffebee;color:$ios-danger;padding:$ios-spacing-md $ios-spacing-lg;border-radius:$ios-radius-md;margin-bottom:$ios-spacing-xl;border-left:4px solid $ios-danger;font-size:$ios-font-size-base;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:$ios-spacing-md}
-.warning-content{display:flex;align-items:center;gap:$ios-spacing-sm;flex:1}
-.warning-icon{font-size:$ios-font-size-lg}
-.contact-leader-btn{background-color:$ios-danger;color:white;border:none;padding:$ios-spacing-xs $ios-spacing-md;border-radius:$ios-radius-md;font-size:$ios-font-size-sm;cursor:pointer;font-weight:$ios-font-weight-medium}
-.contact-leader-btn:hover{background-color:#c62828;transform:translateY(-1px)}
-
-/* 标签样式 */
-.tags-container{display:flex;flex-direction:column;gap:$ios-spacing-lg}
-.tag-section{margin-bottom:$ios-spacing-md}
-.tag-section h3{margin-top:0;font-size:$ios-font-size-base}
-.tags-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(120px, 1fr));gap:$ios-spacing-md}
-.tag-item{display:flex;flex-direction:column;gap:$ios-spacing-xs}
-.tag-label{font-size:$ios-font-size-xs;color:$ios-text-secondary;font-weight:$ios-font-weight-medium}
-.tag{display:inline-block;padding:$ios-spacing-xs $ios-spacing-md;border-radius:$ios-radius-full;background-color:$ios-background-tertiary;color:$ios-text-primary;font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;align-self:flex-start}
-.tags-flex{display:flex;flex-direction:column;gap:$ios-spacing-md}
-.tag-group{display:flex;flex-direction:column;gap:$ios-spacing-sm}
-.group-label{font-size:$ios-font-size-sm;color:$ios-text-secondary;font-weight:$ios-font-weight-medium}
-.tags{display:flex;flex-wrap:wrap;gap:$ios-spacing-sm}
-.priority-tag,.skill-tag{display:inline-block;padding:$ios-spacing-xs $ios-spacing-sm;border-radius:$ios-radius-full;background-color:$ios-background-tertiary;color:$ios-text-primary;font-size:$ios-font-size-xs;font-weight:$ios-font-weight-medium}
-.priority-tag{background-color:#fff3cd;color:$ios-warning}
-.skill-tag{background-color:#e8f0fe;color:$ios-primary}
-
-/* 项目进度看板 - 支持10个阶段的横向进度展示 */
-.stage-progress-container{
-  margin-bottom:$ios-spacing-xl;
-  background-color:$ios-background-secondary;
-  border-radius:$ios-radius-lg;
-  padding:$ios-spacing-lg;
-  box-shadow:0 2px 8px rgba(0,0,0,0.03);
-}
-
-/* 进度条容器 - 支持滚动查看所有10个阶段 */
-.stage-progress-wrapper{
-  position:relative;
-  overflow-x:auto;
-  padding-bottom:$ios-spacing-md;
-  /* 隐藏滚动条但保持功能 */
-  scrollbar-width:none; /* Firefox */
-  -ms-overflow-style:none; /* IE and Edge */
-}
-
-.stage-progress-wrapper::-webkit-scrollbar{
-  display:none; /* Chrome, Safari, Opera */
-}
-
-/* 进度条容器内部样式 */
-.stage-progress{
-  display:flex;
-  align-items:center;
-  position:relative;
-  padding:$ios-spacing-sm 0;
-  min-width:100%;
-}
-
-/* 进度线 */
-.stage-progress::before{
-  content:'';
-  position:absolute;
-  top:50%;
-  left:22px; /* 图标宽度的一半 */
-  right:22px;
-  height:3px;
-  background-color:$ios-border;
-  transform:translateY(-50%);
-  z-index:1;
-}
-
-.progress-line{
-  position:absolute;
-  top:50%;
-  left:22px; /* 图标宽度的一半 */
-  height:3px;
-  background:linear-gradient(90deg, $ios-success, $ios-primary);
-  transform:translateY(-50%);
-  z-index:2;
-  transition:width 0.3s ease;
+@keyframes modalFadeIn {
+  from {
+    opacity: 0;
+    transform: scale(0.9);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1);
+  }
 }
 
-/* 阶段样式 - 10个阶段均匀分布 */
-.stage{
-  display:flex;
-  flex-direction:column;
-  align-items:center;
-  z-index:3;
-  position:relative;
-  flex-shrink:0;
-  width:100px;
-  cursor:pointer;
-  transition:transform 0.2s ease;
-  padding:0 $ios-spacing-sm;
+// 下拉菜单动画
+@keyframes dropdown-fade-in {
+  from {
+    opacity: 0;
+    transform: translateY(-8px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
 }
 
-.stage:hover{
-  transform:translateY(-2px);
+/* 项目标题栏 */
+.project-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: $ios-spacing-xl; // 增加底部间距
+  background-color: white;
+  padding: $ios-spacing-lg;
+  border-radius: $ios-radius-lg;
+  width: 100%;
+  box-sizing: border-box;
+  position: relative; // 确保下拉菜单正确定位
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  
+  .header-left {
+    flex: 1;
+    
+    .header-content {
+      h1 {
+        margin: 0 0 8px 0;
+        font-size: $ios-font-size-xl;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .project-meta {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-md;
+        
+        .project-id {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+        }
+        
+        .project-status {
+          padding: $ios-spacing-xs $ios-spacing-sm;
+          border-radius: $ios-radius-full;
+          font-size: $ios-font-size-xs;
+          font-weight: $ios-font-weight-medium;
+          background-color: $ios-primary;
+          color: white;
+        }
+      }
+    }
+  }
+  
+  .header-right {
+    .header-actions {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-sm;
+      
+      .action-btn {
+        padding: 10px 16px;
+        border: none;
+        border-radius: 8px;
+        font-size: 14px;
+        font-weight: 500;
+        cursor: pointer;
+        transition: all 0.2s ease-in-out;
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        gap: 6px;
+        min-height: 40px;
+        white-space: nowrap;
+        text-decoration: none;
+        
+        svg {
+          flex-shrink: 0;
+        }
+        
+        &:focus {
+          outline: 2px solid $ios-primary;
+          outline-offset: 2px;
+        }
+        
+        &:hover {
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+        }
+        
+        &:active {
+          transform: translateY(0);
+          box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+        }
+        
+        // 主要按钮样式(返回工作台)
+        &.primary {
+          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+          color: white;
+          font-weight: 600;
+          
+          &:hover {
+            background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
+          }
+        }
+        
+        // 次要按钮样式
+        &.secondary-btn {
+          background-color: #f8f9fa;
+          color: #495057;
+          border: 1px solid #e9ecef;
+          
+          &:hover {
+            background-color: #e9ecef;
+            border-color: #dee2e6;
+          }
+        }
+        
+        // 停滞按钮样式
+        &.stagnation-btn {
+          background-color: #fff3cd;
+          color: #856404;
+          border: 1px solid #ffeaa7;
+          
+          &:hover {
+            background-color: #ffeaa7;
+            border-color: #ffdd57;
+          }
+        }
+        
+        // 切换项目按钮样式
+        &.switch-btn {
+          background-color: #e3f2fd;
+          color: #1565c0;
+          border: 1px solid #bbdefb;
+          
+          .dropdown-icon {
+            transition: transform 0.2s ease-in-out;
+          }
+          
+          &:hover {
+            background-color: #bbdefb;
+            border-color: #90caf9;
+          }
+        }
+      }
+      
+      // 项目切换器
+      .project-switcher {
+        position: relative;
+        
+        .switch-dropdown {
+          position: absolute;
+          top: calc(100% + 8px);
+          right: 0;
+          background: white;
+          border: 1px solid #e9ecef;
+          border-radius: 8px;
+          box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+          z-index: 1000;
+          min-width: 280px;
+          max-height: 300px;
+          overflow-y: auto;
+          animation: dropdown-fade-in 0.2s ease-out;
+          
+          .project-item {
+            padding: 12px 16px;
+            cursor: pointer;
+            border-bottom: 1px solid #f8f9fa;
+            transition: background-color 0.2s ease-in-out;
+            
+            &:last-child {
+              border-bottom: none;
+            }
+            
+            &:hover {
+              background-color: #f8f9fa;
+            }
+            
+            &.active {
+              background-color: #e3f2fd;
+              
+              .project-name {
+                color: #1565c0;
+                font-weight: 600;
+              }
+            }
+            
+            .project-name {
+              display: block;
+              font-weight: 500;
+              margin-bottom: 4px;
+            }
+            
+            .project-status-badge {
+              font-size: 12px;
+              padding: 2px 8px;
+              border-radius: 12px;
+              
+              &.ongoing {
+                background-color: #e8f5e8;
+                color: #2e7d32;
+              }
+              
+              &.completed {
+                background-color: #e3f2fd;
+                color: #1565c0;
+              }
+              
+              &.pending {
+                background-color: #fff3e0;
+                color: #ef6c00;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 }
 
-/* 阶段图标 - 现代化设计 */
-.stage-icon{
-  width:44px;
-  height:44px;
-  border-radius:50%;
-  background-color:$ios-border;
-  color:$ios-text-secondary;
-  display:flex;
-  justify-content:center;
-  align-items:center;
-  font-weight:$ios-font-weight-semibold;
-  margin-bottom:$ios-spacing-sm;
-  transition:all 0.3s ease;
-  box-shadow:0 2px 4px rgba(0,0,0,0.05);
+// 下拉菜单动画
+@keyframes dropdown-fade-in {
+  from {
+    opacity: 0;
+    transform: translateY(-8px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
 }
 
-/* 已完成阶段样式 */
-.stage.completed .stage-icon{
-  background:linear-gradient(135deg, $ios-success, #2e7d32);
-  color:white;
-  box-shadow:0 0 0 4px #e6f7e6, 0 4px 12px rgba(46, 125, 50, 0.2);
+// 响应式设计
+@media (max-width: 768px) {
+  .project-header {
+    flex-direction: column;
+    align-items: stretch;
+    gap: 16px;
+    
+    .header-right {
+      .header-actions {
+        flex-wrap: wrap;
+        justify-content: flex-end;
+        gap: 8px;
+        
+        .action-btn {
+          font-size: 13px;
+          padding: 8px 12px;
+          min-height: 36px;
+        }
+      }
+    }
+  }
 }
 
-/* 当前阶段样式 - 强调设计 */
-.stage.active .stage-icon{
-  background:linear-gradient(135deg, $ios-primary, #0056b3);
-  color:white;
-  box-shadow:0 0 0 4px #e8f0fe, 0 4px 16px rgba(0, 123, 255, 0.3);
-  animation:pulse 2s infinite;
-}
+/* 项目内容区域 */
+.project-content {
+  .action-btn {
+    padding: $ios-spacing-sm $ios-spacing-md;
+    border: 1px solid $ios-border;
+    border-radius: $ios-radius-md;
+    font-size: $ios-font-size-sm;
+    font-weight: $ios-font-weight-medium;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    display: inline-flex;
+    align-items: center;
+    gap: $ios-spacing-xs;
+    
+    svg {
+      width: 16px;
+      height: 16px;
+      fill: currentColor;
+    }
+      
+    &:hover {
+      background-color: $ios-background-tertiary;
+      transform: translateY(-1px);
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      border-color: color-mix(in srgb, $ios-border 70%, $ios-primary);
+    }
+    
+    &:active {
+      transform: translateY(0);
+      background-color: $ios-background-secondary;
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    }
+  }
+    
+    .switch-btn {
+      background-color: $ios-primary;
+      color: white;
+      position: relative;
+      
+      &:hover {
+        background-color: color-mix(in srgb, $ios-primary 85%, black);
+        transform: translateY(-1px);
+        box-shadow: 0 4px 12px rgba(22, 93, 255, 0.25);
+      }
+      
+      &:active {
+        transform: translateY(0);
+        box-shadow: 0 2px 6px rgba(22, 93, 255, 0.2);
+      }
+    }
+    
+    .secondary-btn {
+      background-color: $ios-background-secondary;
+      color: $ios-text-primary;
+      border: 1px solid $ios-border;
+      
+      &:hover {
+        background-color: $ios-background-tertiary;
+        transform: translateY(-1px);
+        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+        border-color: color-mix(in srgb, $ios-border 70%, $ios-primary);
+      }
+      
+      &:active {
+        transform: translateY(0);
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+      }
+    }
+    
+    .stagnation-btn {
+      background-color: $ios-warning;
+      color: white;
+      
+      &:hover {
+        background-color: color-mix(in srgb, $ios-warning 85%, black);
+        transform: translateY(-1px);
+        box-shadow: 0 4px 12px rgba(255, 125, 0, 0.25);
+      }
+      
+      &:active {
+        transform: translateY(0);
+        box-shadow: 0 2px 6px rgba(255, 125, 0, 0.2);
+      }
+    }
+    
+    .project-switcher {
+      position: relative;
+      
+      .switch-dropdown {
+        position: absolute;
+        top: 100%;
+        right: 0;
+        margin-top: $ios-spacing-xs;
+        background-color: white;
+        border: 1px solid $ios-border;
+        border-radius: $ios-radius-md;
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+        min-width: 280px;
+        max-height: 300px;
+        overflow-y: auto;
+        z-index: 1000;
+        
+        // 添加动画效果
+        animation: dropdown-fade-in 0.2s ease-out;
+        
+        .project-item {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          padding: $ios-spacing-md;
+          cursor: pointer;
+          transition: all 0.15s ease-in-out;
+          border-bottom: 1px solid $ios-border;
+          
+          &:last-child {
+            border-bottom: none;
+          }
+          
+          &:hover {
+            background-color: $ios-background-secondary;
+            transform: translateX(2px);
+          }
+          
+          &.active {
+            background-color: color-mix(in srgb, $ios-primary 10%, white);
+            color: $ios-primary;
+            border-left: 3px solid $ios-primary;
+            padding-left: calc(#{$ios-spacing-md} - 3px);
+          }
+          
+          .project-name {
+            font-size: $ios-font-size-sm;
+            font-weight: $ios-font-weight-medium;
+            color: $ios-text-primary;
+          }
+          
+          .project-status-badge {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border-radius: $ios-radius-full;
+            font-size: $ios-font-size-xs;
+            font-weight: $ios-font-weight-medium;
+            
+            &.ongoing {
+              background-color: color-mix(in srgb, $ios-success 15%, white);
+              color: $ios-success;
+              border: 1px solid color-mix(in srgb, $ios-success 30%, white);
+            }
+            
+            &.completed {
+              background-color: color-mix(in srgb, $ios-primary 15%, white);
+              color: $ios-primary;
+              border: 1px solid color-mix(in srgb, $ios-primary 30%, white);
+            }
+            
+            &.pending {
+              background-color: color-mix(in srgb, $ios-warning 15%, white);
+              color: $ios-warning;
+              border: 1px solid color-mix(in srgb, $ios-warning 30%, white);
+            }
+          }
+        }
+      }
+    }
+  }
 
-@keyframes pulse {
-  0% {
-    box-shadow:0 0 0 4px #e8f0fe, 0 4px 16px rgba(0, 123, 255, 0.3);
+/* 主内容布局 */
+.main-content-layout {
+  display: grid;
+  grid-template-columns: 1fr 350px; // 调整为两列:主内容区和右侧栏
+  gap: $ios-spacing-lg;
+  max-width: calc(100vw - 40px);
+  margin: 0 auto;
+  overflow: visible;
+  align-items: start;
+  
+  // 移除导航栏列相关样式
+  .nav-column {
+    display: none; // 隐藏导航栏列
   }
-  50% {
-    box-shadow:0 0 0 6px #e8f0fe, 0 4px 20px rgba(0, 123, 255, 0.4);
+  
+  .left-column {
+    padding: 0;
+    min-width: 0;
+    flex: 1;
   }
-  100% {
-    box-shadow:0 0 0 4px #e8f0fe, 0 4px 16px rgba(0, 123, 255, 0.3);
+  
+  .right-column {
+    padding: 0;
+    min-width: 300px;
+    max-width: 400px;
   }
 }
 
-/* 未开始阶段样式 */
-.stage.pending .stage-icon{
-  background-color:$ios-background-tertiary;
-  color:$ios-text-secondary;
-}
-
-/* 阶段名称样式 - 响应式设计 */
-.stage-name{
-  font-size:$ios-font-size-xs;
-  color:$ios-text-secondary;
-  text-align:center;
-  white-space:nowrap;
-  font-weight:$ios-font-weight-medium;
-  max-width:100%;
-  overflow:hidden;
-  text-overflow:ellipsis;
-  transition:color 0.3s ease;
-}
-
-.stage.completed .stage-name,
-.stage.active .stage-name{
-  color:$ios-text-primary;
-  font-weight:$ios-font-weight-semibold;
-}
-
-/* 当前阶段操作区域 */
-.current-stage-actions{
-  background-color:$ios-background;
-  padding:$ios-spacing-lg;
-  border-radius:$ios-radius-md;
-  text-align:center;
-  margin-top:$ios-spacing-lg;
-  border-left:4px solid $ios-primary;
-  box-shadow:0 2px 8px rgba(0,0,0,0.05);
-}
-.current-stage-info h3{margin-top:0;margin-bottom:$ios-spacing-md;font-size:$ios-font-size-base}
-.stage-highlight{color:$ios-primary;font-weight:$ios-font-weight-semibold}
-
-/* 按钮样式 */
-.primary-btn,.secondary-btn{border:none;padding:$ios-spacing-sm $ios-spacing-lg;border-radius:$ios-radius-md;font-size:$ios-font-size-sm;cursor:pointer;font-weight:$ios-font-weight-medium;white-space:nowrap}
-.primary-btn{background-color:$ios-primary;color:white}
-.primary-btn:hover{background-color:#0056b3;transform:translateY(-1px)}
-.primary-btn:disabled{background-color:$ios-border;color:$ios-text-secondary;cursor:not-allowed;transform:none}
-.secondary-btn{background-color:$ios-background-tertiary;color:$ios-text-primary}
-.secondary-btn:hover{background-color:#f0f0f0;transform:translateY(-1px)}
-
-/* 渲染进度卡片 */
-.loading-state,.error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:$ios-spacing-xl;text-align:center;gap:$ios-spacing-md}
-.loading-spinner{width:40px;height:40px;border:3px solid $ios-border;border-top:3px solid $ios-primary;border-radius:50%;animation:spin 1s linear infinite}
-@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
-.timeout-warning{background-color:#fff3cd;color:$ios-warning;padding:$ios-spacing-md;border-radius:$ios-radius-md;margin-bottom:$ios-spacing-lg;display:flex;align-items:center;gap:$ios-spacing-sm;border-left:4px solid $ios-warning}
-.warning-title{font-weight:$ios-font-weight-medium;display:block}
-.warning-time{font-size:$ios-font-size-sm;display:block}
-
-/* 渲染异常反馈模块样式 */
-.render-exception-section {
-  background-color: #f8f9fa;
-  border-radius: 8px;
-  padding: 20px;
-  margin-bottom: 24px;
-}
-
-/* 客户反馈和设计师变更记录卡片并排布局 */
-.additional-info-section {
-  display: flex;
-  gap: $ios-spacing-xl;
-  margin-bottom: $ios-spacing-xl;
-  margin-top: $ios-spacing-xxl; /* 增加与上方内容的间距 */
-  width: 100%;
-  align-items: flex-start;
-  padding: $ios-spacing-xl; /* 添加内边距 */
-  background-color: $ios-background-secondary; /* 添加矩形背景 */
-  border-radius: $ios-radius-lg; /* 添加圆角 */
-  border: 1px solid $ios-border; /* 添加边框 */
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); /* 添加轻微阴影增加层次感 */
-}
-
-.feedback-card,
-.designer-change-card {
-  width: 50%;
-  margin: 0 !important;
-  min-height: 400px; /* 设置最小高度确保两个卡片高度一致 */
-  display: flex;
-  flex-direction: column;
-}
+// 响应式设计优化
+@media (max-width: 1400px) {
+  .project-detail-container {
+    padding: $ios-spacing-md;
+  }
 
-/* 优化客户反馈卡片内部布局 */
-.feedback-card .card-content {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
+  .main-content-layout {
+    grid-template-columns: 1fr 300px; // 中等屏幕调整
+    gap: $ios-spacing-md;
+  }
+  
+  .horizontal-nav-container {
+    margin-bottom: $ios-spacing-md;
+    padding: $ios-spacing-sm;
+  }
 }
 
-.feedback-item {
-  background-color: $ios-background;
-  border-radius: $ios-radius-md;
-  padding: $ios-spacing-md;
-  margin-bottom: $ios-spacing-md;
-  border: 1px solid $ios-border;
-}
+@media (max-width: 1200px) {
+  .project-detail-container {
+    padding: $ios-spacing-sm;
+  }
 
-.feedback-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: flex-start;
-  margin-bottom: $ios-spacing-sm;
+  .main-content-layout {
+    grid-template-columns: 1fr 280px; // 进一步减小右侧栏宽度
+    gap: $ios-spacing-sm;
+    
+    .right-column {
+      min-width: 260px;
+      max-width: 320px;
+    }
+  }
+  
+  .horizontal-nav {
+    .nav-item {
+      padding: $ios-spacing-sm $ios-spacing-md;
+      font-size: $ios-font-size-sm;
+    }
+  }
 }
 
-.feedback-meta {
-  display: flex;
-  gap: $ios-spacing-sm;
-  flex-wrap: wrap;
+@media (max-width: 768px) {
+  .project-detail-container {
+    padding: $ios-spacing-sm;
+  }
+  
+  .main-content-layout {
+    grid-template-columns: 1fr;
+    gap: $ios-spacing-md;
+    
+    .left-column,
+    .right-column {
+      width: 100%;
+      min-width: unset;
+      max-width: unset;
+      margin-bottom: $ios-spacing-md;
+    }
+  }
+  
+  .horizontal-nav-container {
+    margin-bottom: $ios-spacing-md;
+    padding: $ios-spacing-xs;
+  }
+  
+  .horizontal-nav {
+    .nav-item {
+      padding: $ios-spacing-sm;
+      font-size: $ios-font-size-sm;
+      
+      .nav-text {
+        font-size: $ios-font-size-sm;
+      }
+    }
+  }
+  
+  .project-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: $ios-spacing-md;
+    
+    .header-actions {
+      width: 100%;
+      justify-content: flex-start;
+      flex-wrap: wrap;
+      gap: $ios-spacing-sm;
+    }
+  }
 }
 
-/* 优化设计师变更记录卡片内部布局 */
-.designer-change-card .card-content {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
+@media (max-width: 480px) {
+  .project-detail-container {
+    padding: $ios-spacing-xs;
+  }
+  
+  .horizontal-nav {
+    .nav-item {
+      padding: $ios-spacing-xs $ios-spacing-sm;
+      font-size: $ios-font-size-xs;
+      
+      .nav-text {
+        font-size: $ios-font-size-xs;
+      }
+    }
+  }
+  
+  .project-header {
+    padding: $ios-spacing-md;
+    
+    .header-actions {
+      flex-direction: column;
+      align-items: stretch;
+      
+      .action-btn {
+        width: 100%;
+        margin-bottom: $ios-spacing-xs;
+      }
+    }
+  }
 }
 
-.designer-change-card .change-actions {
+/* 阶段详情网格 */
+.stage-details-grid {
   display: flex;
   gap: $ios-spacing-md;
-  margin-bottom: $ios-spacing-md;
-  flex-wrap: wrap;
+  overflow-x: auto;
+  padding-bottom: $ios-spacing-sm;
 }
 
-.change-item {
-  background-color: $ios-background;
+.stage-details-cell {
+  flex: 1 1 auto;
+  min-width: 200px;
+  background-color: $ios-background-secondary;
   border-radius: $ios-radius-md;
   padding: $ios-spacing-md;
-  margin-bottom: $ios-spacing-md;
-  border: 1px solid $ios-border;
-}
-
-.change-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: $ios-spacing-sm;
 }
 
-/* 空状态样式优化 */
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  padding: $ios-spacing-xl;
-  height: 200px;
-  background-color: $ios-background-tertiary;
-  border-radius: $ios-radius-md;
-  margin-top: $ios-spacing-md;
-}
+@media (max-width: 1200px) {
+  .project-detail-container {
+    padding: $ios-spacing-sm; // 减小容器内边距
+  }
 
-.empty-icon {
-  font-size: 48px;
-  margin-bottom: $ios-spacing-md;
-  opacity: 0.6;
+  .main-content-layout {
+    grid-template-columns: 160px 1fr 260px; // 进一步减小列宽
+    gap: $ios-spacing-sm; // 减小间距
+  }
 }
 
-/* 响应式调整 */
 @media (max-width: 768px) {
-  .additional-info-section {
-    flex-direction: column;
-  }
-  
-  .feedback-card,
-  .designer-change-card {
-    width: 100%;
-    margin-bottom: $ios-spacing-xl !important;
+  .project-detail-container {
+    padding: $ios-spacing-xs; // 移动端进一步减小内边距
   }
-}
-
-/* 模型误差检查清单在制作流程进度卡片中的样式 */
-.model-check-section {
-  margin-top: $ios-spacing-lg;
-  padding: $ios-spacing-lg;
-  background-color: $ios-background;
-  border-radius: $ios-radius-md;
-  border-left: 4px solid $ios-primary;
-}
-
-.model-check-section h3 {
-  margin-top: 0;
-  margin-bottom: $ios-spacing-md;
-  color: $ios-text-primary;
-  font-size: $ios-font-size-base;
-  font-weight: $ios-font-weight-medium;
-}
-
-.model-check-section .checklist {
-  gap: $ios-spacing-sm;
-}
-
-.model-check-section .checklist-item {
-  padding: $ios-spacing-sm $ios-spacing-md;
-  background-color: $ios-background-tertiary;
-  transition: all 0.2s ease;
-}
-
-.model-check-section .checklist-item:hover {
-  background-color: #f0f0f0;
-  transform: translateX(4px);
-}
-
-.model-check-section .custom-checkbox {
-  width: 18px;
-  height: 18px;
-}
-
-.model-check-section .checklist-text {
-  font-size: $ios-font-size-sm;
-}
-
-.model-check-section .check-status {
-  font-size: $ios-font-size-xs;
-}
-
-.render-exception-section h3 {
-  margin-top: 0;
-  margin-bottom: 16px;
-  color: #333;
-  font-size: 18px;
-  font-weight: 600;
-}
-
-.exception-feedback-form {
-  background-color: white;
-  padding: 16px;
-  border-radius: 8px;
-  margin-bottom: 20px;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-}
-
-.form-group {
-  margin-bottom: 16px;
-}
-
-.form-group label {
-  display: block;
-  margin-bottom: 6px;
-  color: #555;
-  font-weight: 500;
-  font-size: 14px;
-}
-
-.exception-select {
-  width: 100%;
-  padding: 8px 12px;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  font-size: 14px;
-  background-color: white;
-  cursor: pointer;
-}
-
-.exception-textarea {
-  width: 100%;
-  min-height: 120px;
-  padding: 10px 12px;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  font-size: 14px;
-  resize: vertical;
-  font-family: inherit;
-}
-
-.screenshot-upload {
-  display: none;
-}
 
-.upload-btn {
-  display: inline-block;
-  padding: 8px 16px;
-  background-color: #007bff;
-  color: white;
-  border-radius: 4px;
-  cursor: pointer;
-  font-size: 14px;
-  font-weight: 500;
-  transition: background-color 0.2s;
-}
-
-.upload-btn:hover {
-  background-color: #0056b3;
-}
-
-.screenshot-preview {
-  margin-top: 12px;
-  position: relative;
-  max-width: 200px;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  overflow: hidden;
-}
+  .main-content-layout {
+    grid-template-columns: 1fr;
+    gap: $ios-spacing-sm;
 
-.screenshot-preview img {
-  width: 100%;
-  display: block;
-}
-
-.clear-screenshot-btn {
-  position: absolute;
-  top: 5px;
-  right: 5px;
-  background-color: rgba(0, 0, 0, 0.5);
-  color: white;
-  border: none;
-  border-radius: 50%;
-  width: 24px;
-  height: 24px;
-  font-size: 16px;
-  line-height: 1;
-  cursor: pointer;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.clear-screenshot-btn:hover {
-  background-color: rgba(0, 0, 0, 0.7);
-}
-
-.submit-feedback-btn {
-  padding: 10px 20px;
-  background-color: #28a745;
-  color: white;
-  border: none;
-  border-radius: 4px;
-  font-size: 14px;
-  font-weight: 600;
-  cursor: pointer;
-  transition: background-color 0.2s;
-}
-
-.submit-feedback-btn:hover:not(:disabled) {
-  background-color: #218838;
-}
-
-.submit-feedback-btn:disabled {
-  background-color: #6c757d;
-  cursor: not-allowed;
+    .left-column,
+    .right-column {
+      min-width: unset;
+      max-width: unset;
+    }
+  }
 }
 
-/* 历史反馈记录样式 */
-.exception-history {
+/* 水平导航栏容器 */
+.horizontal-nav-container {
+  margin-bottom: $ios-spacing-lg;
   background-color: white;
-  padding: 16px;
-  border-radius: 8px;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-}
-
-.exception-history h4 {
-  margin-top: 0;
-  margin-bottom: 12px;
-  color: #555;
-  font-size: 16px;
-  font-weight: 600;
-}
-
-.history-list {
-  max-height: 300px;
-  overflow-y: auto;
-}
-
-.history-item {
-  padding: 12px;
-  border: 1px solid #eee;
-  border-radius: 4px;
-  margin-bottom: 12px;
-  transition: border-color 0.2s;
-}
-
-.history-item:hover {
-  border-color: #ddd;
-}
-
-.history-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 8px;
-}
-
-.history-type {
-  font-weight: 600;
-  color: #333;
-  font-size: 14px;
-}
-
-.history-time {
-  font-size: 12px;
-  color: #999;
-}
-
-.history-description {
-  font-size: 14px;
-  color: #555;
-  margin-bottom: 8px;
-  line-height: 1.4;
-}
-
-.history-status {
-  font-size: 12px;
-  padding: 4px 8px;
-  border-radius: 3px;
-  display: inline-block;
-}
-
-.history-status.status-pending {
-  background-color: #fff3cd;
-  color: #856404;
-}
-
-.history-status.status-processing {
-  background-color: #d1ecf1;
-  color: #0c5460;
-}
+  border-radius: $ios-radius-lg;
+  padding: $ios-spacing-md;
+  box-shadow: $ios-shadow-sm;
+}
+
+/* 水平导航栏样式 */
+.horizontal-nav {
+  .vertical-nav {
+    display: flex;
+    flex-direction: row; // 改为横向排列
+    gap: 0; // 移除间距,让按钮紧密排列
+    padding: 0;
+    background: transparent;
+    border: none;
+    box-shadow: none;
+    min-width: unset;
+    max-width: unset;
+    width: 100%;
+  }
 
-.history-status.status-resolved {
-  background-color: #d4edda;
-  color: #155724;
+  .nav-item {
+    flex: 1; // 每个按钮均分宽度
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: $ios-spacing-md $ios-spacing-lg;
+    border-radius: 0; // 移除圆角
+    cursor: pointer;
+    transition: all 0.2s ease;
+    color: $ios-text-primary;
+    text-decoration: none;
+    font-size: $ios-font-size-md;
+    font-weight: $ios-font-weight-medium;
+    line-height: 1.4;
+    background: transparent;
+    border: none;
+    border-bottom: 3px solid transparent; // 添加底部边框用于激活状态
+
+    &:first-child {
+      border-top-left-radius: $ios-radius-md;
+      border-bottom-left-radius: $ios-radius-md;
+    }
+
+    &:last-child {
+      border-top-right-radius: $ios-radius-md;
+      border-bottom-right-radius: $ios-radius-md;
+    }
+
+    &:hover {
+      background-color: $ios-background-secondary;
+    }
+
+    &.active {
+      background-color: rgba(0, 122, 255, 0.1); // 使用透明度替代不存在的变量
+      color: $ios-primary;
+      font-weight: $ios-font-weight-semibold;
+      border-bottom-color: $ios-primary; // 激活状态的底部边框
+    }
+
+    .nav-text {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      flex: 1;
+      text-align: center;
+    }
+
+    // 移除图标相关样式,因为当前导航项没有图标
+    .nav-icon {
+      display: none;
+    }
+  }
 }
 
-/* 响应式设计 */
-@media (max-width: 768px) {
-  .render-exception-section {
-    padding: 16px;
-  }
-  
-  .form-group {
-    margin-bottom: 12px;
+// 优化交付执行模块中的"选择图片"按钮样式 - 提高选择器特异性
+.project-detail-container.designer-page {
+  .upload-section {
+    margin: 20px 0;
+    
+    .upload-header {
+      margin-bottom: 16px;
+      
+      h4 {
+        margin: 0 0 8px 0;
+        font-size: 16px;
+        font-weight: 600;
+        color: #333;
+      }
+      
+      .hint {
+        font-size: 14px;
+        color: #666;
+      }
+    }
+    
+    // 新的大型上传框样式
+    .upload-dropzone {
+      width: 100%;
+      min-height: 200px;
+      border: 2px dashed #d0d7de;
+      border-radius: 12px;
+      background: #f8f9fa;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      position: relative;
+      margin-bottom: 16px;
+      
+      &:hover {
+        border-color: #4A90E2;
+        background: #f0f6ff;
+        
+        .upload-icon {
+          color: #4A90E2;
+          transform: scale(1.1);
+        }
+        
+        .upload-text {
+          color: #4A90E2;
+        }
+      }
+      
+      &.drag-over {
+        border-color: #4A90E2;
+        background: #e6f3ff;
+        
+        .upload-icon {
+          color: #4A90E2;
+          animation: bounce 0.6s ease-in-out infinite alternate;
+        }
+      }
+      
+      // 有图片时的样式
+      &.has-images {
+        min-height: 250px;
+        padding: 16px;
+        cursor: default;
+        
+        &:hover {
+          border-color: #d0d7de;
+          background: #f8f9fa;
+        }
+      }
+      
+      .upload-icon {
+        width: 48px;
+        height: 48px;
+        margin-bottom: 12px;
+        color: #8b949e;
+        transition: all 0.3s ease;
+        
+        // 加号图标样式
+        &::before {
+          content: '+';
+          font-size: 48px;
+          font-weight: 300;
+          line-height: 1;
+          display: block;
+        }
+      }
+      
+      .upload-text {
+        font-size: 16px;
+        font-weight: 500;
+        color: #656d76;
+        margin-bottom: 8px;
+        text-align: center;
+        transition: color 0.3s ease;
+      }
+      
+      .upload-hint {
+        font-size: 14px;
+        color: #8b949e;
+        text-align: center;
+        line-height: 1.4;
+      }
+      
+      // 上传后的图片网格
+      .uploaded-images-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+        gap: 12px;
+        width: 100%;
+        
+        .uploaded-image-item {
+          position: relative;
+          aspect-ratio: 1;
+          border-radius: 8px;
+          overflow: hidden;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          
+          &:hover {
+            transform: scale(1.05);
+            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+            
+            .image-overlay {
+              opacity: 1;
+            }
+          }
+          
+          img {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+            border-radius: 8px;
+          }
+          
+          .image-overlay {
+            position: absolute;
+            top: 0;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            background: linear-gradient(to bottom, rgba(0,0,0,0.7) 0%, transparent 30%, transparent 70%, rgba(0,0,0,0.7) 100%);
+            opacity: 0;
+            transition: opacity 0.3s ease;
+            display: flex;
+            flex-direction: column;
+            justify-content: space-between;
+            padding: 8px;
+            
+            .image-name {
+              color: white;
+              font-size: 12px;
+              font-weight: 500;
+              text-shadow: 0 1px 2px rgba(0,0,0,0.5);
+              overflow: hidden;
+              text-overflow: ellipsis;
+              white-space: nowrap;
+            }
+            
+            .image-actions {
+              display: flex;
+              gap: 8px;
+              justify-content: center;
+              
+              button {
+                width: 32px;
+                height: 32px;
+                border: none;
+                border-radius: 6px;
+                background: rgba(255, 255, 255, 0.9);
+                color: #333;
+                cursor: pointer;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                transition: all 0.2s ease;
+                
+                &:hover {
+                  background: white;
+                  transform: scale(1.1);
+                }
+                
+                &.preview-btn:hover {
+                  color: #4A90E2;
+                }
+                
+                &.remove-btn:hover {
+                  color: #dc3545;
+                }
+              }
+            }
+          }
+        }
+        
+        .add-more-btn {
+          aspect-ratio: 1;
+          border: 2px dashed #d0d7de;
+          border-radius: 8px;
+          background: #f8f9fa;
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          justify-content: center;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          
+          &:hover {
+            border-color: #4A90E2;
+            background: #f0f6ff;
+            
+            .add-icon {
+              color: #4A90E2;
+              transform: scale(1.1);
+            }
+            
+            .add-text {
+              color: #4A90E2;
+            }
+          }
+          
+          .add-icon {
+            font-size: 24px;
+            color: #8b949e;
+            margin-bottom: 4px;
+            transition: all 0.3s ease;
+          }
+          
+          .add-text {
+            font-size: 12px;
+            color: #8b949e;
+            transition: color 0.3s ease;
+          }
+        }
+      }
+      
+      // 隐藏的文件输入框
+      input[type="file"] {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        opacity: 0;
+        cursor: pointer;
+      }
+    }
+    
+    // 弹跳动画
+    @keyframes bounce {
+      0% { transform: translateY(0); }
+      100% { transform: translateY(-8px); }
+    }
   }
   
-  .screenshot-preview {
-    max-width: 150px;
+  .upload-actions {
+    display: flex;
+    gap: 12px;
+    align-items: center;
+    margin-top: 16px;
+    
+    .secondary-btn {
+      // 保持原有的按钮样式,但调整为辅助按钮
+      padding: 8px 16px !important;
+      min-height: 36px;
+      font-size: 14px !important;
+      font-weight: 500 !important;
+      
+      background: #f6f8fa !important;
+      color: #24292f !important;
+      border: 1px solid #d0d7de !important;
+      border-radius: 8px !important;
+      
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
+      
+      transition: all 0.2s ease !important;
+      
+      &:hover {
+        background: #f3f4f6 !important;
+        border-color: #c7c7c7 !important;
+        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15) !important;
+      }
+      
+      &:active {
+        transform: translateY(1px) !important;
+        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
+      }
+    }
+    
+    .primary-btn {
+      padding: 8px 16px;
+      min-height: 36px;
+      font-size: 14px;
+      font-weight: 500;
+      
+      background: #4A90E2;
+      color: white;
+      border: 1px solid #4A90E2;
+      border-radius: 8px;
+      
+      box-shadow: 0 1px 3px rgba(74, 144, 226, 0.3);
+      
+      transition: all 0.2s ease;
+      cursor: pointer;
+      
+      &:hover:not(:disabled) {
+        background: #357ABD;
+        border-color: #357ABD;
+        box-shadow: 0 2px 6px rgba(74, 144, 226, 0.4);
+      }
+      
+      &:disabled {
+        background: #e9ecef;
+        color: #6c757d;
+        border-color: #dee2e6;
+        cursor: not-allowed;
+        box-shadow: none;
+      }
+    }
   }
 }
-.progress-bar-container{position:relative;margin-bottom:$ios-spacing-lg}
-.progress-bar{width:100%;height:12px;background-color:$ios-background-tertiary;border-radius:$ios-radius-full;overflow:hidden}
-.progress-fill{height:100%;background-color:$ios-primary;border-radius:$ios-radius-full}
-.progress-percentage{position:absolute;right:0;top:-$ios-spacing-md;font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-text-secondary}
-.progress-details{display:grid;grid-template-columns:1fr 1fr;gap:$ios-spacing-md}
-.progress-info{display:flex;flex-direction:column;gap:$ios-spacing-xs}
-.info-label{font-size:$ios-font-size-xs;color:$ios-text-secondary}
-.info-value{font-size:$ios-font-size-base;color:$ios-text-primary;font-weight:$ios-font-weight-medium}
-.checklist{display:flex;flex-direction:column;gap:$ios-spacing-md}
-.checklist-item{display:flex;align-items:center;padding:$ios-spacing-md;border-radius:$ios-radius-md;background-color:$ios-background-tertiary}
-.custom-checkbox{margin-right:$ios-spacing-md;width:20px;height:20px;accent-color:$ios-primary;border-radius:$ios-radius-sm}
-.checklist-text{color:$ios-text-primary;font-size:$ios-font-size-base;flex:1}
-.check-status{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;padding:$ios-spacing-xs $ios-spacing-sm;border-radius:$ios-radius-full}
-.check-status.passed{background-color:#e6f7e6;color:$ios-success}
-.check-status.failed{background-color:#ffebee;color:$ios-danger}
-.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:$ios-spacing-xl;text-align:center;gap:$ios-spacing-md;color:$ios-text-secondary}
-.empty-icon{font-size:$ios-font-size-xl}
-.feedback-item{background-color:$ios-background-tertiary;border-radius:$ios-radius-md;padding:$ios-spacing-lg;margin-bottom:$ios-spacing-md}
-.feedback-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:$ios-spacing-md}
-.feedback-meta{display:flex;align-items:center;gap:$ios-spacing-sm}
-.feedback-type{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-text-primary}
-.feedback-tag{font-size:$ios-font-size-xs;background-color:#e8f0fe;color:$ios-primary;padding:$ios-spacing-xs $ios-spacing-sm;border-radius:$ios-radius-full;font-weight:$ios-font-weight-medium}
-.feedback-date{font-size:$ios-font-size-xs;color:$ios-text-secondary}
-.feedback-content{margin-bottom:$ios-spacing-md}
-.feedback-status{display:flex;align-items:center;gap:$ios-spacing-xs;margin-bottom:$ios-spacing-sm;font-size:$ios-font-size-sm}
-.status-label{color:$ios-text-secondary}
-.status-value{color:$ios-text-primary;font-weight:$ios-font-weight-medium}
-.feedback-countdown{display:flex;align-items:center;gap:$ios-spacing-xs;padding:$ios-spacing-xs $ios-spacing-sm;background-color:#fff3cd;color:$ios-warning;border-radius:$ios-radius-full;font-size:$ios-font-size-xs;margin-bottom:$ios-spacing-md}
-.countdown-icon{font-size:$ios-font-size-sm}
-.feedback-details{display:flex;flex-direction:column;gap:$ios-spacing-xs}
-.detail-item{display:flex;gap:$ios-spacing-sm;font-size:$ios-font-size-sm}
-.detail-label{color:$ios-text-secondary;width:80px;flex-shrink:0}
-.detail-value{color:$ios-text-primary;flex:1}
-.feedback-actions{display:flex;gap:$ios-spacing-md;justify-content:flex-end}
-.change-actions{display:flex;gap:$ios-spacing-md;margin-bottom:$ios-spacing-lg;flex-wrap:wrap}
-.change-item{background-color:$ios-background-tertiary;border-radius:$ios-radius-md;padding:$ios-spacing-lg;margin-bottom:$ios-spacing-md}
-.change-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:$ios-spacing-md}
-.change-time{font-size:$ios-font-size-sm;color:$ios-text-secondary;font-weight:$ios-font-weight-semibold}
-.accept-change-btn{background-color:$ios-success;color:white;border:none;padding:$ios-spacing-xs $ios-spacing-md;border-radius:$ios-radius-md;font-size:$ios-font-size-sm;cursor:pointer;font-weight:$ios-font-weight-medium}
-.accept-change-btn:hover{background-color:#2e7d32;transform:translateY(-1px)}
-.designer-change-info{display:grid;grid-template-columns:1fr;gap:$ios-spacing-sm;margin-bottom:$ios-spacing-md}
-.designer-change{display:flex;gap:$ios-spacing-sm;align-items:center}
-.designer-label{color:$ios-text-secondary;font-size:$ios-font-size-sm;width:80px;flex-shrink:0}
-.designer-name{color:$ios-text-primary;font-size:$ios-font-size-base;font-weight:$ios-font-weight-medium}
-.workload-info{font-size:$ios-font-size-sm;color:$ios-text-secondary;margin-bottom:$ios-spacing-md}
-.achievements{margin-bottom:$ios-spacing-md}
-.achievements ul{margin:0;padding-left:$ios-spacing-lg}
-.achievements li{color:$ios-text-primary;font-size:$ios-font-size-sm;margin-bottom:$ios-spacing-xs;position:relative}
-.change-status{font-size:$ios-font-size-sm;color:$ios-text-secondary;background-color:$ios-background;padding:$ios-spacing-sm;border-radius:$ios-radius-md}
 
-/* 分阶段结算记录卡片 */
-.settlement-table{overflow-x:auto;border-radius:$ios-radius-md;background-color:$ios-background;border:1px solid $ios-border}
-.settlement-table table{width:100%;border-collapse:collapse}
-.settlement-table th,.settlement-table td{padding:$ios-spacing-md;text-align:left;border-bottom:1px solid $ios-border;font-size:$ios-font-size-sm}
-.settlement-table th{background-color:$ios-background-secondary;font-weight:$ios-font-weight-semibold;color:$ios-text-primary;position:sticky;top:0;z-index:10}
-.settlement-table td{color:$ios-text-primary;font-weight:$ios-font-weight-regular}
-.settlement-table tr:last-child td{border-bottom:none}
-.settlement-table tr:hover td{background-color:#f5f5f5}
-.status-badge{padding:$ios-spacing-xs $ios-spacing-sm;border-radius:$ios-radius-full;font-size:$ios-font-size-xs;font-weight:$ios-font-weight-medium}
-.status-pending{background-color:#fff3cd;color:$ios-warning}
-.status-settled{background-color:#e6f7e6;color:$ios-success}
-
-/* 响应式设计 */
-@media (max-width: 768px){
-.project-detail-container{padding:$ios-spacing-md}
-.main-content-layout {
-  flex-direction: column;
-}
-.left-column,
-.right-column {
-  width: 100%;
-}
-.project-header{flex-direction:column;align-items:flex-start;gap:$ios-spacing-md}
-.header-actions{width:100%;justify-content:flex-end}
-.project-info-card .info-grid{grid-template-columns:1fr;gap:$ios-spacing-md}
-.stage-progress{flex-direction:column;gap:$ios-spacing-lg}
-.stage-progress::before{display:none}
-.stage{flex-direction:row;gap:$ios-spacing-md;width:100%;justify-content:flex-start}
-.stage-icon{margin-bottom:0;width:36px;height:36px;font-size:$ios-font-size-sm}
-.progress-details{grid-template-columns:1fr;gap:$ios-spacing-md}
-.feedback-actions,.change-actions{flex-direction:column}
-.feedback-actions button,.change-actions button{width:100%}
-.tags-grid{grid-template-columns:1fr}
-.settlement-table th,.settlement-table td{padding:$ios-spacing-sm;font-size:$ios-font-size-xs}
-.stage-tag{font-size:$ios-font-size-xs}
-}
-
-/* 滚动条样式 */
-::-webkit-scrollbar{width:8px;height:8px}
-::-webkit-scrollbar-track{background:$ios-scrollbar-track;border-radius:$ios-radius-full}
-::-webkit-scrollbar-thumb{background:$ios-scrollbar-thumb;border-radius:$ios-radius-full}
-::-webkit-scrollbar-thumb:hover{background:$ios-scrollbar-thumb-hover}
-
-/* 上传与缩略图样式(新增) */
-.upload-section { margin-top: 16px; padding: 12px; background: #fafbfc; border: 1px dashed #e0e3e8; border-radius: 8px; }
-.upload-header { display:flex; align-items:center; gap:12px; margin-bottom: 8px; }
-.upload-header h4 { margin:0; font-size:$ios-font-size-base; }
-.upload-header .hint { color:$ios-text-secondary; font-size:$ios-font-size-xs; }
-.upload-actions { margin-bottom: 12px; }
-.thumb-list { display:grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap:12px; }
-.thumb-item { background:#fff; border:1px solid $ios-border; border-radius:8px; overflow:hidden; display:flex; flex-direction:column; }
-.thumb-item img { width:100%; height:100px; object-fit:cover; background:#f2f2f2; }
-.thumb-meta { padding:8px; display:flex; flex-direction:column; gap:4px; }
-.thumb-meta .name { font-size:$ios-font-size-xs; color:$ios-text-primary; line-height:1.2; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
-.thumb-meta .size { font-size:$ios-font-size-xs; color:$ios-text-secondary; }
-button.link { background:none; border:none; color:$ios-primary; cursor:pointer; padding:6px 8px; text-align:left; }
-button.link.danger { color:$ios-danger; }
-
-/* 弹窗样式(新增) */
-.modal-backdrop { position:fixed; inset:0; background: rgba(0,0,0,0.35); display:flex; align-items:center; justify-content:center; z-index: 999; }
-.modal { width: 720px; max-width: calc(100% - 48px); background:#fff; border-radius:12px; box-shadow: 0 12px 24px rgba(0,0,0,0.15); overflow:hidden; }
-.modal-header { display:flex; align-items:center; justify-content:space-between; padding:12px 16px; border-bottom:1px solid $ios-border; }
-.modal-body { padding:16px; }
-.modal-footer { padding:12px 16px; border-top:1px solid $ios-border; display:flex; gap:12px; justify-content:flex-end; }
-.close-button { background: transparent; border: none; font-size: 20px; cursor: pointer; line-height: 1; }
-
-/* 兼容暗色系(若未来启用) */
-@media (prefers-color-scheme: dark) {
-  .upload-section { background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,0.15); }
-  .thumb-item { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.12); }
-  .modal { background: #111315; }
-}
-/* 阶段详情:与上方阶段横向对齐的横向卡片列表 */
-.stage-details-grid{
-  display:flex;
-  gap:$ios-spacing-md;
-  align-items:stretch;
-  padding-top:$ios-spacing-md;
-  overflow-x:auto;
-  scrollbar-width:none; /* Firefox */
-  -ms-overflow-style:none; /* IE and Edge */
+// 响应式设计优化
+@media (max-width: 768px) {
+  .project-detail-container.designer-page {
+    .upload-actions {
+      flex-direction: column;
+      gap: 12px;
+      
+      .secondary-btn {
+        width: 100% !important;
+        padding: 16px 20px !important;
+        font-size: 16px !important;
+        min-height: 48px;
+        
+        &::after {
+          font-size: 18px;
+        }
+      }
+      
+      .primary-btn {
+        width: 100%;
+        padding: 16px 20px;
+        font-size: 16px;
+        min-height: 48px;
+      }
+    }
+  }
 }
-.stage-details-grid::-webkit-scrollbar{ display:none; }
-.stage-details-cell{ flex:0 0 280px; }
 
-@media (max-width: 768px){
-  .stage-details-grid{flex-direction:column;overflow-x:visible}
-  .stage-details-cell{flex:1 1 auto}
-}
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
-.stage-details-cell{ flex:1 1 auto; }
+@media (max-width: 480px) {
+  .project-detail-container.designer-page {
+    .upload-actions {
+      .secondary-btn {
+        padding: 18px 24px !important;
+        font-size: 17px !important;
+        min-height: 52px;
+        
+        &::after {
+          font-size: 20px;
+        }
+      }
+    }
+  }
+}

+ 306 - 116
src/app/pages/designer/project-detail/project-detail.ts

@@ -12,6 +12,12 @@ import {
   Settlement,
   ProjectStage
 } from '../../../models/project.model';
+import { OrderCreationCardComponent } from '../../../shared/components/order-creation-card/order-creation-card';
+import { RequirementsConfirmCardComponent } from '../../../shared/components/requirements-confirm-card/requirements-confirm-card';
+import { SettlementCardComponent } from '../../../shared/components/settlement-card/settlement-card';
+import { CustomerReviewCardComponent } from '../../../shared/components/customer-review-card/customer-review-card';
+import { ComplaintCardComponent } from '../../../shared/components/complaint-card/complaint-card';
+import { VerticalNavComponent } from './components/vertical-nav/vertical-nav.component';
 
 interface ExceptionHistory {
   id: string;
@@ -54,7 +60,8 @@ type SectionKey = 'order' | 'requirements' | 'delivery' | 'aftercare';
 
 @Component({
   selector: 'app-project-detail',
-  imports: [CommonModule, FormsModule, ReactiveFormsModule],
+  standalone: true,
+  imports: [CommonModule, FormsModule, ReactiveFormsModule, OrderCreationCardComponent, RequirementsConfirmCardComponent, SettlementCardComponent, CustomerReviewCardComponent, ComplaintCardComponent, VerticalNavComponent],
   templateUrl: './project-detail.html',
   styleUrls: ['./project-detail.scss', './debug-styles.scss']
 })
@@ -253,6 +260,19 @@ export class ProjectDetail implements OnInit, OnDestroy {
       this.loadProjectFiles();
       this.loadTimelineEvents();
     });
+
+    // 新增:监听查询参数,支持通过 activeTab 设置初始标签页
+    this.route.queryParamMap.subscribe(qp => {
+      const raw = qp.get('activeTab');
+      const alias: Record<string, 'progress' | 'members' | 'files'> = {
+        requirements: 'progress',
+        overview: 'progress'
+      };
+      const tab = raw && (raw in alias ? alias[raw] : raw);
+      if (tab === 'progress' || tab === 'members' || tab === 'files') {
+        this.activeTab = tab;
+      }
+    });
     
     // 添加点击事件监听器,当点击页面其他位置时关闭下拉菜单
     document.addEventListener('click', this.closeDropdownOnClickOutside);
@@ -305,12 +325,19 @@ export class ProjectDetail implements OnInit, OnDestroy {
   // 只读规则:客服视角为只读
   isReadOnly(): boolean { return this.isCustomerServiceView(); }
 
-  // 设计师仅看三大执行阶段,其它角色看全流程
+  // 计算当前激活板块:优先用户点击的 expandedSection;否则取当前阶段所属板块;再否则回退首个板块
+  private getActiveSectionKey(): SectionKey {
+    if (this.expandedSection) return this.expandedSection;
+    const current = this.project?.currentStage as ProjectStage | undefined;
+    return current ? this.getSectionKeyForStage(current) : this.sections[0].key;
+  }
+
+  // 返回当前板块的全部阶段(所有角色一致):
+  // 设计师也可查看 订单创建/确认需求/售后 板块内容
   getVisibleStages(): ProjectStage[] {
-    if (this.isDesignerView()) {
-      return this.stageOrder.filter(s => ['建模', '软装', '渲染'].includes(s));
-    }
-    return this.stageOrder;
+    const activeKey = this.getActiveSectionKey();
+    const sec = this.sections.find(s => s.key === activeKey);
+    return sec ? sec.stages : [];
   }
 
   // ============ 组长:同步上传与审核(新增,模拟实现) ============
@@ -1119,6 +1146,109 @@ export class ProjectDetail implements OnInit, OnDestroy {
     this.softDecorImages.unshift(...items);
     input.value = '';
   }
+  // 拖拽上传相关属性
+  isDragOver: boolean = false;
+
+  // 图片预览相关属性
+  showImagePreview: boolean = false;
+  previewImageData: any = null;
+
+  // 图片预览方法
+  previewImage(img: any): void {
+    this.previewImageData = img;
+    this.showImagePreview = true;
+  }
+
+  closeImagePreview(): void {
+    this.showImagePreview = false;
+    this.previewImageData = null;
+  }
+
+  downloadImage(img: any): void {
+    if (img) {
+      const link = document.createElement('a');
+      link.href = img.url;
+      link.download = img.name;
+      link.click();
+    }
+  }
+
+  removeImageFromPreview(): void {
+    if (this.previewImageData) {
+      // 根据图片类型调用相应的删除方法
+      if (this.whiteModelImages.find(i => i.id === this.previewImageData.id)) {
+        this.removeWhiteModelImage(this.previewImageData.id);
+      } else if (this.softDecorImages.find(i => i.id === this.previewImageData.id)) {
+        this.removeSoftDecorImage(this.previewImageData.id);
+      } else if (this.renderLargeImages.find(i => i.id === this.previewImageData.id)) {
+        this.removeRenderLargeImage(this.previewImageData.id);
+      }
+      this.closeImagePreview();
+    }
+  }
+
+  // 拖拽事件处理
+  onDragOver(event: DragEvent): void {
+    event.preventDefault();
+    event.stopPropagation();
+    this.isDragOver = true;
+  }
+
+  onDragLeave(event: DragEvent): void {
+    event.preventDefault();
+    event.stopPropagation();
+    this.isDragOver = false;
+  }
+
+  onFileDrop(event: DragEvent, type: 'whiteModel' | 'softDecor' | 'render'): void {
+    event.preventDefault();
+    event.stopPropagation();
+    this.isDragOver = false;
+
+    const files = event.dataTransfer?.files;
+    if (!files || files.length === 0) return;
+
+    // 创建模拟的input事件
+    const mockEvent = {
+      target: {
+        files: files
+      }
+    } as any;
+
+    // 根据类型调用相应的处理方法
+    switch (type) {
+      case 'whiteModel':
+        this.onWhiteModelSelected(mockEvent);
+        break;
+      case 'softDecor':
+        this.onSoftDecorSmallPicsSelected(mockEvent);
+        break;
+      case 'render':
+        this.onRenderLargePicsSelected(mockEvent);
+        break;
+    }
+  }
+
+  // 触发文件输入框
+  triggerFileInput(type: 'whiteModel' | 'softDecor' | 'render'): void {
+    let inputId: string;
+    switch (type) {
+      case 'whiteModel':
+        inputId = 'whiteModelFileInput';
+        break;
+      case 'softDecor':
+        inputId = 'softDecorFileInput';
+        break;
+      case 'render':
+        inputId = 'renderFileInput';
+        break;
+    }
+    const input = document.querySelector(`#${inputId}`) as HTMLInputElement;
+    if (input) {
+      input.click();
+    }
+  }
+
   removeSoftDecorImage(id: string): void {
     const target = this.softDecorImages.find(i => i.id === id);
     if (target) this.revokeUrl(target.url);
@@ -1136,12 +1266,14 @@ export class ProjectDetail implements OnInit, OnDestroy {
     this.showRenderUploadModal = true;
     this.pendingRenderLargeItems = [];
   }
+  
   closeRenderUploadModal(): void {
     // 关闭时释放临时预览URL
     this.pendingRenderLargeItems.forEach(i => this.revokeUrl(i.url));
     this.pendingRenderLargeItems = [];
     this.showRenderUploadModal = false;
   }
+  
   async onRenderLargePicsSelected(event: Event): Promise<void> {
     const input = event.target as HTMLInputElement;
     if (!input.files || input.files.length === 0) return;
@@ -1154,10 +1286,17 @@ export class ProjectDetail implements OnInit, OnDestroy {
         continue;
       }
       const item = this.makeImageItem(f);
-      this.pendingRenderLargeItems.push({ id: item.id, name: item.name, url: item.url, file: f });
+      // 直接添加到正式列表,不再使用待确认列表
+      this.renderLargeImages.unshift({ 
+        id: item.id, 
+        name: item.name, 
+        url: item.url, 
+        size: this.formatFileSize(f.size) 
+      });
     }
     input.value = '';
   }
+  
   confirmRenderUpload(): void {
     // 将待确认的图片加入正式列表(此处模拟上传成功)
     const toAdd = this.pendingRenderLargeItems.map(i => ({ id: i.id, name: i.name, url: i.url, size: this.formatFileSize(i.file.size) }));
@@ -1166,128 +1305,179 @@ export class ProjectDetail implements OnInit, OnDestroy {
     // 新增:渲染阶段确认后,自动进入下一阶段(后期)
     this.advanceToNextStage('渲染');
   }
+  
   removeRenderLargeImage(id: string): void {
     const target = this.renderLargeImages.find(i => i.id === id);
     if (target) this.revokeUrl(target.url);
     this.renderLargeImages = this.renderLargeImages.filter(i => i.id !== id);
   }
 
-// 根据阶段映射所属板块
-getSectionKeyForStage(stage: ProjectStage): SectionKey {
-switch (stage) {
-case '订单创建':
-return 'order';
-case '需求沟通':
-case '方案确认':
-return 'requirements';
-case '建模':
-case '软装':
-case '渲染':
-return 'delivery';
-case '尾款结算':
-case '客户评价':
-case '投诉处理':
-return 'aftercare';
-default:
-return 'order';
-}
-}
+  // 根据阶段映射所属板块
+  getSectionKeyForStage(stage: ProjectStage): SectionKey {
+    switch (stage) {
+      case '订单创建':
+        return 'order';
+      case '需求沟通':
+      case '方案确认':
+        return 'requirements';
+      case '建模':
+      case '软装':
+      case '渲染':
+        return 'delivery';
+      case '尾款结算':
+      case '客户评价':
+      case '投诉处理':
+        return 'aftercare';
+      default:
+        return 'order';
+    }
+  }
 
-// 获取板块状态:completed | 'active' | 'pending'
-getSectionStatus(key: SectionKey): 'completed' | 'active' | 'pending' {
-  const current = this.project?.currentStage as ProjectStage | undefined;
-  if (!current) return 'pending';
+  // 获取板块状态:completed | 'active' | 'pending'
+  getSectionStatus(key: SectionKey): 'completed' | 'active' | 'pending' {
+    const current = this.project?.currentStage as ProjectStage | undefined;
+    if (!current) return 'pending';
 
-  const currentSection = this.getSectionKeyForStage(current);
-  const sectionOrder = this.sections.map(s => s.key);
-  const currentIdx = sectionOrder.indexOf(currentSection);
-  const idx = sectionOrder.indexOf(key);
-  if (idx === -1 || currentIdx === -1) return 'pending';
+    const currentSection = this.getSectionKeyForStage(current);
+    const sectionOrder = this.sections.map(s => s.key);
+    const currentIdx = sectionOrder.indexOf(currentSection);
+    const idx = sectionOrder.indexOf(key);
+    if (idx === -1 || currentIdx === -1) return 'pending';
 
-  if (idx < currentIdx) return 'completed';
-  if (idx === currentIdx) return 'active';
-  return 'pending';
-}
+    if (idx < currentIdx) return 'completed';
+    if (idx === currentIdx) return 'active';
+    return 'pending';
+  }
 
-// 切换四大板块(单展开)
-toggleSection(key: SectionKey): void {
-  this.expandedSection = key;
-}
+  // 切换四大板块(单展开)
+  toggleSection(key: SectionKey): void {
+    this.expandedSection = key;
+    // 点击板块按钮时,滚动到该板块的第一个可见阶段卡片
+    const sec = this.sections.find(s => s.key === key);
+    if (sec) {
+      // 设计师仅滚动到可见的三大执行阶段,否则取该板块第一个阶段
+      const candidate = this.isDesignerView()
+        ? sec.stages.find(st => ['建模', '软装', '渲染'].includes(st)) || sec.stages[0]
+        : sec.stages[0];
+      this.scrollToStage(candidate);
+    }
+  }
 
-// 订单创建阶段:客户信息(迁移自客服端“客户信息”卡片)
-orderCreationMethod: 'miniprogram' | 'manual' = 'miniprogram'
-isSyncing: boolean = false
-orderTime: string = ''
-
-customerForm!: FormGroup
-customerSearchKeyword: string = ''
-customerSearchResults: Array<{ id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }> = []
-selectedOrderCustomer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string } | null = null
-
-demandTypes = [
-{ value: 'price', label: '价格敏感' },
-{ value: 'quality', label: '质量敏感' },
-{ value: 'comprehensive', label: '综合要求' }
-]
-followUpStatus = [
-{ value: 'quotation', label: '待报价' },
-{ value: 'confirm', label: '待确认需求' },
-{ value: 'lost', label: '已失联' }
-]
-// 客户信息:搜索/选择/清空/同步/快速填写 逻辑
-searchCustomer(): void {
-if (this.customerSearchKeyword.trim().length >= 2) {
-this.customerSearchResults = [
-{ id: '1', name: '张先生', phone: '138****5678', customerType: '老客户', source: '官网咨询', avatar: "data:image/svg+xml,%3Csvg width='64' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23E6E6E6'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" },
-{ id: '2', name: '李女士', phone: '139****1234', customerType: 'VIP客户', source: '推荐介绍', avatar: "data:image/svg+xml,%3Csvg width='65' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23DCDCDC'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" }
-]
-} else {
-this.customerSearchResults = []
-}
-}
+  // 阶段到锚点的映射
+  stageToAnchor(stage: ProjectStage): string {
+    const map: Record<ProjectStage, string> = {
+      '订单创建': 'order',
+      '需求沟通': 'requirements-talk',
+      '方案确认': 'proposal-confirm',
+      '建模': 'modeling',
+      '软装': 'softdecor',
+      '渲染': 'render',
+      '后期': 'aftercare',
+      '尾款结算': 'settlement',
+      '客户评价': 'customer-review',
+      '投诉处理': 'complaint'
+    };
+    return `stage-${map[stage] || 'unknown'}`;
+  }
 
-selectCustomer(customer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }): void {
-this.selectedOrderCustomer = customer
-this.customerForm.patchValue({
-name: customer.name,
-phone: customer.phone,
-wechat: customer.wechat || '',
-customerType: customer.customerType || '新客户',
-source: customer.source || '',
-remark: customer.remark || ''
-})
-this.customerSearchResults = []
-this.customerSearchKeyword = ''
-}
+  // 平滑滚动到指定阶段卡片
+  scrollToStage(stage: ProjectStage): void {
+    const anchor = this.stageToAnchor(stage);
+    const el = document.getElementById(anchor);
+    if (el) {
+      el.scrollIntoView({ behavior: 'smooth', block: 'start' });
+    }
+  }
 
-clearSelectedCustomer(): void {
-this.selectedOrderCustomer = null
-this.customerForm.reset({ customerType: '新客户' })
-}
+  // 订单创建阶段:客户信息(迁移自客服端"客户信息"卡片)
+  orderCreationMethod: 'miniprogram' | 'manual' = 'miniprogram';
+  isSyncing: boolean = false;
+  orderTime: string = '';
 
-quickFillCustomerInfo(keyword: string): void {
-const k = (keyword || '').trim()
-if (!k) return
-// 模拟:若有搜索结果,选择第一条
-if (this.customerSearchResults.length === 0) this.searchCustomer()
-if (this.customerSearchResults.length > 0) {
-this.selectCustomer(this.customerSearchResults[0])
-}
-}
+  customerForm!: FormGroup;
+  customerSearchKeyword: string = '';
+  customerSearchResults: Array<{ id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }> = [];
+  selectedOrderCustomer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string } | null = null;
 
-syncMiniprogramCustomerInfo(): void {
-if (this.isSyncing) return
-this.isSyncing = true
-setTimeout(() => {
-// 模拟从小程序同步到客户表单
-this.customerForm.patchValue({
-name: '小程序用户',
-phone: '13800001234',
-wechat: 'wx_user_001',
-customerType: '新客户',
-source: '小程序下单'
-})
-this.isSyncing = false
-}, 1000)
-}
+  demandTypes = [
+    { value: 'price', label: '价格敏感' },
+    { value: 'quality', label: '质量敏感' },
+    { value: 'comprehensive', label: '综合要求' }
+  ];
+  
+  followUpStatus = [
+    { value: 'quotation', label: '待报价' },
+    { value: 'confirm', label: '待确认需求' },
+    { value: 'lost', label: '已失联' }
+  ];
+
+  // 客户信息:搜索/选择/清空/同步/快速填写 逻辑
+  searchCustomer(): void {
+    if (this.customerSearchKeyword.trim().length >= 2) {
+      this.customerSearchResults = [
+        { id: '1', name: '张先生', phone: '138****5678', customerType: '老客户', source: '官网咨询', avatar: "data:image/svg+xml,%3Csvg width='64' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23E6E6E6'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" },
+        { id: '2', name: '李女士', phone: '139****1234', customerType: 'VIP客户', source: '推荐介绍', avatar: "data:image/svg+xml,%3Csvg width='65' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23DCDCDC'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" }
+      ];
+    } else {
+      this.customerSearchResults = [];
+    }
+  }
+
+  selectCustomer(customer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }): void {
+    this.selectedOrderCustomer = customer;
+    this.customerForm.patchValue({
+      name: customer.name,
+      phone: customer.phone,
+      wechat: customer.wechat || '',
+      customerType: customer.customerType || '新客户',
+      source: customer.source || '',
+      remark: customer.remark || ''
+    });
+    this.customerSearchResults = [];
+    this.customerSearchKeyword = '';
+  }
+
+  clearSelectedCustomer(): void {
+    this.selectedOrderCustomer = null;
+    this.customerForm.reset({ customerType: '新客户' });
+  }
+
+  quickFillCustomerInfo(keyword: string): void {
+    const k = (keyword || '').trim();
+    if (!k) return;
+    // 模拟:若有搜索结果,选择第一条
+    if (this.customerSearchResults.length === 0) this.searchCustomer();
+    if (this.customerSearchResults.length > 0) {
+      this.selectCustomer(this.customerSearchResults[0]);
+    }
+  }
+
+  syncMiniprogramCustomerInfo(): void {
+    if (this.isSyncing) return;
+    this.isSyncing = true;
+    setTimeout(() => {
+      // 模拟从小程序同步到客户表单
+      this.customerForm.patchValue({
+        name: '小程序用户',
+        phone: '13800001234',
+        wechat: 'wx_user_001',
+        customerType: '新客户',
+        source: '小程序下单'
+      });
+      this.isSyncing = false;
+    }, 1000);
+  }
+
+  downloadFile(file: ProjectFile): void {
+    // 实现文件下载逻辑
+    const link = document.createElement('a');
+    link.href = file.url;
+    link.download = file.name;
+    link.click();
+  }
+
+  previewFile(file: ProjectFile): void {
+    // 实现文件预览逻辑
+    window.open(file.url, '_blank');
+  }
 }

+ 15 - 7
src/app/pages/finance/project-records/project-records.html

@@ -1,14 +1,22 @@
 <div class="finance-project-records">
   <!-- 顶部标题区 -->
   <header class="page-header">
-    <div class="header-top">
-      <button class="back-to-dashboard-btn" routerLink="/finance/dashboard">
-        <i class="fa fa-arrow-left"></i> 返回工作台
-      </button>
+    <div class="header-left">
+      <div class="header-content">
+        <h1>项目财务档案</h1>
+        <p class="subtitle">管理所有项目的财务状态和报价信息</p>
+      </div>
     </div>
-    <div class="header-content">
-      <h1>项目财务档案</h1>
-      <p class="subtitle">管理所有项目的财务状态和报价信息</p>
+    <div class="header-right">
+      <div class="header-actions">
+        <button class="action-btn back-btn" routerLink="/finance/dashboard">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+            <path d="M19 12H5"></path>
+            <polyline points="12 19 5 12 12 5"></polyline>
+          </svg>
+          返回工作台
+        </button>
+      </div>
     </div>
   </header>
 

+ 85 - 24
src/app/pages/finance/project-records/project-records.scss

@@ -11,48 +11,109 @@
 /* 顶部标题区 */
 .page-header {
   margin-bottom: 24px;
-  .header-top {
-    margin-bottom: 16px;
-  }
-  .header-content {
-    h1 {
-      font-size: 28px;
-      font-weight: 600;
-      margin-bottom: 8px;
-      color: #111;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  min-height: 80px;
+  
+  .header-left {
+    flex: 1;
+    
+    .header-content {
+      h1 {
+        font-size: 28px;
+        font-weight: 600;
+        margin-bottom: 8px;
+        color: #111;
+      }
+      .subtitle {
+        font-size: 14px;
+        color: #888;
+        margin: 0;
+      }
     }
-    .subtitle {
-      font-size: 14px;
-      color: #888;
+  }
+
+  .header-right {
+    .header-actions {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+
+      .action-btn {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 10px 16px;
+        border: none;
+        border-radius: 8px;
+        font-size: 14px;
+        font-weight: 500;
+        cursor: pointer;
+        transition: all 0.2s ease;
+        text-decoration: none;
+        white-space: nowrap;
+
+        svg {
+          width: 16px;
+          height: 16px;
+          flex-shrink: 0;
+        }
+
+        &:hover {
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+        }
+
+        &:active {
+          transform: translateY(0);
+        }
+
+        &.back-btn {
+          background: #28a745;
+          color: white;
+          
+          &:hover {
+            background: #218838;
+          }
+        }
+      }
     }
   }
 }
 
+// 保持原有的back-to-dashboard-btn样式以兼容其他地方的使用
 .back-to-dashboard-btn {
   display: inline-flex;
   align-items: center;
   gap: 6px;
   padding: 8px 16px;
-  background-color: #f5f5f5;
-  color: #007aff;
-  border: 1px solid #d1d1d6;
+  background-color: #F2F3F5;
+  color: #1D2129;
+  border: 1px solid #E5E6EB;
   border-radius: 8px;
   font-size: 14px;
   font-weight: 500;
   cursor: pointer;
-  transition: all 0.2s ease;
+  transition: all 0.2s ease-in-out;
   text-decoration: none;
+  min-height: 40px;
+  white-space: nowrap;
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-}
 
-.back-to-dashboard-btn:hover {
-  background-color: #e5e5ea;
-  transform: translateY(-1px);
-}
+  &:hover {
+    background-color: #F7F8FA;
+    color: #1D2129;
+    border-color: rgba(22, 93, 255, 0.3);
+    transform: translateY(-1px);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  }
 
-.back-to-dashboard-btn:active {
-  transform: translateY(0);
-  background-color: #d1d1d6;
+  &:active {
+    transform: translateY(0);
+    background-color: #F2F3F5;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  }
 }
 
 /* 搜索和筛选区 */

+ 18 - 9
src/app/pages/finance/reconciliation/reconciliation.scss

@@ -66,28 +66,37 @@ $text-muted: #94a3b8;
         display: inline-flex;
         align-items: center;
         gap: 8px;
-        padding: 10px 16px;
-        background-color: $background-white;
-        color: $primary-color;
-        border: 1px solid $border-color;
+        padding: 8px 16px;
+        background-color: #F2F3F5;
+        color: #1D2129;
+        border: 1px solid #E5E6EB;
         border-radius: 8px;
         font-size: 14px;
         font-weight: 500;
         cursor: pointer;
-        transition: all 0.3s ease;
+        transition: all 0.2s ease-in-out;
         text-decoration: none;
+        min-height: 40px;
+        white-space: nowrap;
         box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
 
         &:hover {
-          background-color: $primary-color;
-          border-color: $primary-color;
-          color: white;
+          background-color: #F7F8FA;
+          border-color: rgba(22, 93, 255, 0.3);
+          color: #1D2129;
           transform: translateY(-1px);
-          box-shadow: 0 4px 12px rgba(0, 123, 255, 0.2);
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
         }
 
         &:active {
           transform: translateY(0);
+          background-color: #F2F3F5;
+          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+        }
+
+        &:focus {
+          outline: 2px solid rgba(22, 93, 255, 0.3);
+          outline-offset: 2px;
         }
       }
     }

+ 13 - 8
src/app/pages/finance/reports/reports.scss

@@ -19,26 +19,31 @@
   align-items: center;
   gap: 6px;
   padding: 8px 16px;
-  background-color: #f5f5f5;
-  color: #4a90e2;
-  border: 1px solid #ddd;
-  border-radius: 6px;
+  background-color: #F2F3F5;
+  color: #1D2129;
+  border: 1px solid #E5E6EB;
+  border-radius: 8px;
   font-size: 14px;
   font-weight: 500;
   cursor: pointer;
-  transition: all 0.3s ease;
+  transition: all 0.2s ease-in-out;
   text-decoration: none;
+  min-height: 40px;
+  white-space: nowrap;
 }
 
 .back-to-dashboard-btn:hover {
-  background-color: #4a90e2;
-  color: white;
-  border-color: #4a90e2;
+  background-color: #F7F8FA;
+  color: #1D2129;
+  border-color: rgba(22, 93, 255, 0.3);
   transform: translateY(-1px);
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 }
 
 .back-to-dashboard-btn:active {
   transform: translateY(0);
+  background-color: #F2F3F5;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
 }
 
 .reports-header h1 {

+ 20 - 6
src/app/pages/hr/dashboard/dashboard.scss

@@ -16,20 +16,34 @@
       
       .nav-button {
         padding: 12px 24px;
-        border-radius: 25px;
+        border-radius: 8px;
         font-weight: 500;
-        transition: all 0.3s ease;
-        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+        transition: all 0.2s ease-in-out;
+        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+        white-space: nowrap;
+        border: 1px solid rgba(33, 150, 243, 0.2);
         
         &:hover {
-          transform: translateY(-2px);
-          box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+          background-color: rgba(33, 150, 243, 0.08);
+        }
+
+        &:active {
+          transform: translateY(0);
+          box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
+        }
+
+        &:focus {
+          outline: 2px solid rgba(33, 150, 243, 0.3);
+          outline-offset: 2px;
         }
         
         &.active {
           background: linear-gradient(45deg, #2196F3, #21CBF3);
           color: white;
-          box-shadow: 0 6px 20px rgba(33, 150, 243, 0.3);
+          box-shadow: 0 4px 16px rgba(33, 150, 243, 0.3);
+          border-color: transparent;
         }
         
         mat-icon {

+ 22 - 0
src/app/shared/components/complaint-card/complaint-card.html

@@ -0,0 +1,22 @@
+<div class="info-card complaint-card">
+  <h4>投诉与异常处理</h4>
+  @if (complaints && complaints.length > 0) {
+    <ul class="items">
+      @for (c of complaints; track c.id) {
+        <li class="item">
+          <div class="title">{{ c.type }}</div>
+          <div class="desc">{{ c.description }}</div>
+          <div class="meta">
+            <span class="time">{{ c.submitTime }}</span>
+            <span class="status">{{ c.status }}</span>
+          </div>
+          @if (c.response) {
+            <div class="response">处理意见:{{ c.response }}</div>
+          }
+        </li>
+      }
+    </ul>
+  } @else {
+    <div class="empty">暂无投诉/异常</div>
+  }
+</div>

+ 16 - 0
src/app/shared/components/complaint-card/complaint-card.scss

@@ -0,0 +1,16 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { display: block; height: 100%; }
+
+.complaint-card {
+  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
+  .items { margin: 0; padding-left: 0; list-style: none; }
+  .item { padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; }
+  .item:last-child { border-bottom: none; }
+  .title { font-weight: $ios-font-weight-semibold; color: $ios-text-primary; }
+  .desc { color: $ios-text-secondary; margin-top: $ios-spacing-xs; }
+  .meta { display: flex; gap: $ios-spacing-sm; color: $ios-text-secondary; font-size: $ios-font-size-xs; margin-top: $ios-spacing-xs; }
+  .status { color: $ios-primary; }
+  .time { color: $ios-text-secondary; }
+  .response { margin-top: $ios-spacing-sm; color: $ios-primary; }
+}

+ 22 - 0
src/app/shared/components/complaint-card/complaint-card.ts

@@ -0,0 +1,22 @@
+import { Component, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+export interface ComplaintItem {
+  id: string;
+  type: string;
+  description: string;
+  submitTime: any;
+  status: string;
+  response?: string;
+}
+
+@Component({
+  selector: 'app-complaint-card',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './complaint-card.html',
+  styleUrls: ['./complaint-card.scss']
+})
+export class ComplaintCardComponent {
+  @Input() complaints: ComplaintItem[] = [];
+}

+ 19 - 0
src/app/shared/components/customer-review-card/customer-review-card.html

@@ -0,0 +1,19 @@
+<div class="info-card customer-review-card">
+  <h4>客户评价</h4>
+  @if (feedbacks && feedbacks.length > 0) {
+    <ul class="items">
+      @for (f of feedbacks; track f.id) {
+        <li class="item">
+          <div class="title">{{ f.customerName || '客户反馈' }}</div>
+          <div class="desc">{{ f.content }}</div>
+          <div class="meta">
+            <span class="time">{{ (f.updatedAt || f.createdAt) | date:'yyyy-MM-dd HH:mm' }}</span>
+            <span class="status">{{ f.status }}</span>
+          </div>
+        </li>
+      }
+    </ul>
+  } @else {
+    <div class="empty">暂无客户评价</div>
+  }
+</div>

+ 15 - 0
src/app/shared/components/customer-review-card/customer-review-card.scss

@@ -0,0 +1,15 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { display: block; height: 100%; }
+
+.customer-review-card {
+  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
+  .items { margin: 0; padding-left: 0; list-style: none; }
+  .item { padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; }
+  .item:last-child { border-bottom: none; }
+  .title { font-weight: $ios-font-weight-semibold; color: $ios-text-primary; }
+  .desc { color: $ios-text-secondary; margin-top: $ios-spacing-xs; }
+  .meta { display: flex; gap: $ios-spacing-sm; color: $ios-text-secondary; font-size: $ios-font-size-xs; margin-top: $ios-spacing-xs; }
+  .status { color: $ios-primary; }
+  .time { color: $ios-text-secondary; }
+}

+ 14 - 0
src/app/shared/components/customer-review-card/customer-review-card.ts

@@ -0,0 +1,14 @@
+import { Component, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CustomerFeedback } from '../../../models/project.model';
+
+@Component({
+  selector: 'app-customer-review-card',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './customer-review-card.html',
+  styleUrls: ['./customer-review-card.scss']
+})
+export class CustomerReviewCardComponent {
+  @Input() feedbacks: CustomerFeedback[] = [];
+}

+ 53 - 39
src/app/shared/components/designer-nav/designer-nav.scss

@@ -105,52 +105,66 @@ $transition: all 0.3s ease;
     display: flex;
     align-items: center;
     gap: 16px;
-  }
 
-  .notification-btn {
-    position: relative;
-    background: none;
-    border: none;
-    cursor: pointer;
-    color: $text-secondary;
-    padding: 8px;
-    transition: $transition;
+    .notification-btn {
+      position: relative;
+      background: none;
+      border: none;
+      cursor: pointer;
+      color: $text-secondary;
+      padding: 8px;
+      border-radius: 8px;
+      transition: all 0.2s ease-in-out;
+      white-space: nowrap;
 
-    &:hover {
-      color: $primary-color;
-    }
+      &:hover {
+        color: $primary-color;
+        background-color: rgba(22, 93, 255, 0.08);
+        transform: translateY(-1px);
+      }
 
-    .notification-badge {
-      position: absolute;
-      top: 2px;
-      right: 2px;
-      background-color: $danger-color;
-      color: white;
-      font-size: 10px;
-      font-weight: 500;
-      padding: 2px 6px;
-      border-radius: 10px;
-      min-width: 18px;
-      text-align: center;
-    }
-  }
+      &:active {
+        transform: translateY(0);
+        background-color: rgba(22, 93, 255, 0.12);
+      }
 
-  .user-profile {
-    display: flex;
-    align-items: center;
-    gap: 8px;
+      &:focus {
+        outline: 2px solid rgba(22, 93, 255, 0.3);
+        outline-offset: 2px;
+      }
 
-    .user-avatar {
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      object-fit: cover;
+      .notification-badge {
+        position: absolute;
+        top: 0;
+        right: 0;
+        background-color: $danger-color;
+        color: white;
+        font-size: 12px;
+        padding: 2px 6px;
+        border-radius: 10px;
+        min-width: 18px;
+        text-align: center;
+        box-shadow: 0 2px 4px rgba(245, 63, 63, 0.3);
+      }
     }
 
-    .user-name {
-      font-size: 14px;
-      font-weight: 500;
-      color: $text-primary;
+    .user-profile {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+
+      .user-avatar {
+        width: 36px;
+        height: 36px;
+        border-radius: 50%;
+        object-fit: cover;
+      }
+
+      .user-name {
+        font-size: 14px;
+        font-weight: 500;
+        color: $text-primary;
+      }
     }
   }
 }

+ 105 - 0
src/app/shared/components/order-creation-card/order-creation-card.html

@@ -0,0 +1,105 @@
+<div class="order-creation-card">
+  <div class="method-switch">
+    <button class="secondary-btn" [class.active]="orderCreationMethod === 'miniprogram'" (click)="setMethod('miniprogram')">小程序同步</button>
+    <button class="secondary-btn" [class.active]="orderCreationMethod === 'manual'" (click)="setMethod('manual')">手动录入</button>
+    <span class="hint">下单时间:{{ orderTime }}</span>
+  </div>
+
+  <div class="sync-row" *ngIf="orderCreationMethod === 'miniprogram'">
+    <button class="primary-btn" [disabled]="isSyncing" (click)="triggerSync()">{{ isSyncing ? '同步中...' : '从小程序同步客户信息' }}</button>
+    <span class="hint">点击同步后将自动填充客户姓名、手机号、微信等信息</span>
+  </div>
+
+  <div class="divider"></div>
+
+  <div class="search-row">
+    <input type="text" placeholder="搜索客户姓名或手机号" [ngModel]="customerSearchKeyword" (ngModelChange)="onKeywordChange($event)" />
+    <button class="secondary-btn" (click)="triggerSearch()">搜索</button>
+    @if (selectedCustomer) {
+      <div class="selected-pill">
+        <span>已选客户:{{ selectedCustomer.name }}({{ selectedCustomer.phone }})</span>
+        <button class="link danger" (click)="clearCustomer()">清除</button>
+      </div>
+    }
+  </div>
+
+  @if (customerSearchResults.length > 0) {
+    <div class="search-results">
+      @for (c of customerSearchResults; track c.id) {
+        <div class="result-item">
+          <div class="left">
+            <span class="name">{{ c.name }}</span>
+            <span class="phone">{{ c.phone }}</span>
+            @if (c.wechat) { <span class="wx">wx: {{ c.wechat }}</span> }
+          </div>
+          <button class="link" (click)="chooseCustomer(c)">选择</button>
+        </div>
+      }
+    </div>
+  }
+
+  <form [formGroup]="customerForm" novalidate>
+    <div class="form-group">
+      <label>客户姓名</label>
+      <input type="text" formControlName="name" placeholder="请输入客户姓名" />
+      @if (customerForm.get('name')?.touched && customerForm.get('name')?.invalid) {
+        <div class="error">请填写客户姓名</div>
+      }
+    </div>
+
+    <div class="form-group">
+      <label>手机号</label>
+      <input type="text" formControlName="phone" placeholder="请输入11位手机号" />
+      @if (customerForm.get('phone')?.touched && customerForm.get('phone')?.errors?.['required']) {
+        <div class="error">请填写手机号</div>
+      }
+      @if (customerForm.get('phone')?.touched && customerForm.get('phone')?.errors?.['pattern']) {
+        <div class="error">手机号格式不正确</div>
+      }
+    </div>
+
+    <div class="form-group">
+      <label>微信号</label>
+      <input type="text" formControlName="wechat" placeholder="可选" />
+    </div>
+
+    <div class="form-group">
+      <label>客户类型</label>
+      <select formControlName="customerType">
+        <option value="新客户">新客户</option>
+        <option value="老客户">老客户</option>
+        <option value="VIP客户">VIP客户</option>
+      </select>
+    </div>
+
+    <div class="form-group">
+      <label>需求类型</label>
+      <select formControlName="demandType">
+        <option value="">未指定</option>
+        @for (d of demandTypes; track d.value) {
+          <option [value]="d.value">{{ d.label }}</option>
+        }
+      </select>
+    </div>
+
+    <div class="form-group">
+      <label>跟进状态</label>
+      <select formControlName="followUpStatus">
+        <option value="">未指定</option>
+        @for (s of followUpStatus; track s.value) {
+          <option [value]="s.value">{{ s.label }}</option>
+        }
+      </select>
+    </div>
+
+    <div class="form-group">
+      <label>来源渠道</label>
+      <input type="text" formControlName="source" placeholder="如:抖音/官网/转介绍/线下" />
+    </div>
+
+    <div class="form-group">
+      <label>备注</label>
+      <textarea formControlName="remark" rows="3" placeholder="可填写特殊要求或沟通记录"></textarea>
+    </div>
+  </form>
+</div>

+ 7 - 0
src/app/shared/components/order-creation-card/order-creation-card.scss

@@ -0,0 +1,7 @@
+.order-creation-card {
+  background: white;
+  border-radius: 12px;
+  padding: 16px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  margin-bottom: 16px;
+}

+ 73 - 0
src/app/shared/components/order-creation-card/order-creation-card.ts

@@ -0,0 +1,73 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule, FormGroup } from '@angular/forms';
+
+export interface OrderCustomer {
+  id: string;
+  name: string;
+  phone: string;
+  wechat?: string;
+  avatar?: string;
+  customerType?: string;
+  source?: string;
+  remark?: string;
+}
+
+@Component({
+  selector: 'app-order-creation-card',
+  standalone: true,
+  imports: [CommonModule, FormsModule, ReactiveFormsModule],
+  templateUrl: './order-creation-card.html',
+  styleUrls: ['./order-creation-card.scss']
+})
+export class OrderCreationCardComponent {
+  @Input() orderCreationMethod: 'miniprogram' | 'manual' = 'miniprogram';
+  @Output() orderCreationMethodChange = new EventEmitter<'miniprogram' | 'manual'>();
+
+  @Input() isSyncing = false;
+  @Input() orderTime = '';
+
+  @Input() customerSearchKeyword = '';
+  @Output() customerSearchKeywordChange = new EventEmitter<string>();
+
+  @Input() customerSearchResults: OrderCustomer[] = [];
+  @Input() selectedCustomer: OrderCustomer | null = null;
+  @Output() searchCustomer = new EventEmitter<void>();
+  @Output() selectCustomer = new EventEmitter<OrderCustomer>();
+  @Output() clearSelectedCustomer = new EventEmitter<void>();
+  @Output() syncMiniprogram = new EventEmitter<void>();
+
+  // 共享的表单与选项(由父组件提供)
+  @Input() customerForm!: FormGroup;
+  @Input() demandTypes: Array<{ value: string; label: string }> = [];
+  @Input() followUpStatus: Array<{ value: string; label: string }> = [];
+
+  // 行为封装
+  setMethod(method: 'miniprogram' | 'manual') {
+    if (this.orderCreationMethod !== method) {
+      this.orderCreationMethod = method;
+      this.orderCreationMethodChange.emit(method);
+    }
+  }
+
+  onKeywordChange(v: string) {
+    this.customerSearchKeyword = v;
+    this.customerSearchKeywordChange.emit(v);
+  }
+
+  triggerSearch() {
+    this.searchCustomer.emit();
+  }
+
+  chooseCustomer(c: OrderCustomer) {
+    this.selectCustomer.emit(c);
+  }
+
+  clearCustomer() {
+    this.clearSelectedCustomer.emit();
+  }
+
+  triggerSync() {
+    if (!this.isSyncing) this.syncMiniprogram.emit();
+  }
+}

+ 884 - 0
src/app/shared/components/proposal-confirm-card/proposal-confirm-card.html

@@ -0,0 +1,884 @@
+<div class="proposal-confirm-card">
+  <!-- 卡片头部 -->
+  <div class="card-header">
+    <div class="header-left">
+      <h3 class="card-title">方案确认</h3>
+      <div class="progress-indicator">
+        <div class="progress-bar">
+          <div class="progress-fill" [style.width.%]="getProgressPercentage()"></div>
+        </div>
+        <span class="progress-text">{{ getProgressPercentage() | number:'1.0-0' }}% 完成</span>
+      </div>
+    </div>
+    <div class="header-right">
+      <button class="btn btn-secondary" (click)="toggle3DPreview()" [class.active]="show3DPreview">
+        <i class="icon-3d"></i> 3D预览
+      </button>
+      <button class="btn btn-secondary" (click)="toggleCADOverlay()" [class.active]="showCADOverlay">
+        <i class="icon-cad"></i> CAD叠加
+      </button>
+    </div>
+  </div>
+
+  <!-- 标签页导航 -->
+  <div class="tab-navigation">
+    <button class="tab-btn" 
+            [class.active]="activeTab === 'overview'" 
+            (click)="switchTab('overview')">
+      概览
+    </button>
+    <button class="tab-btn" 
+            [class.active]="activeTab === 'versions'" 
+            (click)="switchTab('versions')">
+      版本对比
+    </button>
+    <button class="tab-btn" 
+            [class.active]="activeTab === 'details'" 
+            (click)="switchTab('details')">
+      详细方案
+    </button>
+    <button class="tab-btn" 
+            [class.active]="activeTab === 'collaboration'" 
+            (click)="switchTab('collaboration')">
+      协作批注
+    </button>
+    <button class="tab-btn" 
+            [class.active]="activeTab === 'confirmation'" 
+            (click)="switchTab('confirmation')">
+      确认流程
+    </button>
+  </div>
+
+  <!-- 标签页内容 -->
+  <div class="tab-content">
+    
+    <!-- 概览标签页 -->
+    @if (activeTab === 'overview') {
+      <div class="overview-content">
+        <!-- 当前选中版本概览 -->
+        @if (selectedVersion) {
+          <div class="version-overview">
+            <div class="version-header">
+              <h4>{{ selectedVersion.name }}</h4>
+              <span class="version-badge">当前方案</span>
+            </div>
+            <p class="version-description">{{ selectedVersion.description }}</p>
+            
+            <div class="version-metrics">
+              <div class="metric-item">
+                <span class="metric-label">主色调</span>
+                <div class="color-preview" [style.background-color]="'rgb(' + selectedVersion.mainColor.rgb + ')'"></div>
+                <span class="metric-value">{{ selectedVersion.mainColor.colorTemp }}</span>
+              </div>
+              <div class="metric-item">
+                <span class="metric-label">预算</span>
+                <span class="metric-value">¥{{ selectedVersion.budget | number }}</span>
+              </div>
+              <div class="metric-item">
+                <span class="metric-label">工期</span>
+                <span class="metric-value">{{ selectedVersion.workDays }}天</span>
+              </div>
+            </div>
+
+            <!-- 材质比例图表 -->
+            <div class="material-chart">
+              <h5>材质比例</h5>
+              <div class="chart-container">
+                @for (material of selectedVersion.materialRatio; track material.material) {
+                  <div class="material-bar">
+                    <span class="material-name">{{ material.material }}</span>
+                    <div class="bar-container">
+                      <div class="bar-fill" [style.width.%]="material.percentage"></div>
+                    </div>
+                    <span class="material-percentage">{{ material.percentage }}%</span>
+                  </div>
+                }
+              </div>
+            </div>
+          </div>
+        }
+
+        <!-- 3D预览区域 -->
+        @if (show3DPreview) {
+          <div class="preview-3d">
+            <div class="preview-header">
+              <h4>3D方案预览</h4>
+              <button class="btn btn-close" (click)="toggle3DPreview()">
+                <i class="icon-close"></i>
+              </button>
+            </div>
+            <div class="preview-container">
+              <div class="preview-placeholder">
+                <i class="icon-3d-preview"></i>
+                <p>3D预览加载中...</p>
+                <small>支持鼠标拖拽查看全景</small>
+              </div>
+            </div>
+          </div>
+        }
+
+        <!-- CAD叠加区域 -->
+        @if (showCADOverlay) {
+          <div class="cad-overlay">
+            <div class="overlay-controls">
+              <label class="overlay-toggle">
+                <input type="checkbox" checked>
+                <span>显示CAD结构线</span>
+              </label>
+              <div class="legend">
+                <span class="legend-item load-bearing">承重柱</span>
+                <span class="legend-item furniture">家具区域</span>
+                <span class="legend-item flow">人流动线</span>
+              </div>
+            </div>
+          </div>
+        }
+        <div class="quick-actions">
+          <button class="btn btn-primary" (click)="switchTab('versions')">
+            <i class="icon-compare"></i> 版本对比
+          </button>
+          <button class="btn btn-primary" (click)="switchTab('details')">
+            <i class="icon-detail"></i> 查看详情
+          </button>
+          <button class="btn btn-success" (click)="switchTab('confirmation')">
+            <i class="icon-confirm"></i> 开始确认
+          </button>
+        </div>
+      </div>
+    }
+
+    <!-- 版本对比标签页 -->
+    @if (activeTab === 'versions') {
+      <div class="versions-content">
+        <!-- 版本选择器 -->
+        <div class="version-selector">
+          <h4>选择方案版本</h4>
+          <div class="version-grid">
+            @for (version of proposalVersions; track version.id) {
+              <div class="version-card" 
+                   [class.selected]="version.isSelected"
+                   (click)="selectVersion(version)">
+                <div class="version-preview">
+                  @if (version.preview) {
+                    <img [src]="version.preview" [alt]="version.name">
+                  } @else {
+                    <div class="preview-placeholder">
+                      <i class="icon-image"></i>
+                    </div>
+                  }
+                </div>
+                <div class="version-info">
+                  <h5>{{ version.name }}</h5>
+                  <p>{{ version.description }}</p>
+                  <div class="version-stats">
+                    <span class="stat">¥{{ version.budget | number }}</span>
+                    <span class="stat">{{ version.workDays }}天</span>
+                  </div>
+                  <div class="preference-actions">
+                    <button class="preference-btn like" 
+                            (click)="markPreference('version', version.id, 'like')"
+                            title="喜欢这个版本">
+                      <i class="icon-heart"></i>
+                    </button>
+                    <button class="preference-btn dislike" 
+                            (click)="markPreference('version', version.id, 'dislike')"
+                            title="不喜欢这个版本">
+                      <i class="icon-heart-broken"></i>
+                    </button>
+                  </div>
+                </div>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 版本对比功能 -->
+        <div class="comparison-controls">
+          <button class="btn btn-secondary" (click)="toggleVersionComparison()">
+            <i class="icon-compare"></i> 
+            {{ showVersionComparison ? '隐藏对比' : '显示对比' }}
+          </button>
+        </div>
+
+        @if (showVersionComparison && comparisonVersions) {
+          <div class="version-comparison">
+            <div class="comparison-header">
+              <h4>版本对比分析</h4>
+              <button class="btn btn-secondary" (click)="showVersionComparison = false">
+                <i class="icon-close"></i> 关闭对比
+              </button>
+            </div>
+            
+            <div class="comparison-table">
+              <div class="comparison-row header">
+                <div class="field-name">对比项目</div>
+                <div class="version-a">{{ comparisonVersions.versionA.name }}</div>
+                <div class="version-b">{{ comparisonVersions.versionB.name }}</div>
+                <div class="difference">差异</div>
+              </div>
+              
+              @if (versionDifferences) {
+                @for (diff of versionDifferences | keyvalue; track diff.key) {
+                  <div class="comparison-row" 
+                       [attr.data-field]="diff.key"
+                       (click)="highlightDifferences(diff.key)">
+                    <div class="field-name">{{ diff.value.field }}</div>
+                    <div class="version-a">{{ diff.value.valueA }}</div>
+                    <div class="version-b">{{ diff.value.valueB }}</div>
+                    <div class="difference" 
+                         [class.increase]="diff.value.type === 'increase'"
+                         [class.decrease]="diff.value.type === 'decrease'"
+                         [class.different]="diff.value.type === 'different'"
+                         [class.same]="diff.value.type === 'same'">
+                      @if (diff.value.type === 'increase') {
+                        <i class="icon-arrow-up"></i> +{{ diff.value.difference }}
+                      } @else if (diff.value.type === 'decrease') {
+                        <i class="icon-arrow-down"></i> {{ diff.value.difference }}
+                      } @else if (diff.value.type === 'different') {
+                        <i class="icon-different"></i> 不同
+                      } @else {
+                        <i class="icon-same"></i> 相同
+                      }
+                    </div>
+                  </div>
+                }
+              }
+            </div>
+            
+            <div class="comparison-summary">
+              <h5>对比总结</h5>
+              <div class="summary-cards">
+                <div class="summary-card budget">
+                  <i class="icon-money"></i>
+                  <div class="summary-content">
+                    <span class="label">预算差异</span>
+                    <span class="value">¥{{ (comparisonVersions.versionB.budget - comparisonVersions.versionA.budget) | number }}</span>
+                  </div>
+                </div>
+                <div class="summary-card timeline">
+                  <i class="icon-time"></i>
+                  <div class="summary-content">
+                    <span class="label">工期差异</span>
+                    <span class="value">{{ comparisonVersions.versionB.workDays - comparisonVersions.versionA.workDays }}天</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        }
+      </div>
+    }
+
+    <!-- 详细方案标签页 -->
+    @if (activeTab === 'details' && quantifiedProposal) {
+      <div class="details-content">
+        
+        <!-- 空间功能模块 -->
+        <div class="detail-section">
+          <h4>
+            <i class="icon-space"></i> 空间功能模块
+            @if (getSpaceModulesVerified()) {
+              <span class="status-badge verified">已验证</span>
+            }
+          </h4>
+          @for (module of quantifiedProposal.spaceModules; track module.id) {
+            <div class="space-module">
+              <div class="module-header">
+                <h5>{{ module.name }}</h5>
+                <span class="requirements-tag">{{ module.requirements }}</span>
+              </div>
+              <div class="furniture-list">
+                @for (furniture of module.furniture; track furniture.name) {
+                  <div class="furniture-item">
+                    <div class="furniture-info">
+                      <h6>{{ furniture.name }}</h6>
+                      <div class="furniture-specs">
+                        <span class="spec">尺寸: {{ furniture.size }}</span>
+                        <span class="spec">材质: {{ furniture.material }}</span>
+                        <span class="spec">颜色: {{ furniture.color }}</span>
+                        @if (furniture.price) {
+                          <span class="spec price">¥{{ furniture.price | number }}</span>
+                        }
+                      </div>
+                      <div class="furniture-features">
+                        @for (feature of furniture.features; track feature) {
+                          <span class="feature-tag">{{ feature }}</span>
+                        }
+                      </div>
+                    </div>
+                  </div>
+                }
+              </div>
+            </div>
+          }
+        </div>
+
+        <!-- 色彩与质感模块 -->
+        <div class="detail-section">
+          <h4>
+            <i class="icon-color"></i> 色彩与质感
+            @if (quantifiedProposal.colorAndTexture.verified) {
+              <span class="status-badge verified">已验证</span>
+            }
+          </h4>
+          <div class="color-texture-content">
+            <div class="main-color-info">
+              <h5>主色调</h5>
+              <div class="color-display">
+                <div class="color-preview large" 
+                     [style.background-color]="'rgb(' + quantifiedProposal.colorAndTexture.mainColor.rgb + ')'">
+                </div>
+                <div class="color-details">
+                  <span>RGB: {{ quantifiedProposal.colorAndTexture.mainColor.rgb }}</span>
+                  <span>色温: {{ quantifiedProposal.colorAndTexture.mainColor.colorTemp }}</span>
+                </div>
+              </div>
+            </div>
+
+            <div class="material-ratios">
+              <h5>材质比例</h5>
+              @for (material of quantifiedProposal.colorAndTexture.materialRatio; track material.material) {
+                <div class="material-item">
+                  <span class="material-name">{{ material.material }}</span>
+                  <div class="ratio-bar">
+                    <div class="ratio-fill" [style.width.%]="material.percentage"></div>
+                  </div>
+                  <span class="ratio-value">{{ material.percentage }}%</span>
+                </div>
+              }
+            </div>
+
+            <div class="color-samples">
+              <h5>色彩样本对比</h5>
+              @for (sample of quantifiedProposal.colorAndTexture.colorSamples; track sample.name) {
+                <div class="sample-comparison">
+                  <span class="sample-name">{{ sample.name }}</span>
+                  <div class="sample-colors">
+                    <div class="sample-color">
+                      <div class="color-preview" [style.background-color]="'rgb(' + sample.rgb + ')'"></div>
+                      <span>参考色</span>
+                    </div>
+                    <div class="sample-color">
+                      <div class="color-preview" [style.background-color]="'rgb(' + sample.actual + ')'"></div>
+                      <span>实际方案色</span>
+                      <button class="btn btn-sm btn-secondary" 
+                              (click)="adjustColorSample(sample.name, sample.actual)">
+                        <i class="icon-edit"></i> 调整
+                      </button>
+                    </div>
+                    <div class="difference-indicator">
+                      <span class="difference-value" 
+                            [class.good]="sample.difference <= 5"
+                            [class.warning]="sample.difference > 5 && sample.difference <= 10"
+                            [class.error]="sample.difference > 10">
+                        差异度: {{ sample.difference }}%
+                      </span>
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+          </div>
+        </div>
+
+        <!-- 结构适配模块 -->
+        <div class="detail-section">
+          <h4>
+            <i class="icon-structure"></i> 结构适配
+            @if (quantifiedProposal.structuralAdaptation.verified) {
+              <span class="status-badge verified">已验证</span>
+            }
+          </h4>
+          <div class="structural-content">
+            <div class="constraints-section">
+              <h5>结构约束</h5>
+              @for (constraint of quantifiedProposal.structuralAdaptation.constraints; track constraint.position) {
+                <div class="constraint-item" [class]="constraint.type">
+                  <div class="constraint-icon">
+                    <i [class]="'icon-' + constraint.type"></i>
+                  </div>
+                  <div class="constraint-info">
+                    <span class="constraint-type">{{ constraint.type === 'load-bearing' ? '承重柱' : constraint.type }}</span>
+                    <span class="constraint-position">位置: {{ constraint.position }}</span>
+                    <span class="constraint-size">尺寸: {{ constraint.size }}</span>
+                    <span class="constraint-status" 
+                          [class.changeable]="constraint.changeable"
+                          [class.fixed]="!constraint.changeable">
+                      {{ constraint.changeable ? '可调整' : '不可改动' }}
+                    </span>
+                  </div>
+                </div>
+              }
+            </div>
+
+            <div class="adaptations-section">
+              <h5>适配方案</h5>
+              @for (adaptation of quantifiedProposal.structuralAdaptation.adaptations; track adaptation.area) {
+                <div class="adaptation-item">
+                  <div class="adaptation-area">{{ adaptation.area }}</div>
+                  <div class="adaptation-solution">{{ adaptation.solution }}</div>
+                  <div class="adaptation-flow">人流动线宽度: {{ adaptation.flowWidth }}</div>
+                  <div class="compliance-status" 
+                       [class.compliant]="adaptation.compliance"
+                       [class.non-compliant]="!adaptation.compliance">
+                    {{ adaptation.compliance ? '符合标准' : '需要调整' }}
+                  </div>
+                </div>
+              }
+            </div>
+          </div>
+        </div>
+
+        <!-- 可行性验证模块 -->
+        <div class="detail-section">
+          <h4>
+            <i class="icon-verify"></i> 可行性验证
+            @if (quantifiedProposal.feasibilityVerification.verified) {
+              <span class="status-badge verified">已验证</span>
+            }
+          </h4>
+          <div class="feasibility-content">
+            <div class="verification-item">
+              <h5>结构可行性</h5>
+              <div class="status-indicator" [class]="quantifiedProposal.feasibilityVerification.structural.status">
+                <i [class]="'icon-' + quantifiedProposal.feasibilityVerification.structural.status"></i>
+                <span>{{ quantifiedProposal.feasibilityVerification.structural.message }}</span>
+              </div>
+            </div>
+
+            <div class="verification-item">
+              <h5>成本与工期</h5>
+              <div class="cost-timeline">
+                <div class="cost-info">
+                  <span class="label">预算:</span>
+                  <span class="value">¥{{ quantifiedProposal.feasibilityVerification.cost.budget | number }}</span>
+                </div>
+                <div class="timeline-info">
+                  <span class="label">采购周期:</span>
+                  <span class="value">{{ quantifiedProposal.feasibilityVerification.cost.timeline }}</span>
+                </div>
+                @if (quantifiedProposal.feasibilityVerification.cost.risks.length > 0) {
+                  <div class="risks-section">
+                    <h6>风险提示</h6>
+                    @for (risk of quantifiedProposal.feasibilityVerification.cost.risks; track risk) {
+                      <div class="risk-item">
+                        <i class="icon-warning"></i>
+                        <span>{{ risk }}</span>
+                      </div>
+                    }
+                  </div>
+                }
+              </div>
+            </div>
+
+            <div class="verification-item">
+              <h5>工期安排</h5>
+              <div class="timeline-phases">
+                @for (phase of quantifiedProposal.feasibilityVerification.timeline.phases; track phase.name) {
+                  <div class="phase-item">
+                    <div class="phase-name">{{ phase.name }}</div>
+                    <div class="phase-duration">{{ phase.days }}天</div>
+                    @if (phase.dependencies.length > 0) {
+                      <div class="phase-dependencies">
+                        依赖: {{ phase.dependencies.join(', ') }}
+                      </div>
+                    }
+                  </div>
+                }
+              </div>
+              <div class="total-timeline">
+                总工期: {{ quantifiedProposal.feasibilityVerification.timeline.totalDays }}天
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+
+    <!-- 协作批注标签页 -->
+    @if (activeTab === 'collaboration') {
+      <div class="collaboration-content">
+        <!-- 批注工具栏 -->
+        <div class="annotation-toolbar">
+          <form [formGroup]="annotationForm" class="annotation-form">
+            <select formControlName="type" class="form-control">
+              <option value="question">疑问</option>
+              <option value="suggestion">建议</option>
+              <option value="confirmation">确认</option>
+            </select>
+            <div class="input-with-mentions">
+              <input type="text" 
+                     formControlName="content" 
+                     placeholder="输入批注内容,使用@提及用户..." 
+                     class="form-control"
+                     (input)="onContentInput($event)">
+              @if (showMentionSuggestions) {
+                <div class="mention-suggestions">
+                  @for (user of filteredMentionUsers; track user.id) {
+                    <div class="mention-item" (click)="selectMention(user)">
+                      <span class="user-name">{{ user.name }}</span>
+                      <span class="user-role">{{ user.role }}</span>
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+            <button type="button" 
+                    (click)="addAnnotation($event)"
+                    [disabled]="!annotationForm.valid"
+                    class="btn btn-primary">
+              <i class="icon-comment"></i> 添加批注
+            </button>
+          </form>
+        </div>
+
+        <!-- 批注列表 -->
+        <div class="annotations-list">
+          <h4>协作批注 ({{ annotations.length }})</h4>
+          @if (annotations.length === 0) {
+            <div class="empty-state">
+              <i class="icon-comment"></i>
+              <p>暂无批注,点击上方添加批注开始协作</p>
+            </div>
+          } @else {
+            @for (annotation of annotations; track annotation.id) {
+              <div class="annotation-item" [class]="annotation.status">
+                <div class="annotation-header">
+                  <div class="author-info">
+                    <span class="author-name" [class]="getRoleClass(annotation.role)">
+                      {{ annotation.author }}
+                    </span>
+                    <span class="annotation-type">{{ annotation.type === 'question' ? '疑问' : annotation.type === 'suggestion' ? '建议' : '确认' }}</span>
+                    <span class="annotation-time">{{ annotation.timestamp | date:'MM-dd HH:mm' }}</span>
+                  </div>
+                  <div class="annotation-actions">
+                    @if (annotation.status === 'open') {
+                      <button class="btn btn-sm btn-success" (click)="resolveAnnotation(annotation.id)">
+                        解决
+                      </button>
+                    } @else {
+                      <span class="status-badge resolved">已解决</span>
+                    }
+                  </div>
+                </div>
+                <div class="annotation-content">
+                  <span [innerHTML]="formatContentWithMentions(annotation.content)"></span>
+                  @if (annotation.position) {
+                    <div class="annotation-position">
+                      <i class="icon-location"></i>
+                      <small>位置: {{ annotation.position.element }} ({{ annotation.position.x }}, {{ annotation.position.y }})</small>
+                    </div>
+                  }
+                </div>
+                @if (annotation.replies.length > 0) {
+                  <div class="annotation-replies">
+                    @for (reply of annotation.replies; track reply.id) {
+                      <div class="reply-item">
+                        <div class="reply-header">
+                          <span class="reply-author" [class]="getRoleClass(reply.role)">{{ reply.author }}</span>
+                          <span class="reply-time">{{ reply.timestamp | date:'MM-dd HH:mm' }}</span>
+                        </div>
+                        <div class="reply-content">
+                          <span [innerHTML]="formatContentWithMentions(reply.content)"></span>
+                        </div>
+                      </div>
+                    }
+                  </div>
+                }
+                @if (annotation.status === 'open') {
+                  <div class="reply-form">
+                    <input type="text" 
+                           #replyInput
+                           placeholder="回复批注..." 
+                           class="form-control">
+                    <button class="btn btn-sm btn-secondary" 
+                            (click)="replyToAnnotation(annotation.id, replyInput.value, replyInput)">
+                      回复
+                    </button>
+                  </div>
+                }
+              </div>
+            }
+          }
+        </div>
+      </div>
+    }
+
+    <!-- 确认流程标签页 -->
+    @if (activeTab === 'confirmation') {
+      <div class="confirmation-content">
+        <div class="confirmation-header">
+          <h4>方案确认流程</h4>
+          <div class="overall-progress">
+            <div class="progress-circle">
+              <svg viewBox="0 0 36 36" class="circular-chart">
+                <path class="circle-bg"
+                      d="M18 2.0845
+                         a 15.9155 15.9155 0 0 1 0 31.831
+                         a 15.9155 15.9155 0 0 1 0 -31.831"
+                />
+                <path class="circle"
+                      [style.stroke-dasharray]="getProgressPercentage() + ', 100'"
+                      d="M18 2.0845
+                         a 15.9155 15.9155 0 0 1 0 31.831
+                         a 15.9155 15.9155 0 0 1 0 -31.831"
+                />
+                <text x="18" y="20.35" class="percentage">{{ getProgressPercentage() | number:'1.0-0' }}%</text>
+              </svg>
+            </div>
+            <div class="progress-info">
+              <span class="progress-label">整体进度</span>
+              <span class="progress-description">
+                {{ getConfirmedRequiredCount() }} / 
+                {{ getRequiredCount() }} 项已确认
+              </span>
+            </div>
+          </div>
+        </div>
+
+        <!-- 确认步骤列表 -->
+        <div class="confirmation-steps">
+          @for (step of confirmationSteps; track step.id) {
+            <div class="confirmation-step" [class]="getStatusClass(step.status)">
+              <div class="step-indicator">
+                <div class="step-number">{{ $index + 1 }}</div>
+                @if (step.status === 'confirmed') {
+                  <i class="icon-check"></i>
+                } @else if (step.status === 'rejected') {
+                  <i class="icon-close"></i>
+                } @else {
+                  <i class="icon-pending"></i>
+                }
+              </div>
+              <div class="step-content">
+                <div class="step-header">
+                  <h5>{{ step.name }}</h5>
+                  @if (step.required) {
+                    <span class="required-badge">必需</span>
+                  }
+                  @if (step.timestamp) {
+                    <span class="step-time">{{ step.timestamp | date:'MM-dd HH:mm' }}</span>
+                  }
+                </div>
+                <p class="step-description">{{ step.description }}</p>
+                <div class="step-actions">
+                  @if (step.status === 'pending') {
+                    <button class="btn btn-success" (click)="confirmStep(step.id)">
+                      <i class="icon-check"></i> 确认
+                    </button>
+                    <button class="btn btn-danger" (click)="showRejectDialog(step.id)">
+                      <i class="icon-close"></i> 拒绝
+                    </button>
+                  } @else if (step.status === 'rejected') {
+                    <button class="btn btn-success" (click)="confirmStep(step.id)">
+                      <i class="icon-check"></i> 重新确认
+                    </button>
+                    @if (step.rejectionReason) {
+                      <div class="rejection-reason">
+                        <small>拒绝原因:{{ step.rejectionReason }}</small>
+                      </div>
+                    }
+                  } @else {
+                    <span class="status-text confirmed">已确认</span>
+                    @if (step.confirmedAt) {
+                      <small class="confirm-time">{{ step.confirmedAt | date:'yyyy-MM-dd HH:mm' }}</small>
+                    }
+                  }
+                </div>
+              </div>
+            </div>
+          }
+        </div>
+
+        <!-- 最终确认按钮 -->
+        <div class="final-confirmation">
+          @if (allStepsConfirmed) {
+            <div class="confirmation-ready">
+              <div class="ready-message">
+                <i class="icon-success"></i>
+                <h4>所有必需项目已确认完成</h4>
+                <p>您可以进行最终确认,确认后将进入设计执行阶段</p>
+              </div>
+              @if (!isFinalConfirmed) {
+                <button class="btn btn-primary btn-large" (click)="finalConfirm()">
+                  <i class="icon-confirm"></i> 最终确认方案
+                </button>
+              } @else {
+                <div class="final-confirmed">
+                  <i class="icon-check-circle"></i>
+                  <span>方案已最终确认</span>
+                  <small>确认时间:{{ finalConfirmedAt | date:'yyyy-MM-dd HH:mm:ss' }}</small>
+                </div>
+              }
+              <button class="btn btn-secondary" (click)="exportConfirmationReport()">
+                <i class="icon-download"></i> 导出确认报告
+              </button>
+            </div>
+          } @else {
+            <div class="confirmation-pending">
+              <div class="pending-message">
+                <i class="icon-warning"></i>
+                <h4>请完成所有必需确认项目</h4>
+                <p>还有 {{ getRequiredUnconfirmedCount() }} 项必需内容需要确认</p>
+              </div>
+            </div>
+          }
+        </div>
+      </div>
+    }
+  </div>
+
+  <!-- 方案调整面板 -->
+  @if (isAdjusting) {
+    <div class="adjustment-panel">
+      <div class="panel-header">
+        <h4>方案调整</h4>
+        <button class="btn btn-close" (click)="isAdjusting = false">
+          <i class="icon-close"></i>
+        </button>
+      </div>
+      <form [formGroup]="adjustmentForm" class="adjustment-form">
+        <div class="form-group">
+          <label>调整元素</label>
+          <select formControlName="element" class="form-control">
+            <option value="furniture">家具</option>
+            <option value="color">色彩</option>
+            <option value="material">材质</option>
+            <option value="layout">布局</option>
+          </select>
+        </div>
+        <div class="form-group">
+          <label>调整参数</label>
+          <input type="text" formControlName="parameter" class="form-control" placeholder="如:沙发尺寸">
+        </div>
+        <div class="form-group">
+          <label>新值</label>
+          <input type="text" formControlName="value" class="form-control" placeholder="如:2.5m×1.6m">
+        </div>
+        <div class="form-group">
+          <label>调整原因</label>
+          <textarea formControlName="reason" class="form-control" placeholder="说明调整原因..."></textarea>
+        </div>
+        <div class="form-actions">
+          <button type="button" 
+                  (click)="adjustProposal()" 
+                  [disabled]="!adjustmentForm.valid"
+                  class="btn btn-primary">
+            应用调整
+          </button>
+          <button type="button" (click)="isAdjusting = false" class="btn btn-secondary">
+            取消
+          </button>
+        </div>
+      </form>
+    </div>
+  }
+
+  <!-- 版本历史面板 -->
+  @if (versionHistory.length > 0) {
+    <div class="version-history">
+      <h4>版本历史</h4>
+      <div class="history-list">
+        @for (historyItem of versionHistory; track $index) {
+          <div class="history-item">
+            <div class="history-time">{{ historyItem.timestamp | date:'MM-dd HH:mm' }}</div>
+            <div class="history-actions">
+              <button class="btn btn-sm btn-secondary" (click)="restoreVersion($index)">
+                恢复此版本
+              </button>
+            </div>
+          </div>
+        }
+      </div>
+    </div>
+  }
+
+  <!-- 浮动操作按钮 -->
+  <div class="floating-actions">
+    <button class="fab" (click)="isAdjusting = true" title="调整方案">
+      <i class="icon-edit"></i>
+    </button>
+    <button class="fab" (click)="showVersionHistory = !showVersionHistory" title="版本历史">
+      <i class="icon-history"></i>
+    </button>
+  </div>
+</div>
+
+<!-- 拒绝原因对话框 -->
+@if (showRejectReasonDialog) {
+  <div class="modal-overlay" (click)="closeRejectDialog()">
+    <div class="modal-dialog" (click)="$event.stopPropagation()">
+      <div class="modal-header">
+        <h4>拒绝原因</h4>
+        <button class="btn btn-close" (click)="closeRejectDialog()">
+          <i class="icon-close"></i>
+        </button>
+      </div>
+      <div class="modal-body">
+        <div class="form-group">
+          <label>请说明拒绝原因:</label>
+          <textarea 
+            [(ngModel)]="rejectionReason" 
+            class="form-control" 
+            rows="4"
+            placeholder="请详细说明拒绝的原因...">
+          </textarea>
+        </div>
+      </div>
+      <div class="modal-footer">
+        <button class="btn btn-danger" (click)="confirmReject()">
+          <i class="icon-close"></i> 确认拒绝
+        </button>
+        <button class="btn btn-secondary" (click)="closeRejectDialog()">
+          取消
+        </button>
+      </div>
+    </div>
+  </div>
+}
+
+<!-- 确认历史对话框 -->
+@if (showConfirmationHistory) {
+  <div class="modal-overlay" (click)="showConfirmationHistory = false">
+    <div class="modal-dialog large" (click)="$event.stopPropagation()">
+      <div class="modal-header">
+        <h4>确认历史记录</h4>
+        <button class="btn btn-close" (click)="showConfirmationHistory = false">
+          <i class="icon-close"></i>
+        </button>
+      </div>
+      <div class="modal-body">
+        <div class="history-timeline">
+          @for (record of confirmationHistory; track record.id) {
+            <div class="timeline-item" [class]="record.action">
+              <div class="timeline-marker">
+                @if (record.action === 'confirmed') {
+                  <i class="icon-check"></i>
+                } @else if (record.action === 'rejected') {
+                  <i class="icon-close"></i>
+                } @else {
+                  <i class="icon-info"></i>
+                }
+              </div>
+              <div class="timeline-content">
+                <div class="timeline-header">
+                  <span class="action-text">{{ getActionText(record.action) }}</span>
+                  <span class="timestamp">{{ record.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}</span>
+                </div>
+                <div class="timeline-details">
+                  <strong>{{ record.stepName }}</strong>
+                  @if (record.reason) {
+                    <p>{{ record.reason }}</p>
+                  }
+                  @if (record.operator) {
+                    <small>操作人:{{ record.operator }}</small>
+                  }
+                </div>
+              </div>
+            </div>
+          }
+        </div>
+      </div>
+    </div>
+  </div>
+}

+ 1676 - 0
src/app/shared/components/proposal-confirm-card/proposal-confirm-card.scss

@@ -0,0 +1,1676 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host {
+  display: block;
+  height: 100%;
+}
+
+.proposal-confirm-card {
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: $ios-spacing-md;
+    
+    h4 {
+      margin: 0;
+      font-size: $ios-font-size-sm;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+    
+    .progress-indicator {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-xs;
+      
+      .progress-bar {
+        width: 80px;
+        height: 4px;
+        background: $ios-border;
+        border-radius: 2px;
+        overflow: hidden;
+        
+        .progress-fill {
+          height: 100%;
+          background: linear-gradient(90deg, #007AFF 0%, #34C759 100%);
+          transition: width 0.3s ease;
+        }
+      }
+      
+      .progress-text {
+        font-size: $ios-font-size-xs;
+        color: $ios-text-secondary;
+        font-weight: $ios-font-weight-medium;
+      }
+    }
+  }
+
+  // 标签页导航
+  .tab-navigation {
+    display: flex;
+    background: $ios-background-secondary;
+    border-radius: 8px;
+    padding: 2px;
+    margin-bottom: $ios-spacing-md;
+    
+    .tab-button {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: $ios-spacing-xs;
+      padding: $ios-spacing-sm $ios-spacing-xs;
+      border: none;
+      background: transparent;
+      color: $ios-text-secondary;
+      font-weight: $ios-font-weight-medium;
+      border-radius: 6px;
+      transition: all 0.2s ease;
+      cursor: pointer;
+      
+      svg {
+        width: 14px;
+        height: 14px;
+      }
+      
+      &:hover {
+        color: $ios-text-primary;
+        background: rgba(0, 122, 255, 0.1);
+      }
+      
+      &.active {
+        background: white;
+        color: $ios-primary;
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+      }
+    }
+  }
+
+  // 标签页内容
+  .tab-content {
+    min-height: 300px;
+  }
+
+  // 方案概览部分
+  .overview-section {
+    .overview-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-lg;
+      
+      .overview-item {
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        padding: $ios-spacing-md;
+        background: white;
+        
+        h5 {
+          margin: 0 0 $ios-spacing-sm 0;
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+        }
+        
+        .overview-content {
+          .overview-list {
+            .overview-row {
+              display: grid;
+              grid-template-columns: 1fr 2fr 2fr 1fr;
+              border-bottom: 1px solid $ios-border;
+              
+              &:last-child {
+                border-bottom: none;
+              }
+              
+              .overview-cell {
+                padding: $ios-spacing-sm;
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+                
+                &.cell-name {
+                  font-weight: $ios-font-weight-medium;
+                  color: $ios-text-primary;
+                }
+                
+                &.cell-price {
+                  color: $ios-primary;
+                  font-weight: $ios-font-weight-semibold;
+                }
+                
+                &.cell-status {
+                  .status-badge {
+                    display: inline-block;
+                    padding: 2px 6px;
+                    border-radius: 4px;
+                    font-size: $ios-font-size-xs;
+                    font-weight: $ios-font-weight-medium;
+                    
+                    &.status-confirmed {
+                      background: rgba(52, 199, 89, 0.1);
+                      color: #34C759;
+                    }
+                    
+                    &.status-pending {
+                      background: rgba(255, 149, 0, 0.1);
+                      color: #FF9500;
+                    }
+                    
+                    &.status-rejected {
+                      background: rgba(255, 59, 48, 0.1);
+                      color: $ios-error;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 版本对比部分
+  .comparison-section {
+    .comparison-warning {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-xs;
+      padding: $ios-spacing-sm;
+      background: rgba(255, 149, 0, 0.1);
+      border: 1px solid rgba(255, 149, 0, 0.2);
+      border-radius: 6px;
+      color: #FF9500;
+      font-size: $ios-font-size-xs;
+      margin-bottom: $ios-spacing-md;
+      
+      svg {
+        width: 16px;
+        height: 16px;
+        flex-shrink: 0;
+      }
+    }
+    
+    .comparison-grid {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: $ios-spacing-lg;
+      
+      .version-panel {
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        padding: $ios-spacing-md;
+        background: white;
+        
+        .panel-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: $ios-spacing-md;
+          padding-bottom: $ios-spacing-sm;
+          border-bottom: 1px solid $ios-border;
+          
+          h5 {
+            margin: 0;
+            font-size: $ios-font-size-sm;
+            font-weight: $ios-font-weight-semibold;
+            color: $ios-text-primary;
+          }
+          
+          .version-meta {
+            display: flex;
+            flex-direction: column;
+            align-items: flex-end;
+            gap: $ios-spacing-xs;
+            
+            .version-date {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+            
+            .version-status {
+              .status-badge {
+                padding: 2px 6px;
+                border-radius: 4px;
+                font-size: $ios-font-size-xs;
+                font-weight: $ios-font-weight-medium;
+                
+                &.status-current {
+                  background: rgba(0, 122, 255, 0.1);
+                  color: $ios-primary;
+                }
+                
+                &.status-previous {
+                  background: $ios-background-secondary;
+                  color: $ios-text-secondary;
+                }
+              }
+            }
+          }
+        }
+        
+        .version-content {
+          .content-section {
+            margin-bottom: $ios-spacing-md;
+            
+            &:last-child {
+              margin-bottom: 0;
+            }
+            
+            h6 {
+              margin: 0 0 $ios-spacing-xs 0;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+              color: $ios-text-primary;
+              text-transform: uppercase;
+              letter-spacing: 0.5px;
+            }
+            
+            .content-list {
+              .content-item {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                padding: $ios-spacing-xs 0;
+                border-bottom: 1px dashed $ios-border;
+                
+                &:last-child {
+                  border-bottom: none;
+                }
+                
+                .item-name {
+                  font-size: $ios-font-size-xs;
+                  color: $ios-text-primary;
+                }
+                
+                .item-value {
+                  font-size: $ios-font-size-xs;
+                  color: $ios-text-secondary;
+                  font-weight: $ios-font-weight-medium;
+                  
+                  &.value-price {
+                    color: $ios-primary;
+                  }
+                  
+                  &.value-changed {
+                    color: #FF9500;
+                    position: relative;
+                    
+                    &::after {
+                      content: '●';
+                      position: absolute;
+                      right: -8px;
+                      top: 0;
+                      font-size: 6px;
+                      color: #FF9500;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    .differences-summary {
+      margin-top: $ios-spacing-lg;
+      padding: $ios-spacing-md;
+      background: $ios-background-secondary;
+      border-radius: 8px;
+      
+      h5 {
+        margin: 0 0 $ios-spacing-sm 0;
+        font-size: $ios-font-size-sm;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .differences-list {
+        .difference-item {
+          display: flex;
+          align-items: center;
+          gap: $ios-spacing-sm;
+          padding: $ios-spacing-xs 0;
+          
+          .difference-icon {
+            width: 16px;
+            height: 16px;
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 10px;
+            color: white;
+            
+            &.icon-added {
+              background: #34C759;
+            }
+            
+            &.icon-removed {
+              background: #FF3B30;
+            }
+            
+            &.icon-modified {
+              background: #FF9500;
+            }
+          }
+          
+          .difference-text {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-primary;
+          }
+        }
+      }
+    }
+  }
+
+  // 家具清单部分
+  .furniture-section {
+    .furniture-categories {
+      .category-group {
+        margin-bottom: $ios-spacing-lg;
+        
+        .category-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: $ios-spacing-md;
+          padding-bottom: $ios-spacing-sm;
+          border-bottom: 2px solid $ios-border;
+          
+          h5 {
+            margin: 0;
+            font-size: $ios-font-size-sm;
+            font-weight: $ios-font-weight-semibold;
+            color: $ios-text-primary;
+          }
+          
+          .category-summary {
+            display: flex;
+            gap: $ios-spacing-md;
+            
+            .summary-item {
+              text-align: center;
+              
+              .summary-value {
+                display: block;
+                font-size: $ios-font-size-sm;
+                font-weight: $ios-font-weight-bold;
+                color: $ios-primary;
+              }
+              
+              .summary-label {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+              }
+            }
+          }
+        }
+        
+        .furniture-grid {
+          display: grid;
+          grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+          gap: $ios-spacing-md;
+          
+          .furniture-card {
+            border: 1px solid $ios-border;
+            border-radius: 8px;
+            padding: $ios-spacing-md;
+            background: white;
+            transition: all 0.2s ease;
+            
+            &:hover {
+              box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+            }
+            
+            .furniture-image {
+              width: 100%;
+              height: 120px;
+              background: $ios-background-secondary;
+              border-radius: 6px;
+              margin-bottom: $ios-spacing-sm;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              
+              img {
+                max-width: 100%;
+                max-height: 100%;
+                border-radius: 6px;
+              }
+              
+              .image-placeholder {
+                color: $ios-text-tertiary;
+                font-size: $ios-font-size-xs;
+              }
+            }
+            
+            .furniture-info {
+              h6 {
+                margin: 0 0 $ios-spacing-xs 0;
+                font-size: $ios-font-size-sm;
+                font-weight: $ios-font-weight-semibold;
+                color: $ios-text-primary;
+              }
+              
+              .furniture-specs {
+                display: flex;
+                flex-wrap: wrap;
+                gap: $ios-spacing-sm;
+                margin-bottom: $ios-spacing-xs;
+                
+                .spec {
+                  font-size: $ios-font-size-xs;
+                  color: $ios-text-secondary;
+                  
+                  &.price {
+                    font-weight: $ios-font-weight-medium;
+                    color: $ios-primary;
+                  }
+                }
+              }
+              
+              .furniture-features {
+                display: flex;
+                flex-wrap: wrap;
+                gap: $ios-spacing-xs;
+                
+                .feature-tag {
+                  padding: 2px $ios-spacing-xs;
+                  background: $ios-background-tertiary;
+                  color: $ios-text-secondary;
+                  border-radius: 4px;
+                  font-size: $ios-font-size-xs;
+                }
+              }
+            }
+            
+            .furniture-actions {
+              display: flex;
+              gap: $ios-spacing-xs;
+              margin-top: $ios-spacing-sm;
+              padding-top: $ios-spacing-sm;
+              border-top: 1px solid $ios-border;
+              
+              .btn {
+                flex: 1;
+                padding: $ios-spacing-xs;
+                border: none;
+                border-radius: 4px;
+                font-size: $ios-font-size-xs;
+                cursor: pointer;
+                transition: all 0.2s ease;
+                
+                &.btn-primary {
+                  background: $ios-primary;
+                  color: white;
+                  
+                  &:hover {
+                    background: darken($ios-primary, 10%);
+                  }
+                }
+                
+                &.btn-secondary {
+                  background: $ios-background-secondary;
+                  color: $ios-text-primary;
+                  
+                  &:hover {
+                    background: darken($ios-background-secondary, 5%);
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 协作批注部分
+  .collaboration-section {
+    .collaboration-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: $ios-spacing-md;
+      
+      h5 {
+        margin: 0;
+        font-size: $ios-font-size-sm;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .collaboration-controls {
+        display: flex;
+        gap: $ios-spacing-sm;
+        
+        .filter-dropdown {
+          position: relative;
+          
+          .dropdown-toggle {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border: 1px solid $ios-border;
+            border-radius: 4px;
+            background: white;
+            font-size: $ios-font-size-xs;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+    
+    .annotations-list {
+      .annotation-item {
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        background: white;
+        
+        &.annotation-resolved {
+          opacity: 0.7;
+          background: rgba(52, 199, 89, 0.02);
+          border-color: rgba(52, 199, 89, 0.3);
+        }
+        
+        &.annotation-urgent {
+          border-left: 4px solid #FF3B30;
+        }
+        
+        .annotation-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-sm;
+          
+          .annotation-meta {
+            .annotation-author {
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+              color: $ios-text-primary;
+              margin-bottom: 2px;
+            }
+            
+            .annotation-time {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-tertiary;
+            }
+            
+            .annotation-location {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+              margin-top: 2px;
+            }
+          }
+          
+          .annotation-actions {
+            display: flex;
+            gap: $ios-spacing-xs;
+            
+            .action-btn {
+              padding: 4px 8px;
+              border: none;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              cursor: pointer;
+              
+              &.btn-resolve {
+                background: rgba(52, 199, 89, 0.1);
+                color: #34C759;
+              }
+              
+              &.btn-reply {
+                background: rgba(0, 122, 255, 0.1);
+                color: $ios-primary;
+              }
+            }
+          }
+        }
+        
+        .annotation-content {
+          .annotation-text {
+            font-size: $ios-font-size-sm;
+            color: $ios-text-primary;
+            line-height: 1.4;
+            margin-bottom: $ios-spacing-sm;
+          }
+          
+          .annotation-attachments {
+            display: flex;
+            gap: $ios-spacing-xs;
+            margin-bottom: $ios-spacing-sm;
+            
+            .attachment-item {
+              padding: $ios-spacing-xs;
+              background: $ios-background-secondary;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+          }
+          
+          .annotation-replies {
+            border-top: 1px solid $ios-border;
+            padding-top: $ios-spacing-sm;
+            
+            .reply-item {
+              display: flex;
+              gap: $ios-spacing-sm;
+              margin-bottom: $ios-spacing-sm;
+              
+              .reply-avatar {
+                width: 24px;
+                height: 24px;
+                border-radius: 50%;
+                background: $ios-background-secondary;
+                flex-shrink: 0;
+              }
+              
+              .reply-content {
+                flex: 1;
+                
+                .reply-header {
+                  display: flex;
+                  align-items: center;
+                  gap: $ios-spacing-xs;
+                  margin-bottom: 2px;
+                  
+                  .reply-author {
+                    font-size: $ios-font-size-xs;
+                    font-weight: $ios-font-weight-medium;
+                    color: $ios-text-primary;
+                  }
+                  
+                  .reply-time {
+                    font-size: $ios-font-size-xs;
+                    color: $ios-text-tertiary;
+                  }
+                }
+                
+                .reply-text {
+                  font-size: $ios-font-size-sm;
+                  color: $ios-text-secondary;
+                }
+              }
+            }
+            
+            .reply-form {
+              display: flex;
+              gap: $ios-spacing-sm;
+              margin-top: $ios-spacing-sm;
+              
+              .form-control {
+                flex: 1;
+                padding: $ios-spacing-xs $ios-spacing-sm;
+                border: 1px solid $ios-border;
+                border-radius: $ios-radius-sm;
+                font-size: $ios-font-size-sm;
+              }
+              
+              .btn {
+                padding: $ios-spacing-xs $ios-spacing-sm;
+                border: none;
+                border-radius: $ios-radius-sm;
+                font-size: $ios-font-size-xs;
+                cursor: pointer;
+                
+                &.btn-secondary {
+                  background: $ios-background-tertiary;
+                  color: $ios-text-primary;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    // 协作批注标签
+    .collaboration-content {
+      .annotation-toolbar {
+        background: $ios-background-secondary;
+        border-radius: $ios-radius-md;
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        
+        .annotation-form {
+          display: flex;
+          gap: $ios-spacing-sm;
+          
+          .form-control {
+            flex: 1;
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border: 1px solid $ios-border;
+            border-radius: $ios-radius-sm;
+            font-size: $ios-font-size-sm;
+          }
+          
+          .btn-add {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            background: $ios-primary;
+            color: white;
+            border: none;
+            border-radius: $ios-radius-sm;
+            font-size: $ios-font-size-xs;
+            cursor: pointer;
+          }
+        }
+      }
+      
+      .annotations-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+        gap: $ios-spacing-md;
+        
+        .annotation-card {
+          border: 1px solid $ios-border;
+          border-radius: 8px;
+          padding: $ios-spacing-md;
+          background: white;
+          
+          .annotation-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: $ios-spacing-sm;
+            
+            .annotation-type {
+              padding: 2px 6px;
+              background: $ios-background-secondary;
+              color: $ios-text-secondary;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              text-transform: uppercase;
+            }
+            
+            .annotation-priority {
+              &.priority-high {
+                color: #FF3B30;
+              }
+              
+              &.priority-medium {
+                color: #FF9500;
+              }
+              
+              &.priority-low {
+                color: $ios-text-secondary;
+              }
+            }
+          }
+          
+          .annotation-content {
+            .annotation-title {
+              font-size: $ios-font-size-sm;
+              font-weight: $ios-font-weight-medium;
+              color: $ios-text-primary;
+              margin-bottom: $ios-spacing-xs;
+            }
+            
+            .annotation-description {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+              line-height: 1.4;
+              margin-bottom: $ios-spacing-sm;
+            }
+            
+            .annotation-meta {
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              font-size: $ios-font-size-xs;
+              color: $ios-text-tertiary;
+              
+              .annotation-author {
+                font-weight: $ios-font-weight-medium;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 确认流程部分
+  .confirmation-section {
+    .confirmation-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: $ios-spacing-lg;
+      
+      h4 {
+        margin: 0;
+        font-size: $ios-font-size-md;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .overall-progress {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+        
+        .progress-circle {
+          width: 40px;
+          height: 40px;
+          border-radius: 50%;
+          background: conic-gradient(#34C759 0deg 180deg, $ios-border 180deg 360deg);
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          .progress-inner {
+            width: 32px;
+            height: 32px;
+            border-radius: 50%;
+            background: white;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-size: $ios-font-size-xs;
+            font-weight: $ios-font-weight-bold;
+            color: $ios-text-primary;
+          }
+        }
+        
+        .progress-text {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+        }
+      }
+    }
+    
+    .confirmation-steps {
+      .confirmation-step {
+        display: flex;
+        gap: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        
+        &.pending {
+          .step-indicator {
+            background: $ios-background-tertiary;
+            color: $ios-text-secondary;
+          }
+        }
+        
+        &.confirmed {
+          .step-indicator {
+            background: $ios-success;
+            color: white;
+          }
+        }
+        
+        &.rejected {
+          .step-indicator {
+            background: $ios-error;
+            color: white;
+          }
+        }
+        
+        .step-indicator {
+          width: 40px;
+          height: 40px;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-weight: $ios-font-weight-bold;
+          position: relative;
+          
+          .step-number {
+            position: absolute;
+            font-size: $ios-font-size-xs;
+          }
+          
+          i {
+            font-size: $ios-font-size-sm;
+          }
+        }
+        
+        .step-content {
+          flex: 1;
+          
+          .step-header {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+            margin-bottom: $ios-spacing-xs;
+            
+            h5 {
+              margin: 0;
+              font-size: $ios-font-size-sm;
+              font-weight: $ios-font-weight-semibold;
+              color: $ios-text-primary;
+            }
+            
+            .step-status {
+              padding: 2px 6px;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-medium;
+              
+              &.status-pending {
+                background: rgba(255, 149, 0, 0.1);
+                color: #FF9500;
+              }
+              
+              &.status-confirmed {
+                background: rgba(52, 199, 89, 0.1);
+                color: #34C759;
+              }
+              
+              &.status-rejected {
+                background: rgba(255, 59, 48, 0.1);
+                color: #FF3B30;
+              }
+            }
+          }
+          
+          .step-description {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+            margin-bottom: $ios-spacing-sm;
+            line-height: 1.4;
+          }
+          
+          .step-actions {
+            display: flex;
+            gap: $ios-spacing-xs;
+            
+            .btn {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              border: none;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              cursor: pointer;
+              transition: all 0.2s ease;
+              
+              &.btn-confirm {
+                background: #34C759;
+                color: white;
+                
+                &:hover {
+                  background: darken(#34C759, 10%);
+                }
+              }
+              
+              &.btn-reject {
+                background: #FF3B30;
+                color: white;
+                
+                &:hover {
+                  background: darken(#FF3B30, 10%);
+                }
+              }
+              
+              &.btn-comment {
+                background: $ios-background-secondary;
+                color: $ios-text-primary;
+                
+                &:hover {
+                  background: darken($ios-background-secondary, 5%);
+                }
+              }
+            }
+          }
+          
+          .step-comments {
+            margin-top: $ios-spacing-sm;
+            padding-top: $ios-spacing-sm;
+            border-top: 1px solid $ios-border;
+            
+            .comment-item {
+              display: flex;
+              gap: $ios-spacing-sm;
+              margin-bottom: $ios-spacing-sm;
+              
+              .comment-avatar {
+                width: 24px;
+                height: 24px;
+                border-radius: 50%;
+                background: $ios-background-secondary;
+                flex-shrink: 0;
+              }
+              
+              .comment-content {
+                flex: 1;
+                
+                .comment-header {
+                  display: flex;
+                  align-items: center;
+                  gap: $ios-spacing-xs;
+                  margin-bottom: 2px;
+                  
+                  .comment-author {
+                    font-size: $ios-font-size-xs;
+                    font-weight: $ios-font-weight-medium;
+                    color: $ios-text-primary;
+                  }
+                  
+                  .comment-time {
+                    font-size: $ios-font-size-xs;
+                    color: $ios-text-tertiary;
+                  }
+                }
+                
+                .comment-text {
+                  font-size: $ios-font-size-sm;
+                  color: $ios-text-secondary;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    .confirmation-summary {
+      margin-top: $ios-spacing-lg;
+      padding: $ios-spacing-md;
+      background: $ios-background-secondary;
+      border-radius: 8px;
+      
+      .summary-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+        gap: $ios-spacing-md;
+        
+        .summary-item {
+          text-align: center;
+          
+          .summary-icon {
+            width: 32px;
+            height: 32px;
+            border-radius: 50%;
+            margin: 0 auto $ios-spacing-xs;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            
+            &.icon-confirmed {
+              background: rgba(52, 199, 89, 0.1);
+              color: #34C759;
+            }
+            
+            &.icon-pending {
+              background: rgba(255, 149, 0, 0.1);
+              color: #FF9500;
+            }
+            
+            &.icon-rejected {
+              background: rgba(255, 59, 48, 0.1);
+              color: #FF3B30;
+            }
+          }
+          
+          .summary-value {
+            display: block;
+            font-size: $ios-font-size-lg;
+            font-weight: $ios-font-weight-bold;
+            color: $ios-text-primary;
+            margin-bottom: 2px;
+          }
+          
+          .summary-label {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+          }
+        }
+      }
+    }
+  }
+
+  // 时间轴部分
+  .timeline-section {
+    .timeline-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: $ios-spacing-md;
+      
+      h5 {
+        margin: 0;
+        font-size: $ios-font-size-sm;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .timeline-filters {
+        display: flex;
+        gap: $ios-spacing-xs;
+        
+        .filter-btn {
+          padding: 4px 8px;
+          border: 1px solid $ios-border;
+          border-radius: 4px;
+          background: white;
+          font-size: $ios-font-size-xs;
+          cursor: pointer;
+          
+          &.active {
+            background: $ios-primary;
+            color: white;
+            border-color: $ios-primary;
+          }
+        }
+      }
+    }
+    
+    .timeline-container {
+      position: relative;
+      
+      &::before {
+        content: '';
+        position: absolute;
+        left: 20px;
+        top: 0;
+        bottom: 0;
+        width: 2px;
+        background: $ios-border;
+      }
+      
+      .timeline-item {
+        position: relative;
+        padding-left: 50px;
+        margin-bottom: $ios-spacing-lg;
+        
+        &:last-child {
+          margin-bottom: 0;
+        }
+        
+        .timeline-marker {
+          position: absolute;
+          left: 12px;
+          top: 0;
+          width: 16px;
+          height: 16px;
+          border-radius: 50%;
+          background: white;
+          border: 3px solid $ios-border;
+          
+          &.marker-confirmed {
+            border-color: #34C759;
+            background: #34C759;
+          }
+          
+          &.marker-rejected {
+            border-color: #FF3B30;
+            background: #FF3B30;
+          }
+          
+          &.marker-pending {
+            border-color: #FF9500;
+            background: #FF9500;
+          }
+        }
+        
+        .timeline-content {
+          .timeline-time {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-tertiary;
+            margin-bottom: $ios-spacing-xs;
+          }
+          
+          .timeline-title {
+            font-size: $ios-font-size-sm;
+            font-weight: $ios-font-weight-medium;
+            color: $ios-text-primary;
+            margin-bottom: $ios-spacing-xs;
+          }
+          
+          .timeline-description {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+            line-height: 1.4;
+            margin-bottom: $ios-spacing-sm;
+          }
+          
+          .timeline-meta {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+            
+            .timeline-author {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+            
+            .timeline-status {
+              padding: 2px 6px;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-medium;
+              
+              &.status-confirmed {
+                background: rgba(52, 199, 89, 0.1);
+                color: #34C759;
+              }
+              
+              &.status-rejected {
+                background: rgba(255, 59, 48, 0.1);
+                color: #FF3B30;
+              }
+              
+              &.status-pending {
+                background: rgba(255, 149, 0, 0.1);
+                color: #FF9500;
+              }
+            }
+          }
+          
+          .timeline-attachments {
+            margin-top: $ios-spacing-sm;
+            
+            .attachment-list {
+              display: flex;
+              gap: $ios-spacing-xs;
+              
+              .attachment-item {
+                padding: $ios-spacing-xs;
+                background: $ios-background-secondary;
+                border-radius: 4px;
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+                text-decoration: none;
+                
+                &:hover {
+                  background: darken($ios-background-secondary, 5%);
+                }
+              }
+            }
+          }
+          
+          .timeline-actions {
+            margin-top: $ios-spacing-sm;
+            
+            .action-list {
+              display: flex;
+              gap: $ios-spacing-xs;
+              
+              .action-btn {
+                padding: 4px 8px;
+                border: none;
+                border-radius: 4px;
+                font-size: $ios-font-size-xs;
+                cursor: pointer;
+                
+                &.btn-reply {
+                  background: rgba(0, 122, 255, 0.1);
+                  color: $ios-primary;
+                }
+                
+                &.btn-edit {
+                  background: rgba(255, 149, 0, 0.1);
+                  color: #FF9500;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    .timeline-summary {
+      margin-top: $ios-spacing-lg;
+      padding: $ios-spacing-md;
+      background: $ios-background-secondary;
+      border-radius: 8px;
+      
+      .summary-stats {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+        gap: $ios-spacing-md;
+        
+        .stat-item {
+          text-align: center;
+          
+          .stat-value {
+            display: block;
+            font-size: $ios-font-size-lg;
+            font-weight: $ios-font-weight-bold;
+            color: $ios-text-primary;
+            margin-bottom: 2px;
+          }
+          
+          .stat-label {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+          }
+        }
+      }
+      
+      .timeline-progress {
+        margin-top: $ios-spacing-md;
+        
+        .progress-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: $ios-spacing-xs;
+          
+          .progress-label {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+          }
+          
+          .progress-percentage {
+            font-size: $ios-font-size-xs;
+            font-weight: $ios-font-weight-medium;
+            color: $ios-text-primary;
+          }
+        }
+        
+        .progress-bar {
+          width: 100%;
+          height: 6px;
+          background: $ios-border;
+          border-radius: 3px;
+          overflow: hidden;
+          
+          .progress-fill {
+            height: 100%;
+            background: linear-gradient(90deg, #007AFF 0%, #34C759 100%);
+            transition: width 0.3s ease;
+          }
+        }
+        
+        .progress-details {
+          display: flex;
+          justify-content: space-between;
+          margin-top: $ios-spacing-xs;
+          
+          .detail-item {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-tertiary;
+            
+            .detail-value {
+              font-weight: $ios-font-weight-medium;
+              color: $ios-text-secondary;
+            }
+          }
+        }
+      }
+      
+      .estimated-completion {
+        margin-top: $ios-spacing-md;
+        padding-top: $ios-spacing-md;
+        border-top: 1px solid $ios-border;
+        text-align: center;
+        
+        .completion-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+          margin-bottom: $ios-spacing-xs;
+        }
+        
+        .completion-date {
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-primary;
+        }
+        
+        .completion-confidence {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-tertiary;
+          margin-top: 2px;
+        }
+      }
+    }
+  }
+
+  // 通用按钮样式
+  .btn-primary, .btn-success, .btn-ghost {
+    border: none;
+    border-radius: 6px;
+    font-size: $ios-font-size-xs;
+    font-weight: $ios-font-weight-medium;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    
+    &.btn-sm {
+      padding: 6px 12px;
+    }
+    
+    &.btn-xs {
+      padding: 4px 8px;
+    }
+    
+    &:disabled {
+      opacity: 0.5;
+      cursor: not-allowed;
+    }
+  }
+  
+  .btn-primary {
+    background: $ios-primary;
+    color: white;
+    
+    &:hover:not(:disabled) {
+      background: darken($ios-primary, 10%);
+    }
+  }
+  
+  .btn-success {
+    background: #34C759;
+    color: white;
+    
+    &:hover:not(:disabled) {
+      background: darken(#34C759, 10%);
+    }
+  }
+  
+  .btn-ghost {
+    background: transparent;
+    color: $ios-text-secondary;
+    border: 1px solid $ios-border;
+    
+    &:hover:not(:disabled) {
+      background: $ios-background-secondary;
+      color: $ios-text-primary;
+    }
+  }
+}
+
+// 方案调整面板
+.adjustment-panel {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 90%;
+  max-width: 600px;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
+  z-index: 1000;
+  
+  .panel-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: $ios-spacing-lg;
+    border-bottom: 1px solid $ios-border;
+    
+    h4 {
+      margin: 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+    
+    .close-btn {
+      width: 32px;
+      height: 32px;
+      border: none;
+      background: $ios-background-secondary;
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      
+      &:hover {
+        background: darken($ios-background-secondary, 5%);
+      }
+    }
+  }
+  
+  .panel-content {
+    padding: $ios-spacing-lg;
+    max-height: 60vh;
+    overflow-y: auto;
+    
+    .adjustment-form {
+      .form-group {
+        margin-bottom: $ios-spacing-md;
+        
+        label {
+          display: block;
+          font-size: $ios-font-size-xs;
+          font-weight: $ios-font-weight-medium;
+          color: $ios-text-primary;
+          margin-bottom: $ios-spacing-xs;
+        }
+        
+        .form-control {
+          width: 100%;
+          padding: $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: 6px;
+          font-size: $ios-font-size-sm;
+          
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+            box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
+          }
+        }
+        
+        .form-help {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+          margin-top: $ios-spacing-xs;
+        }
+      }
+      
+      .adjustment-preview {
+        margin-top: $ios-spacing-lg;
+        padding: $ios-spacing-md;
+        background: $ios-background-secondary;
+        border-radius: 8px;
+        
+        h5 {
+          margin: 0 0 $ios-spacing-sm 0;
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+        }
+        
+        .preview-content {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+          line-height: 1.4;
+        }
+      }
+    }
+  }
+  
+  .panel-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: $ios-spacing-sm;
+    padding: $ios-spacing-lg;
+    border-top: 1px solid $ios-border;
+    
+    .btn {
+      padding: $ios-spacing-sm $ios-spacing-md;
+      border: none;
+      border-radius: 6px;
+      font-size: $ios-font-size-sm;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      
+      &.btn-cancel {
+        background: $ios-background-secondary;
+        color: $ios-text-primary;
+        
+        &:hover {
+          background: darken($ios-background-secondary, 5%);
+        }
+      }
+      
+      &.btn-save {
+        background: $ios-primary;
+        color: white;
+        
+        &:hover {
+          background: darken($ios-primary, 10%);
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .proposal-confirm-card {
+    .card-header {
+      flex-direction: column;
+      gap: $ios-spacing-sm;
+      text-align: center;
+    }
+    
+    .tab-navigation {
+      .tab-button {
+        padding: $ios-spacing-xs;
+        font-size: $ios-font-size-xs;
+        
+        svg {
+          width: 12px;
+          height: 12px;
+        }
+      }
+    }
+    
+    .overview-grid,
+    .comparison-grid,
+    .furniture-grid,
+    .annotations-grid {
+      grid-template-columns: 1fr;
+    }
+    
+    .confirmation-steps {
+      .confirmation-step {
+        flex-direction: column;
+        text-align: center;
+      }
+    }
+    
+    .timeline-container {
+      &::before {
+        left: 10px;
+      }
+      
+      .timeline-item {
+        padding-left: 30px;
+        
+        .timeline-marker {
+          left: 2px;
+        }
+      }
+    }
+    
+    .summary-grid,
+    .summary-stats {
+      grid-template-columns: repeat(2, 1fr);
+    }
+    
+    .adjustment-panel {
+      width: 90%;
+      max-width: 400px;
+    }
+  }
+}

+ 1252 - 0
src/app/shared/components/proposal-confirm-card/proposal-confirm-card.ts

@@ -0,0 +1,1252 @@
+import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+// 方案版本接口
+interface ProposalVersion {
+  id: string;
+  name: string;
+  description: string;
+  version: string;
+  mainColor: { rgb: string; colorTemp: string };
+  materialRatio: { material: string; percentage: number }[];
+  budget: number;
+  workDays: number;
+  preview?: string;
+  isSelected?: boolean;
+}
+
+// 量化方案清单接口
+interface QuantifiedProposal {
+  spaceModules: SpaceModule[];
+  colorAndTexture: ColorTextureModule;
+  structuralAdaptation: StructuralModule;
+  feasibilityVerification: FeasibilityModule;
+}
+
+// 空间功能模块接口
+interface SpaceModule {
+  id: string;
+  name: string;
+  furniture: FurnitureItem[];
+  requirements: string;
+  verified: boolean;
+}
+
+// 家具项接口
+interface FurnitureItem {
+  name: string;
+  size: string;
+  material: string;
+  color: string;
+  features: string[];
+  price?: number;
+}
+
+// 色彩质感模块接口
+interface ColorTextureModule {
+  mainColor: { rgb: string; colorTemp: string };
+  materialRatio: { material: string; percentage: number }[];
+  colorSamples: ColorSample[];
+  verified: boolean;
+}
+
+// 色彩样本接口
+interface ColorSample {
+  name: string;
+  rgb: string;
+  reference: string;
+  actual: string;
+  difference: number;
+}
+
+// 结构适配模块接口
+interface StructuralModule {
+  constraints: StructuralConstraint[];
+  adaptations: StructuralAdaptation[];
+  cadOverlay?: string;
+  verified: boolean;
+}
+
+// 结构约束接口
+interface StructuralConstraint {
+  type: 'load-bearing' | 'window' | 'door' | 'pipe';
+  position: string;
+  size: string;
+  changeable: boolean;
+}
+
+// 结构适配接口
+interface StructuralAdaptation {
+  area: string;
+  solution: string;
+  flowWidth: string;
+  compliance: boolean;
+}
+
+// 可行性模块接口
+interface FeasibilityModule {
+  structural: { status: 'verified' | 'warning' | 'error'; message: string };
+  cost: { budget: number; timeline: string; risks: string[] };
+  timeline: { totalDays: number; phases: TimelinePhase[] };
+  verified: boolean;
+}
+
+// 时间线阶段接口
+interface TimelinePhase {
+  name: string;
+  days: number;
+  dependencies: string[];
+}
+
+// 协作批注接口
+interface CollaborationAnnotation {
+  id: string;
+  x: number;
+  y: number;
+  author: string;
+  role: 'client' | 'designer' | 'manager';
+  type: 'question' | 'suggestion' | 'confirmation';
+  content: string;
+  timestamp: Date;
+  replies: AnnotationReply[];
+  status: 'open' | 'resolved';
+  position?: { x: number; y: number; element?: string };
+  resolvedAt?: Date;
+  resolvedBy?: string;
+}
+
+// 批注回复接口
+interface AnnotationReply {
+  id: string;
+  author: string;
+  role: 'client' | 'designer' | 'manager';
+  content: string;
+  timestamp: Date;
+  mentions?: string[];
+}
+
+// 确认步骤接口
+interface ConfirmationStep {
+  id: string;
+  name: string;
+  description: string;
+  status: 'pending' | 'confirmed' | 'rejected';
+  required: boolean;
+  timestamp?: Date;
+  confirmedAt?: Date;
+  confirmedBy?: string;
+  rejectedAt?: Date;
+  rejectedBy?: string;
+  rejectionReason?: string;
+}
+
+// 批注数据模型接口
+interface Annotation {
+  id: string;
+  x?: number;
+  y?: number;
+  type: 'question' | 'suggestion' | 'confirmation';
+  content: string;
+  author: string;
+  role: 'client' | 'designer' | 'manager';
+  timestamp: Date;
+  status: 'open' | 'resolved';
+  position?: { x: number; y: number; element?: string };
+  mentions?: string[];
+  replies: AnnotationReply[];
+  resolvedAt?: Date;
+  resolvedBy?: string;
+}
+
+// 用户接口
+interface User {
+  id: string;
+  name: string;
+  role: 'client' | 'designer' | 'manager';
+  avatar?: string;
+}
+
+@Component({
+  selector: 'app-proposal-confirm-card',
+  standalone: true,
+  imports: [CommonModule, FormsModule, ReactiveFormsModule],
+  templateUrl: './proposal-confirm-card.html',
+  styleUrls: ['./proposal-confirm-card.scss']
+})
+export class ProposalConfirmCardComponent implements OnInit {
+  @Input() projectId?: string;
+  @Input() proposal?: any; // 添加proposal输入属性
+  @Output() proposalConfirmed = new EventEmitter<any>();
+  @Output() progressUpdated = new EventEmitter<number>();
+  @Output() versionSelected = new EventEmitter<ProposalVersion>();
+  @Output() annotationAdded = new EventEmitter<CollaborationAnnotation>();
+  @Output() stepConfirmed = new EventEmitter<ConfirmationStep>();
+  @Output() finalConfirmed = new EventEmitter<any>();
+  @Output() allConfirmed = new EventEmitter<any>();
+  @Output() confirmationHistoryUpdated = new EventEmitter<any>();
+  @Output() confirmationReportExported = new EventEmitter<any>();
+
+  // 表单
+  annotationForm!: FormGroup;
+  adjustmentForm!: FormGroup;
+
+  // 数据
+  proposalVersions: ProposalVersion[] = [];
+  selectedVersion?: ProposalVersion;
+  quantifiedProposal?: QuantifiedProposal;
+  annotations: Annotation[] = [];
+  confirmationSteps: ConfirmationStep[] = [];
+  
+  // 状态管理
+  activeTab: 'overview' | 'versions' | 'details' | 'collaboration' | 'confirmation' = 'overview';
+  showVersionComparison = false;
+  comparisonVersions: any = null;
+  versionDifferences: { [key: string]: { field: string; valueA: any; valueB: any; difference: number; type: string } } | null = null;
+  isAdjusting = false;
+  show3DPreview = false;
+  showCADOverlay = false;
+  
+  // 确认相关状态
+  overallProgress = 0;
+  allStepsConfirmed = false;
+  allStepsConfirmedAt?: Date;
+  isFinalConfirmed = false;
+  finalConfirmedAt?: Date;
+  confirmationHistory: any[] = [];
+  
+  // 历史版本
+  versionHistory: VersionHistoryItem[] = [];
+  showVersionHistory = false;
+  
+  // 方案调整相关
+  adjustmentHistory: ProposalAdjustment[] = [];
+  canUndo = false;
+  canRedo = false;
+  undoStack: VersionHistoryItem[] = [];
+  redoStack: VersionHistoryItem[] = [];
+  
+  constructor(private fb: FormBuilder) {}
+
+  ngOnInit() {
+    this.initializeForms();
+    this.initializeProposalData();
+    this.initializeConfirmationSteps();
+  }
+
+  private initializeForms() {
+    // 初始化批注表单
+    this.annotationForm = this.fb.group({
+      type: ['question', Validators.required],
+      content: ['', [Validators.required, Validators.minLength(1)]]
+    });
+
+    // 初始化方案调整表单
+    this.adjustmentForm = this.fb.group({
+      element: ['furniture', Validators.required],
+      parameter: ['', Validators.required],
+      newValue: ['', Validators.required],
+      reason: ['', [Validators.required, Validators.minLength(5)]]
+    });
+
+    // 初始化提及用户列表
+    this.mentionUsers = [
+      { id: '1', name: '张设计师', role: 'designer' },
+      { id: '2', name: '李经理', role: 'manager' },
+      { id: '3', name: '王客户', role: 'client' }
+    ];
+  }
+
+  private initializeProposalData() {
+     // 初始化方案版本数据
+     this.proposalVersions = [
+       {
+         id: 'version-a',
+         name: '版本A(暖木色主导)',
+         description: '以实木材质为主,营造温馨家庭氛围',
+         version: '1.0',
+         mainColor: { rgb: '255,230,180', colorTemp: '2800K' },
+         materialRatio: [
+           { material: '实木', percentage: 40 },
+           { material: '布艺', percentage: 35 },
+           { material: '金属', percentage: 25 }
+         ],
+         budget: 18000,
+         workDays: 25,
+         isSelected: true
+       },
+       {
+         id: 'version-b',
+         name: '版本B(浅米色主导)',
+         description: '以布艺材质为主,打造舒适休闲空间',
+         version: '1.1',
+         mainColor: { rgb: '240,235,220', colorTemp: '3200K' },
+         materialRatio: [
+           { material: '布艺', percentage: 60 },
+           { material: '实木', percentage: 25 },
+           { material: '金属', percentage: 15 }
+         ],
+         budget: 16000,
+         workDays: 20
+       }
+     ];
+
+    this.selectedVersion = this.proposalVersions[0];
+    this.generateQuantifiedProposal();
+  }
+
+  private initializeConfirmationSteps() {
+    this.confirmationSteps = [
+      {
+        id: 'space-modules',
+        name: '空间功能模块',
+        description: '确认各空间的功能布局和家具配置',
+        status: 'pending',
+        required: true
+      },
+      {
+        id: 'color-texture',
+        name: '色彩与质感',
+        description: '确认整体色调、材质比例和色彩样本',
+        status: 'pending',
+        required: true
+      },
+      {
+        id: 'structural-adaptation',
+        name: '结构适配',
+        description: '确认方案与现有结构的适配性',
+        status: 'pending',
+        required: true
+      },
+      {
+        id: 'budget-timeline',
+        name: '预算与工期',
+        description: '确认项目预算和时间安排',
+        status: 'pending',
+        required: true
+      }
+    ];
+  }
+
+  private generateQuantifiedProposal() {
+    if (!this.selectedVersion) return;
+
+    this.quantifiedProposal = {
+      spaceModules: [
+        {
+          id: 'living-room',
+          name: '客厅亲子互动区',
+          furniture: [
+            {
+              name: 'L型布艺沙发',
+              size: '2.8m×1.8m',
+              material: '棉麻',
+              color: 'RGB 255,230,180',
+              features: ['防污处理', '圆角设计', '可拆洗'],
+              price: 4500
+            },
+            {
+              name: '圆形实木茶几',
+              size: '直径80cm',
+              material: '橡木',
+              color: '原木色',
+              features: ['圆角3cm', '承重≥50kg', '环保漆面'],
+              price: 1200
+            }
+          ],
+          requirements: '符合亲子家庭(孩子3-6岁)的安全+温馨需求',
+          verified: true
+        }
+      ],
+      colorAndTexture: {
+        mainColor: this.selectedVersion.mainColor,
+        materialRatio: this.selectedVersion.materialRatio,
+        colorSamples: [
+          {
+            name: '主色调',
+            rgb: '255,230,180',
+            reference: '参考色',
+            actual: '实际方案色',
+            difference: 3
+          }
+        ],
+        verified: true
+      },
+      structuralAdaptation: {
+        constraints: [
+          {
+            type: 'load-bearing',
+            position: '客厅东侧距墙1.2m处',
+            size: '40cm×40cm',
+            changeable: false
+          }
+        ],
+        adaptations: [
+          {
+            area: '客厅',
+            solution: '方案避开承重柱,保持人流动线',
+            flowWidth: '1.1m',
+            compliance: true
+          }
+        ],
+        verified: true
+      },
+      feasibilityVerification: {
+        structural: {
+          status: 'verified',
+          message: '所有家具尺寸均适配空间,无遮挡问题'
+        },
+        cost: {
+          budget: this.selectedVersion.budget,
+          timeline: '采购周期7-10天',
+          risks: ['实木茶几需提前15天下单']
+        },
+        timeline: {
+          totalDays: this.selectedVersion.workDays,
+          phases: [
+            { name: '材料采购', days: 10, dependencies: [] },
+            { name: '施工安装', days: 12, dependencies: ['材料采购'] },
+            { name: '验收整理', days: 3, dependencies: ['施工安装'] }
+          ]
+        },
+        verified: true
+      }
+    };
+  }
+
+  // 版本选择和对比
+  selectVersion(version: ProposalVersion) {
+    this.selectedVersion = version;
+    this.proposalVersions.forEach(v => v.isSelected = v.id === version.id);
+    this.generateQuantifiedProposal();
+    this.versionSelected.emit(version);
+  }
+
+  toggleVersionComparison() {
+    this.showVersionComparison = !this.showVersionComparison;
+    if (this.showVersionComparison && this.proposalVersions.length >= 2) {
+      this.compareVersions(this.proposalVersions[0].id, this.proposalVersions[1].id);
+    }
+  }
+
+  // 协作批注功能
+  addAnnotation(event?: Event): void {
+    if (this.annotationForm.valid) {
+      const formValue = this.annotationForm.value;
+      const content = formValue.content;
+      
+      // 检测@提及
+      const mentions = this.extractMentions(content);
+      
+      const annotation: Annotation = {
+        id: this.generateId(),
+        type: formValue.type,
+        content: content,
+        author: '当前用户',
+        role: 'client',
+        timestamp: new Date(),
+        status: 'open',
+        mentions: mentions,
+        replies: [],
+        x: 0,
+        y: 0
+      };
+  
+      // 如果是点击事件,记录位置
+      if (event && event.target) {
+        const rect = (event.target as HTMLElement).getBoundingClientRect();
+        annotation.x = rect.left;
+        annotation.y = rect.top;
+        annotation.position = {
+          x: rect.left,
+          y: rect.top,
+          element: (event.target as HTMLElement).tagName
+        };
+      }
+  
+      this.annotations.unshift(annotation);
+      this.annotationForm.reset({ type: 'question', content: '' });
+  
+      // 发送提及通知
+      if (mentions.length > 0) {
+        this.sendMentionNotifications(mentions, annotation);
+      }
+    }
+  }
+
+  replyToAnnotation(annotationId: string, content: string, inputElement?: HTMLInputElement): void {
+    const annotation = this.annotations.find(a => a.id === annotationId);
+    if (annotation && content.trim()) {
+      const mentions = this.extractMentions(content);
+  
+      const reply: AnnotationReply = {
+        id: this.generateId(),
+        author: '当前用户',
+        role: 'designer',
+        content: content.trim(),
+        timestamp: new Date(),
+        mentions: mentions
+      };
+  
+      annotation.replies.push(reply);
+      this.sendMentionNotifications(mentions, annotation, reply);
+      
+      // 清空输入框
+      if (inputElement) {
+        inputElement.value = '';
+      }
+    }
+  }
+
+  resolveAnnotation(annotationId: string): void {
+    const annotation = this.annotations.find(a => a.id === annotationId);
+    if (annotation) {
+      annotation.status = 'resolved';
+      annotation.resolvedAt = new Date();
+      annotation.resolvedBy = '当前用户';
+    }
+  }
+
+  // 方案调整功能
+  adjustProposal(): void {
+    if (!this.adjustmentForm.valid) return;
+
+    const formValue = this.adjustmentForm.value;
+    const adjustment: ProposalAdjustment = {
+      id: this.generateId(),
+      element: formValue.element,
+      parameter: formValue.parameter,
+      oldValue: this.getCurrentParameterValue(formValue.element, formValue.parameter),
+      newValue: formValue.newValue,
+      reason: formValue.reason,
+      timestamp: new Date(),
+      operator: '当前用户'
+    };
+
+    // 保存当前状态到历史
+    this.saveVersionHistory(adjustment);
+    
+    // 应用调整
+    this.applyAdjustment(adjustment);
+    
+    // 记录调整历史
+    this.adjustmentHistory.push(adjustment);
+    
+    // 更新撤销重做状态
+    this.updateUndoRedoState();
+    
+    this.adjustmentForm.reset();
+    this.isAdjusting = false;
+  }
+
+  private getCurrentParameterValue(element: string, parameter: string): string {
+    // 根据元素类型和参数获取当前值
+    if (!this.selectedVersion || !this.quantifiedProposal) return '';
+    
+    switch (element) {
+      case 'furniture':
+        // 从家具配置中获取参数值
+        const furniture = this.quantifiedProposal.spaceModules[0]?.furniture.find(f => f.name.includes(parameter));
+        return furniture ? JSON.stringify(furniture) : '';
+      case 'color':
+        return this.selectedVersion.mainColor.rgb;
+      case 'material':
+        const material = this.selectedVersion.materialRatio.find(m => m.material === parameter);
+        return material ? material.percentage.toString() : '';
+      case 'layout':
+        return '当前布局配置';
+      default:
+        return '';
+    }
+  }
+
+  private applyAdjustment(adjustment: ProposalAdjustment): void {
+    if (!this.selectedVersion || !this.quantifiedProposal) return;
+
+    // 根据调整类型更新方案数据
+    switch (adjustment.element) {
+      case 'furniture':
+        this.applyFurnitureAdjustment(adjustment);
+        break;
+      case 'color':
+        this.applyColorAdjustment(adjustment);
+        break;
+      case 'material':
+        this.applyMaterialAdjustment(adjustment);
+        break;
+      case 'layout':
+        this.applyLayoutAdjustment(adjustment);
+        break;
+    }
+    
+    // 重新生成量化方案
+    this.generateQuantifiedProposal();
+    
+    // 更新预览
+    this.updatePreview();
+  }
+
+  private applyFurnitureAdjustment(adjustment: ProposalAdjustment): void {
+    // 应用家具调整
+    if (this.quantifiedProposal?.spaceModules[0]?.furniture) {
+      const furniture = this.quantifiedProposal.spaceModules[0].furniture.find(f => 
+        f.name.includes(adjustment.parameter)
+      );
+      if (furniture) {
+        // 根据新值更新家具属性
+        try {
+          const newFurnitureData = JSON.parse(adjustment.newValue);
+          Object.assign(furniture, newFurnitureData);
+        } catch {
+          // 如果不是JSON格式,则作为简单值处理
+          furniture.name = adjustment.newValue;
+        }
+      }
+    }
+  }
+
+  private applyColorAdjustment(adjustment: ProposalAdjustment): void {
+    // 应用色彩调整
+    if (this.selectedVersion) {
+      this.selectedVersion.mainColor.rgb = adjustment.newValue;
+      if (this.quantifiedProposal?.colorAndTexture) {
+        this.quantifiedProposal.colorAndTexture.mainColor = this.selectedVersion.mainColor;
+      }
+    }
+  }
+
+  private applyMaterialAdjustment(adjustment: ProposalAdjustment): void {
+    // 应用材质调整
+    if (this.selectedVersion) {
+      const material = this.selectedVersion.materialRatio.find(m => m.material === adjustment.parameter);
+      if (material) {
+        material.percentage = parseInt(adjustment.newValue);
+      }
+    }
+  }
+
+  private applyLayoutAdjustment(adjustment: ProposalAdjustment): void {
+    // 应用布局调整
+    console.log('应用布局调整:', adjustment);
+    // 这里可以根据具体的布局调整逻辑进行实现
+  }
+
+  private saveVersionHistory(adjustment?: ProposalAdjustment): void {
+    if (this.selectedVersion && this.quantifiedProposal) {
+      const historyItem: VersionHistoryItem = {
+        id: this.generateId(),
+        timestamp: new Date(),
+        version: JSON.parse(JSON.stringify(this.selectedVersion)),
+        proposal: JSON.parse(JSON.stringify(this.quantifiedProposal)),
+        adjustment: adjustment,
+        description: adjustment ? 
+          `调整${adjustment.element}: ${adjustment.parameter} -> ${adjustment.newValue}` : 
+          '保存当前版本'
+      };
+      
+      this.versionHistory.push(historyItem);
+      this.undoStack.push(historyItem);
+      this.redoStack = []; // 清空重做栈
+      this.updateUndoRedoState();
+    }
+  }
+
+  restoreVersion(index: number): void {
+    if (this.versionHistory[index]) {
+      const historyItem = this.versionHistory[index];
+      
+      // 保存当前状态到重做栈
+      if (this.selectedVersion && this.quantifiedProposal) {
+        this.redoStack.push({
+          id: this.generateId(),
+          timestamp: new Date(),
+          version: JSON.parse(JSON.stringify(this.selectedVersion)),
+          proposal: JSON.parse(JSON.stringify(this.quantifiedProposal)),
+          description: '恢复前状态'
+        });
+      }
+      
+      this.selectedVersion = JSON.parse(JSON.stringify(historyItem.version));
+      this.quantifiedProposal = JSON.parse(JSON.stringify(historyItem.proposal));
+      
+      this.updateUndoRedoState();
+      this.updatePreview();
+    }
+  }
+
+  // 撤销功能
+  undoAdjustment(): void {
+    if (this.canUndo && this.undoStack.length > 1) {
+      // 保存当前状态到重做栈
+      const currentState: VersionHistoryItem = {
+        id: this.generateId(),
+        timestamp: new Date(),
+        version: JSON.parse(JSON.stringify(this.selectedVersion)),
+        proposal: JSON.parse(JSON.stringify(this.quantifiedProposal)),
+        description: '撤销前状态'
+      };
+      this.redoStack.push(currentState);
+      
+      // 移除当前状态
+      this.undoStack.pop();
+      
+      // 恢复到上一个状态
+      const previousState = this.undoStack[this.undoStack.length - 1];
+      this.selectedVersion = JSON.parse(JSON.stringify(previousState.version));
+      this.quantifiedProposal = JSON.parse(JSON.stringify(previousState.proposal));
+      
+      this.updateUndoRedoState();
+      this.updatePreview();
+    }
+  }
+
+  // 重做功能
+  redoAdjustment(): void {
+    if (this.canRedo && this.redoStack.length > 0) {
+      const redoState = this.redoStack.pop()!;
+      
+      // 保存当前状态到撤销栈
+      this.undoStack.push({
+        id: this.generateId(),
+        timestamp: new Date(),
+        version: JSON.parse(JSON.stringify(this.selectedVersion)),
+        proposal: JSON.parse(JSON.stringify(this.quantifiedProposal)),
+        description: '重做前状态'
+      });
+      
+      this.selectedVersion = JSON.parse(JSON.stringify(redoState.version));
+      this.quantifiedProposal = JSON.parse(JSON.stringify(redoState.proposal));
+      
+      this.updateUndoRedoState();
+      this.updatePreview();
+    }
+  }
+
+  private updateUndoRedoState(): void {
+    this.canUndo = this.undoStack.length > 1;
+    this.canRedo = this.redoStack.length > 0;
+  }
+
+  // 快速调整方法
+  quickAdjustColor(newColor: string): void {
+    const adjustment: ProposalAdjustment = {
+      id: this.generateId(),
+      element: 'color',
+      parameter: 'mainColor',
+      oldValue: this.selectedVersion?.mainColor.rgb || '',
+      newValue: newColor,
+      reason: '快速色彩调整',
+      timestamp: new Date(),
+      operator: '当前用户'
+    };
+    
+    this.saveVersionHistory(adjustment);
+    this.applyAdjustment(adjustment);
+    this.adjustmentHistory.push(adjustment);
+    this.updateUndoRedoState();
+  }
+
+  quickAdjustMaterial(material: string, percentage: number): void {
+    const adjustment: ProposalAdjustment = {
+      id: this.generateId(),
+      element: 'material',
+      parameter: material,
+      oldValue: this.getCurrentParameterValue('material', material),
+      newValue: percentage.toString(),
+      reason: '快速材质比例调整',
+      timestamp: new Date(),
+      operator: '当前用户'
+    };
+    
+    this.saveVersionHistory(adjustment);
+    this.applyAdjustment(adjustment);
+    this.adjustmentHistory.push(adjustment);
+    this.updateUndoRedoState();
+  }
+
+  // 切换调整面板
+  toggleAdjustmentPanel(): void {
+    this.isAdjusting = !this.isAdjusting;
+    if (this.isAdjusting) {
+      this.adjustmentForm.reset({
+        element: 'furniture',
+        parameter: '',
+        newValue: '',
+        reason: ''
+      });
+    }
+  }
+
+  // 切换版本历史面板
+  toggleVersionHistory(): void {
+    this.showVersionHistory = !this.showVersionHistory;
+  }
+
+  // 清空调整历史
+  clearAdjustmentHistory(): void {
+    this.adjustmentHistory = [];
+    this.versionHistory = [];
+    this.undoStack = [];
+    this.redoStack = [];
+    this.updateUndoRedoState();
+  }
+
+  // 工具方法
+  generateId(): string {
+    return Math.random().toString(36).substr(2, 9);
+  }
+
+  getRoleClass(role: string): string {
+    switch (role) {
+      case 'client': return 'role-client';
+      case 'designer': return 'role-designer';
+      case 'manager': return 'role-manager';
+      case 'customer-service': return 'role-customer-service';
+      default: return 'role-default';
+    }
+  }
+
+  switchTab(tab: 'overview' | 'versions' | 'details' | 'collaboration' | 'confirmation') {
+    this.activeTab = tab;
+  }
+
+  toggle3DPreview() {
+    this.show3DPreview = !this.show3DPreview;
+    if (this.show3DPreview) {
+      this.initialize3DPreview();
+    }
+  }
+
+  toggleCADOverlay() {
+    this.showCADOverlay = !this.showCADOverlay;
+    if (this.showCADOverlay) {
+      this.loadCADOverlay();
+    }
+  }
+
+  // 3D预览功能
+  private initialize3DPreview() {
+    // 初始化3D预览组件
+    // 这里可以集成Three.js或其他3D库
+    console.log('初始化3D预览');
+  }
+
+  // CAD叠加功能
+  private loadCADOverlay() {
+    // 加载CAD图层
+    console.log('加载CAD叠加图层');
+  }
+
+  // 色彩对比功能
+  adjustColorSample(sampleName: string, newRgb: string) {
+    if (this.quantifiedProposal?.colorAndTexture.colorSamples) {
+      const sample = this.quantifiedProposal.colorAndTexture.colorSamples.find(s => s.name === sampleName);
+      if (sample) {
+        sample.actual = newRgb;
+        sample.difference = this.calculateColorDifference(sample.rgb, newRgb);
+      }
+    }
+  }
+
+  private calculateColorDifference(rgb1: string, rgb2: string): number {
+    // 简化的色彩差异计算
+    const [r1, g1, b1] = rgb1.split(',').map(Number);
+    const [r2, g2, b2] = rgb2.split(',').map(Number);
+    
+    const deltaR = r1 - r2;
+    const deltaG = g1 - g2;
+    const deltaB = b1 - b2;
+    
+    const distance = Math.sqrt(deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
+    return Math.round((distance / 441.67) * 100); // 归一化到百分比
+  }
+
+  // 偏好标记功能
+  markPreference(elementType: string, elementId: string, preference: 'like' | 'dislike') {
+    // 记录用户偏好
+    console.log(`标记偏好: ${elementType} ${elementId} - ${preference}`);
+    // 这里可以保存到后端或本地存储
+  }
+
+  // 实时预览更新
+  updatePreview() {
+    if (this.show3DPreview) {
+      this.initialize3DPreview();
+    }
+    if (this.showCADOverlay) {
+      this.loadCADOverlay();
+    }
+  }
+
+  // 版本对比功能
+  compareVersions(versionA: string, versionB: string): void {
+    this.showVersionComparison = true;
+    this.comparisonVersions = {
+      versionA: this.proposalVersions.find(v => v.id === versionA)!,
+      versionB: this.proposalVersions.find(v => v.id === versionB)!
+    };
+    this.generateVersionDifferences();
+  }
+
+  generateVersionDifferences(): void {
+    if (!this.comparisonVersions) return;
+    
+    const { versionA, versionB } = this.comparisonVersions;
+    this.versionDifferences = {
+      budget: {
+        field: '预算',
+        valueA: `¥${versionA.budget}`,
+        valueB: `¥${versionB.budget}`,
+        difference: versionB.budget - versionA.budget,
+        type: versionB.budget > versionA.budget ? 'increase' : 'decrease'
+      },
+      workDays: {
+        field: '工期',
+        valueA: `${versionA.workDays}天`,
+        valueB: `${versionB.workDays}天`,
+        difference: versionB.workDays - versionA.workDays,
+        type: versionB.workDays > versionA.workDays ? 'increase' : 'decrease'
+      },
+      mainColor: {
+        field: '主色调',
+        valueA: versionA.mainColor,
+        valueB: versionB.mainColor,
+        difference: 0,
+        type: versionA.mainColor === versionB.mainColor ? 'same' : 'different'
+      },
+      style: {
+        field: '风格',
+        valueA: versionA.description,
+        valueB: versionB.description,
+        difference: 0,
+        type: versionA.description === versionB.description ? 'same' : 'different'
+      }
+    };
+  }
+
+  highlightDifferences(field: string): void {
+    // 高亮显示差异项
+    const element = document.querySelector(`[data-field="${field}"]`);
+    if (element) {
+      element.classList.add('highlight-difference');
+      setTimeout(() => {
+        element.classList.remove('highlight-difference');
+      }, 2000);
+    }
+  }
+
+  // 方案确认与闭环功能
+  confirmStep(stepId: string): void {
+    const step = this.confirmationSteps.find(s => s.id === stepId);
+    if (step) {
+      step.status = 'confirmed';
+      step.confirmedAt = new Date();
+      step.confirmedBy = '当前用户'; // 实际应用中从用户服务获取
+      
+      // 记录电子留痕
+      this.recordConfirmationTrace(stepId, 'confirmed');
+      
+      // 更新整体进度
+      this.updateOverallProgress();
+      
+      // 检查是否所有步骤都已确认
+      this.checkAllStepsConfirmed();
+    }
+  }
+
+  rejectStep(stepId: string, reason: string): void {
+    const step = this.confirmationSteps.find(s => s.id === stepId);
+    if (step) {
+      step.status = 'rejected';
+      step.rejectedAt = new Date();
+      step.rejectedBy = '当前用户';
+      step.rejectionReason = reason;
+      
+      // 记录电子留痕
+      this.recordConfirmationTrace(stepId, 'rejected', reason);
+      
+      // 更新整体进度
+      this.updateOverallProgress();
+    }
+  }
+
+  recordConfirmationTrace(stepId: string, action: 'confirmed' | 'rejected', reason?: string): void {
+    const trace = {
+      id: this.generateId(),
+      stepId,
+      action,
+      timestamp: new Date(),
+      user: '当前用户',
+      reason: reason || '',
+      ipAddress: '127.0.0.1', // 实际应用中获取真实IP
+      userAgent: navigator.userAgent
+    };
+    
+    // 存储到确认历史中
+    if (!this.confirmationHistory) {
+      this.confirmationHistory = [];
+    }
+    this.confirmationHistory.push(trace);
+    
+    // 触发确认历史更新事件
+    this.confirmationHistoryUpdated.emit(trace);
+  }
+
+  updateOverallProgress(): void {
+    const totalSteps = this.confirmationSteps.length;
+    const confirmedSteps = this.confirmationSteps.filter(s => s.status === 'confirmed').length;
+    this.overallProgress = Math.round((confirmedSteps / totalSteps) * 100);
+  }
+
+  checkAllStepsConfirmed(): void {
+    const allConfirmed = this.confirmationSteps.every(s => s.status === 'confirmed');
+    if (allConfirmed) {
+      this.allStepsConfirmed = true;
+      this.allStepsConfirmedAt = new Date();
+      
+      // 触发全部确认完成事件
+      this.allConfirmed.emit({
+        proposalId: this.proposal?.id,
+        confirmedAt: this.allStepsConfirmedAt,
+        confirmationHistory: this.confirmationHistory
+      });
+    }
+  }
+
+  finalConfirm(): void {
+    if (this.allStepsConfirmed) {
+      const finalConfirmation = {
+        id: this.generateId(),
+        proposalId: this.proposal?.id,
+        versionId: this.selectedVersion?.id,
+        confirmedAt: new Date(),
+        confirmedBy: '当前用户',
+        digitalSignature: this.generateDigitalSignature(),
+        confirmationSteps: this.confirmationSteps,
+        confirmationHistory: this.confirmationHistory
+      };
+      
+      // 触发最终确认事件
+      this.finalConfirmed.emit(finalConfirmation);
+      
+      // 更新组件状态
+      this.isFinalConfirmed = true;
+      this.finalConfirmedAt = new Date();
+    }
+  }
+
+  generateDigitalSignature(): string {
+    // 简化的数字签名生成(实际应用中应使用加密算法)
+    const data = {
+      proposalId: this.proposal?.id,
+      versionId: this.selectedVersion?.id,
+      timestamp: new Date().getTime(),
+      user: '当前用户'
+    };
+    
+    return btoa(JSON.stringify(data));
+  }
+
+  // 对话框状态
+  showRejectReasonDialog = false;
+  showConfirmationHistory = false;
+  rejectionReason = '';
+  currentRejectingStepId: string | null = null;
+
+  // 显示拒绝对话框
+  showRejectDialog(stepId: string): void {
+    this.currentRejectingStepId = stepId;
+    this.rejectionReason = '';
+    this.showRejectReasonDialog = true;
+  }
+
+  // 关闭拒绝对话框
+  closeRejectDialog(): void {
+    this.showRejectReasonDialog = false;
+    this.currentRejectingStepId = null;
+    this.rejectionReason = '';
+  }
+
+  // 确认拒绝
+  confirmReject(): void {
+    if (this.currentRejectingStepId && this.rejectionReason.trim()) {
+      this.rejectStep(this.currentRejectingStepId, this.rejectionReason.trim());
+      this.closeRejectDialog();
+    }
+  }
+
+  // 批注相关属性
+  mentionUsers: User[] = [];
+  showMentionSuggestions = false;
+  filteredMentionUsers: User[] = [];
+  currentMentionQuery = '';
+  mentionFilter = '';
+
+  // 提取@提及
+  extractMentions(content: string): string[] {
+    const mentionRegex = /@(\w+)/g;
+    const mentions: string[] = [];
+    let match;
+    
+    while ((match = mentionRegex.exec(content)) !== null) {
+      const username = match[1];
+      // 验证用户是否存在
+      const user = this.mentionUsers.find(u => u.name.toLowerCase() === username.toLowerCase());
+      if (user) {
+        mentions.push(user.id);
+      }
+    }
+    
+    return mentions;
+  }
+
+  // 发送提及通知
+  sendMentionNotifications(mentions: string[], annotation: Annotation, reply?: AnnotationReply): void {
+    mentions.forEach(userId => {
+      const user = this.mentionUsers.find(u => u.id === userId);
+      if (user) {
+        console.log(`发送提及通知给 ${user.name}`);
+        // 这里可以集成实际的通知系统
+      }
+    });
+  }
+
+  // 内容输入处理
+  onContentInput(event: Event): void {
+    const target = event.target as HTMLTextAreaElement;
+    const content = target.value;
+    const cursorPosition = target.selectionStart;
+    
+    // 检查是否在输入@提及
+    const beforeCursor = content.substring(0, cursorPosition);
+    const mentionMatch = beforeCursor.match(/@(\w*)$/);
+    
+    if (mentionMatch) {
+      this.currentMentionQuery = mentionMatch[1];
+      this.showMentionSuggestions = true;
+      this.filteredMentionUsers = this.mentionUsers.filter(user => 
+        user.name.toLowerCase().includes(this.currentMentionQuery.toLowerCase())
+      );
+    } else {
+      this.showMentionSuggestions = false;
+    }
+  }
+
+  // 选择提及用户
+  selectMention(user: User): void {
+    const textarea = document.querySelector('textarea[formControlName="content"]') as HTMLTextAreaElement;
+    if (textarea) {
+      const content = textarea.value;
+      const cursorPosition = textarea.selectionStart;
+      const beforeCursor = content.substring(0, cursorPosition);
+      const afterCursor = content.substring(cursorPosition);
+      const newContent = beforeCursor.replace(/@\w*$/, `@${user.name} `) + afterCursor;
+      this.annotationForm.patchValue({ content: newContent });
+    }
+    this.showMentionSuggestions = false;
+  }
+
+  // 格式化包含提及的内容
+  formatContentWithMentions(content: string): string {
+    return content.replace(/@(\w+)/g, '<span class="mention">@$1</span>');
+  }
+
+  // 获取状态样式类
+  getStatusClass(status: string): string {
+    switch (status) {
+      case 'open': return 'status-open';
+      case 'resolved': return 'status-resolved';
+      case 'pending': return 'status-pending';
+      case 'confirmed': return 'status-confirmed';
+      case 'rejected': return 'status-rejected';
+      default: return '';
+    }
+  }
+
+  // 导出确认报告
+  exportConfirmationReport(): void {
+    const report = {
+      projectId: this.projectId,
+      proposalVersion: this.selectedVersion,
+      confirmationSteps: this.confirmationSteps,
+      overallProgress: this.overallProgress,
+      allStepsConfirmed: this.allStepsConfirmed,
+      allStepsConfirmedAt: this.allStepsConfirmedAt,
+      isFinalConfirmed: this.isFinalConfirmed,
+      finalConfirmedAt: this.finalConfirmedAt,
+      confirmationHistory: this.confirmationHistory,
+      annotations: this.annotations,
+      exportedAt: new Date(),
+      digitalSignature: this.generateDigitalSignature()
+    };
+
+    const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
+    const url = URL.createObjectURL(blob);
+    const a = document.createElement('a');
+    a.href = url;
+    a.download = `confirmation-report-${this.projectId}-${Date.now()}.json`;
+    a.click();
+    URL.revokeObjectURL(url);
+
+    this.confirmationReportExported.emit(report);
+  }
+
+  // 获取操作文本
+  getActionText(action: string): string {
+    switch (action) {
+      case 'confirmed': return '确认';
+      case 'rejected': return '拒绝';
+      default: return action;
+    }
+  }
+
+  // 获取进度百分比
+  getProgressPercentage(): number {
+    return Math.round(this.overallProgress);
+  }
+
+  // 获取未确认的必需项目数量
+  getRequiredUnconfirmedCount(): number {
+    if (!this.confirmationSteps) {
+      return 0;
+    }
+    return this.confirmationSteps.filter(s => s && s.required && s.status !== 'confirmed').length;
+  }
+  getRequiredCount(): number {
+    if (!this.confirmationSteps) {
+      return 0;
+    }
+    return this.confirmationSteps.filter(s => s && s.required).length;
+  }
+  getConfirmedRequiredCount(): number {
+    if (!this.confirmationSteps) return 0;
+    return this.confirmationSteps.filter(s => s && s.required && s.status === 'confirmed').length;
+  }
+
+  getSpaceModulesVerified(): boolean {
+    if (!this.quantifiedProposal?.spaceModules) return false;
+    return this.quantifiedProposal.spaceModules.every(m => m.verified);
+  }
+}
+
+// 方案调整接口
+interface ProposalAdjustment {
+  id: string;
+  element: 'furniture' | 'color' | 'material' | 'layout';
+  parameter: string;
+  oldValue: string;
+  newValue: string;
+  reason: string;
+  timestamp: Date;
+  operator: string;
+}
+
+// 版本历史接口
+interface VersionHistoryItem {
+  id: string;
+  timestamp: Date;
+  version: ProposalVersion;
+  proposal: QuantifiedProposal;
+  adjustment?: ProposalAdjustment;
+  description: string;
+}

+ 600 - 0
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.html

@@ -0,0 +1,600 @@
+<div class="info-card requirements-confirm-card">
+  <div class="card-header">
+    <h4>确认需求</h4>
+    <div class="progress-indicator">
+      <div class="progress-bar">
+        <div class="progress-fill" [style.width.%]="getProgressPercentage()"></div>
+      </div>
+      <span class="progress-text">{{ getProgressPercentage() | number:'1.0-0' }}% 完成</span>
+    </div>
+  </div>
+
+  <!-- 标签页导航 -->
+  <div class="tab-navigation">
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'materials'"
+      (click)="switchTab('materials')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+        <polyline points="14,2 14,8 20,8"></polyline>
+      </svg>
+      素材解析
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'mapping'"
+      (click)="switchTab('mapping')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <circle cx="12" cy="12" r="3"></circle>
+        <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"></path>
+      </svg>
+      需求映射
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'collaboration'"
+      (click)="switchTab('collaboration')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+        <circle cx="9" cy="7" r="4"></circle>
+        <path d="M23 21v-2a4 4 0 0 0-3-3.87m-4-12a4 4 0 0 1 0 7.75"></path>
+      </svg>
+      协作验证
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'progress'"
+      (click)="switchTab('progress')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <polyline points="22,12 18,12 15,21 9,3 6,12 2,12"></polyline>
+      </svg>
+      进度管理
+    </button>
+  </div>
+
+  <!-- 标签页内容 -->
+  <div class="tab-content">
+    
+    <!-- 素材解析标签页 -->
+    @if (activeTab === 'materials') {
+      <div class="materials-section">
+        <div class="upload-grid">
+          
+          <!-- 文本输入区域 -->
+          <div class="upload-item">
+            <h5>文本描述</h5>
+            <form [formGroup]="materialUploadForm" (ngSubmit)="onTextSubmit()">
+              <textarea 
+                formControlName="textContent" 
+                placeholder="请描述您的装修需求,如:希望温馨的暖木色调,适合亲子家庭,需要瑜伽区收纳..."
+                rows="4">
+              </textarea>
+              <button type="submit" class="btn-primary btn-sm" [disabled]="!materialUploadForm.get('textContent')?.value">
+                解析文本
+              </button>
+            </form>
+          </div>
+
+          <!-- 图片上传区域 -->
+          <div class="upload-item">
+            <h5>参考图片</h5>
+            <div class="file-upload-zone" (click)="imageInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
+                <circle cx="8.5" cy="8.5" r="1.5"></circle>
+                <polyline points="21,15 16,10 5,21"></polyline>
+              </svg>
+              <p>点击上传参考图片</p>
+              <span class="hint">支持多张图片,自动分析色调和材质</span>
+            </div>
+            <input #imageInput type="file" multiple accept="image/*" (change)="onFileSelected($event, 'image')" style="display: none;">
+          </div>
+
+          <!-- CAD上传区域 -->
+          <div class="upload-item">
+            <h5>CAD图纸</h5>
+            <div class="file-upload-zone" (click)="cadInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+                <polyline points="14,2 14,8 20,8"></polyline>
+                <line x1="16" y1="13" x2="8" y2="13"></line>
+                <line x1="16" y1="17" x2="8" y2="17"></line>
+              </svg>
+              <p>点击上传CAD图纸</p>
+              <span class="hint">自动识别结构和空间尺寸</span>
+            </div>
+            <input #cadInput type="file" accept=".dwg,.dxf,.pdf" (change)="onFileSelected($event, 'cad')" style="display: none;">
+          </div>
+        </div>
+
+        <!-- 已上传素材列表 -->
+        @if (materials.length > 0) {
+          <div class="materials-list">
+            <h5>已上传素材</h5>
+            <div class="material-cards">
+              @for (material of materials; track material.id) {
+                <div class="material-card" [class]="'material-' + material.type">
+                  <div class="material-header">
+                    <span class="material-type">{{ material.type === 'text' ? '文本' : material.type === 'image' ? '图片' : 'CAD' }}</span>
+                    <button class="btn-ghost btn-xs" (click)="removeMaterial(material.id)">
+                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                        <line x1="18" y1="6" x2="6" y2="18"></line>
+                        <line x1="6" y1="6" x2="18" y2="18"></line>
+                      </svg>
+                    </button>
+                  </div>
+                  <div class="material-name">{{ material.name }}</div>
+                  @if (material.analysis) {
+                    <div class="parsed-info">
+                      @if (material.type === 'text') {
+                        <div class="parsed-tags">
+                          @if (material.analysis.atmosphere) {
+                          <div class="tag-group">
+                            <span class="tag-label">氛围:</span>
+                            <span class="tag">{{ material.analysis.atmosphere.description }}</span>
+                          </div>
+                        }
+                        </div>
+                      } @else if (material.type === 'image') {
+                        <div class="color-info">
+                          <span class="color-temp">色温: {{ material.analysis.colorTemperature }}K</span>
+                        </div>
+                      }
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    }
+
+    <!-- 需求映射标签页 -->
+    @if (activeTab === 'mapping') {
+      <div class="mapping-section">
+        
+        <!-- 一致性警告 -->
+        @if (showConsistencyWarning) {
+          <div class="consistency-warning">
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
+              <line x1="12" y1="9" x2="12" y2="13"></line>
+              <line x1="12" y1="17" x2="12.01" y2="17"></line>
+            </svg>
+            {{ warningMessage }}
+          </div>
+        }
+
+        <div class="indicator-grid">
+          
+          <!-- 色彩指标 -->
+          <div class="indicator-group">
+            <h5>色彩氛围</h5>
+            
+            <!-- RGB滑块 -->
+            <div class="indicator-item">
+              <label>主色调 RGB</label>
+              <div class="rgb-controls">
+                <div class="rgb-slider">
+                  <span>R</span>
+                  <input 
+                    type="range" 
+                    [min]="indicatorRanges.color.r.min" 
+                    [max]="indicatorRanges.color.r.max" 
+                    [value]="colorIndicators.mainColor.r"
+                    (input)="colorIndicators.mainColor.r = +$event.target.value; updateColorIndicator('mainColor', colorIndicators.mainColor)">
+                  <span>{{ colorIndicators.mainColor.r }}</span>
+                </div>
+                <div class="rgb-slider">
+                  <span>G</span>
+                  <input 
+                    type="range" 
+                    [min]="indicatorRanges.color.g.min" 
+                    [max]="indicatorRanges.color.g.max" 
+                    [value]="colorIndicators.mainColor.g"
+                    (input)="colorIndicators.mainColor.g = +$event.target.value; updateColorIndicator('mainColor', colorIndicators.mainColor)">
+                  <span>{{ colorIndicators.mainColor.g }}</span>
+                </div>
+                <div class="rgb-slider">
+                  <span>B</span>
+                  <input 
+                    type="range" 
+                    [min]="indicatorRanges.color.b.min" 
+                    [max]="indicatorRanges.color.b.max" 
+                    [value]="colorIndicators.mainColor.b"
+                    (input)="colorIndicators.mainColor.b = +$event.target.value; updateColorIndicator('mainColor', colorIndicators.mainColor)">
+                  <span>{{ colorIndicators.mainColor.b }}</span>
+                </div>
+                <div class="color-preview" [style.background]="getRgbString()"></div>
+              </div>
+            </div>
+            
+            <!-- 色温滑块 -->
+            <div class="indicator-item">
+              <label>色温 ({{ getColorTemperatureDescription() }})</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.color.temperature.min" 
+                  [max]="indicatorRanges.color.temperature.max" 
+                  [value]="colorIndicators.colorTemperature"
+                  (input)="colorIndicators.colorTemperature = +$event.target.value; updateColorIndicator('colorTemperature')">
+                <span class="slider-value">{{ colorIndicators.colorTemperature }}K</span>
+              </div>
+            </div>
+
+            <!-- 饱和度和亮度 -->
+            <div class="indicator-item">
+              <label>饱和度</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.color.saturation.min" 
+                  [max]="indicatorRanges.color.saturation.max" 
+                  [value]="colorIndicators.saturation"
+                  (input)="colorIndicators.saturation = +$event.target.value; updateColorIndicator('saturation')">
+                <span class="slider-value">{{ colorIndicators.saturation }}%</span>
+              </div>
+            </div>
+          </div>
+
+          <!-- 空间指标 -->
+          <div class="indicator-group">
+            <h5>空间结构</h5>
+            <div class="indicator-item">
+              <label>留白占比</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.space.blankRatio.min" 
+                  [max]="indicatorRanges.space.blankRatio.max" 
+                  [value]="spaceIndicators.blankRatio"
+                  (input)="updateSpaceIndicator('blankRatio', +$event.target.value)">
+                <span class="slider-value">{{ spaceIndicators.blankRatio }}%</span>
+              </div>
+            </div>
+            
+            <div class="indicator-item">
+              <label>直线条占比</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.space.lineRatio.min" 
+                  [max]="indicatorRanges.space.lineRatio.max" 
+                  [value]="spaceIndicators.lineRatio"
+                  (input)="updateSpaceIndicator('lineRatio', +$event.target.value)">
+                <span class="slider-value">{{ spaceIndicators.lineRatio }}%</span>
+              </div>
+            </div>
+
+            <div class="indicator-item">
+              <label>动线宽度</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.space.flowWidth.min" 
+                  [max]="indicatorRanges.space.flowWidth.max" 
+                  step="0.1"
+                  [value]="spaceIndicators.flowWidth"
+                  (input)="updateSpaceIndicator('flowWidth', +$event.target.value)">
+                <span class="slider-value">{{ spaceIndicators.flowWidth }}m</span>
+              </div>
+            </div>
+          </div>
+
+          <!-- 材质权重 -->
+          <div class="indicator-group">
+            <h5>材质权重</h5>
+            <div class="indicator-item">
+              <label>布艺占比</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.material.fabricRatio.min" 
+                  [max]="indicatorRanges.material.fabricRatio.max" 
+                  [value]="materialIndicators.fabricRatio"
+                  (input)="updateMaterialIndicator('fabricRatio', +$event.target.value)">
+                <span class="slider-value">{{ materialIndicators.fabricRatio }}%</span>
+              </div>
+            </div>
+
+            <div class="indicator-item">
+              <label>木质占比</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.material.woodRatio.min" 
+                  [max]="indicatorRanges.material.woodRatio.max" 
+                  [value]="materialIndicators.woodRatio"
+                  (input)="updateMaterialIndicator('woodRatio', +$event.target.value)">
+                <span class="slider-value">{{ materialIndicators.woodRatio }}%</span>
+              </div>
+            </div>
+
+            <div class="indicator-item">
+              <label>金属占比</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.material.metalRatio.min" 
+                  [max]="indicatorRanges.material.metalRatio.max" 
+                  [value]="materialIndicators.metalRatio"
+                  (input)="updateMaterialIndicator('metalRatio', +$event.target.value)">
+                <span class="slider-value">{{ materialIndicators.metalRatio }}%</span>
+              </div>
+            </div>
+
+            <div class="indicator-item">
+              <label>光滑度</label>
+              <div class="slider-container">
+                <input 
+                  type="range" 
+                  [min]="indicatorRanges.material.smoothness.min" 
+                  [max]="indicatorRanges.material.smoothness.max" 
+                  [value]="materialIndicators.smoothness"
+                  (input)="updateMaterialIndicator('smoothness', +$event.target.value)">
+                <span class="slider-value">{{ materialIndicators.smoothness }}/10</span>
+              </div>
+            </div>
+          </div>
+
+          <!-- 预设氛围 -->
+          <div class="indicator-group">
+            <h5>预设氛围</h5>
+            <div class="atmosphere-presets">
+              @for (preset of presetAtmospheres; track preset.name) {
+                <div class="preset-card" (click)="applyPresetAtmosphere(preset)">
+                  <div class="preset-color" [style.background]="'rgb(' + preset.rgb + ')'"></div>
+                  <div class="preset-info">
+                    <div class="preset-name">{{ preset.name }}</div>
+                    <div class="preset-details">{{ preset.colorTemp }}</div>
+                    <div class="preset-materials">
+                      @for (material of preset.materials; track material) {
+                        <span class="material-tag">{{ material }}</span>
+                      }
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+
+    <!-- 协作验证标签页 -->
+    @if (activeTab === 'collaboration') {
+      <div class="collaboration-section">
+        <!-- 一致性预警 -->
+        @if (consistencyWarnings.length > 0) {
+          <div class="consistency-warning">
+            <svg viewBox="0 0 24 24" fill="currentColor">
+              <path d="M12 2L1 21h22L12 2zm0 3.99L19.53 19H4.47L12 5.99zM11 16h2v2h-2v-2zm0-6h2v4h-2v-4z"/>
+            </svg>
+            <div>
+              <strong>一致性预警:</strong>
+              @for (warning of consistencyWarnings; track warning) {
+                <div>{{ warning }}</div>
+              }
+            </div>
+          </div>
+        }
+
+        <!-- 需求列表 -->
+        <div class="requirements-list">
+          <div class="list-header">
+            <h5>需求项目</h5>
+            <div class="list-controls">
+              <button class="btn-ghost btn-sm" (click)="sortRequirementsByPriority()">按优先级排序</button>
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存状态</button>
+            </div>
+          </div>
+
+          @for (requirement of requirementItems; track requirement.id) {
+            <div class="requirement-item" [class]="getStatusClass(requirement.status)">
+              <div class="requirement-header">
+                <div class="requirement-info">
+                  <h6>{{ requirement.title }}</h6>
+                  <p>{{ requirement.description }}</p>
+                  <div class="requirement-tags">
+                    @for (tag of requirement.tags; track tag) {
+                      <span class="tag">{{ tag }}</span>
+                    }
+                  </div>
+                </div>
+                <div class="requirement-meta">
+                  <select class="priority-select" 
+                          [value]="requirement.priority" 
+                          (change)="onPriorityChange(requirement.id, $event)">
+                    <option value="high">高优先级</option>
+                    <option value="medium">中优先级</option>
+                    <option value="low">低优先级</option>
+                  </select>
+                  <span class="status-badge" [class]="getStatusClass(requirement.status)">
+                    {{ requirement.status === 'confirmed' ? '已确认' : requirement.status === 'rejected' ? '已拒绝' : '待确认' }}
+                  </span>
+                </div>
+              </div>
+              
+              <div class="requirement-actions">
+                @if (requirement.status === 'pending') {
+                  <button class="btn-success btn-sm" (click)="confirmRequirement(requirement.id)">确认</button>
+                  <button class="btn-danger btn-sm" (click)="rejectRequirement(requirement.id, '需要进一步讨论')">拒绝</button>
+                }
+                <button class="btn-ghost btn-sm" (click)="requirement.showComments = !requirement.showComments">
+                  评论 ({{ getCommentsForRequirement(requirement.id).length }})
+                  @if (hasUnreadComments(requirement.id)) {
+                    <span class="unread-indicator"></span>
+                  }
+                </button>
+              </div>
+
+              @if (requirement.showComments) {
+                <div class="comments-section">
+                  @if (getCommentsForRequirement(requirement.id).length > 0) {
+                    <div class="comments-list">
+                      @for (comment of getCommentsForRequirement(requirement.id); track comment.id) {
+                        <div class="comment-item" [class.resolved]="comment.status === 'resolved'">
+                          <div class="comment-header">
+                            <span class="comment-author">{{ comment.author }}</span>
+                            <span class="comment-role">{{ comment.role === 'designer' ? '设计师' : comment.role === 'customer-service' ? '客服' : '客户' }}</span>
+                            <span class="comment-time">{{ comment.timestamp | date:'MM-dd HH:mm' }}</span>
+                            @if (comment.status === 'pending') {
+                              <button class="btn-ghost btn-xs" (click)="resolveComment(comment.id)">标记已解决</button>
+                            }
+                          </div>
+                          <div class="comment-content">{{ comment.content }}</div>
+                        </div>
+                      }
+                    </div>
+                  }
+                  <div class="add-comment">
+                    <textarea #commentInput placeholder="添加评论..." rows="2"></textarea>
+                    <button class="btn-primary btn-sm" (click)="addCommentToRequirement(requirement.id, commentInput.value); commentInput.value = ''">
+                      发送
+                    </button>
+                  </div>
+                </div>
+              }
+            </div>
+          }
+        </div>
+      </div>
+    }
+
+    <!-- 进度管理标签页 -->
+    @if (activeTab === 'progress') {
+      <div class="progress-section">
+        <!-- 进度概览 -->
+        <div class="progress-overview">
+          <div class="progress-stats">
+            <div class="stat-item">
+                <div class="stat-number">{{ getRequirementCountByStatus('confirmed') }}</div>
+                <div class="stat-label">已确认</div>
+              </div>
+              <div class="stat-item">
+                <div class="stat-number">{{ getRequirementCountByStatus('pending') }}</div>
+                <div class="stat-label">待确认</div>
+              </div>
+              <div class="stat-item">
+                <div class="stat-number">{{ getRequirementCountByStatus('rejected') }}</div>
+                <div class="stat-label">已拒绝</div>
+              </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getUnresolvedCommentsCount() }}</div>
+              <div class="stat-label">待解决评论</div>
+            </div>
+          </div>
+          
+          <div class="progress-chart">
+            <div class="chart-container">
+              <div class="progress-circle">
+                <svg width="120" height="120" viewBox="0 0 120 120">
+                  <circle cx="60" cy="60" r="50" fill="none" stroke="#e5e5ea" stroke-width="8"></circle>
+                  <circle 
+                    cx="60" 
+                    cy="60" 
+                    r="50" 
+                    fill="none" 
+                    stroke="#34C759" 
+                    stroke-width="8"
+                    stroke-linecap="round"
+                    [style.stroke-dasharray]="314"
+                    [style.stroke-dashoffset]="314 - (314 * getProgressPercentage() / 100)"
+                    transform="rotate(-90 60 60)">
+                  </circle>
+                </svg>
+                <div class="progress-text">
+                  <div class="progress-percentage">{{ getProgressPercentage() }}%</div>
+                  <div class="progress-label">完成度</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 历史状态 -->
+        <div class="history-section">
+          <div class="section-header">
+            <h5>历史状态</h5>
+            <div class="history-controls">
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+              <select class="history-select" (change)="onHistoryStateChange($event)">
+                <option value="-1">选择历史状态</option>
+                @for (state of historyStates; track $index) {
+                  <option [value]="$index">{{ state.timestamp | date:'MM-dd HH:mm' }} - {{ state.author }}</option>
+                }
+              </select>
+            </div>
+          </div>
+
+          @if (historyStates.length > 0) {
+            <div class="history-timeline">
+              @for (state of historyStates; track $index) {
+                <div class="timeline-item" (click)="restoreHistoryState($index)">
+                  <div class="timeline-marker"></div>
+                  <div class="timeline-content">
+                    <div class="timeline-header">
+                      <span class="timeline-time">{{ state.timestamp | date:'MM-dd HH:mm' }}</span>
+                      <span class="timeline-author">{{ state.author }}</span>
+                    </div>
+                    <div class="timeline-summary">
+                      状态快照:{{ state.requirementItems.length }}个需求项,
+                      {{ getRequirementCountByStatus('confirmed') }}个已确认
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-history">
+              <p>暂无历史状态记录</p>
+              <button class="btn-primary btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+            </div>
+          }
+        </div>
+
+        <!-- 需求状态分布 -->
+        <div class="status-distribution">
+          <h5>需求状态分布</h5>
+          <div class="status-bars">
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('confirmed') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar confirmed" 
+                     [style.width.%]="getStatusPercentage('confirmed')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">待确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('pending') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar pending" 
+                     [style.width.%]="getStatusPercentage('pending')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已拒绝</span>
+                <span class="status-count">{{ getRequirementCountByStatus('rejected') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar rejected" 
+                     [style.width.%]="getStatusPercentage('rejected')"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+  </div>
+</div>

+ 1063 - 0
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.scss

@@ -0,0 +1,1063 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { 
+  display: block; 
+  height: 100%; 
+}
+
+.requirements-confirm-card {
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: $ios-spacing-md;
+    
+    h4 {
+      margin: 0;
+      font-size: $ios-font-size-sm;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+    
+    .progress-indicator {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-xs;
+      
+      .progress-bar {
+        width: 80px;
+        height: 4px;
+        background: $ios-border;
+        border-radius: 2px;
+        overflow: hidden;
+        
+        .progress-fill {
+          height: 100%;
+          background: linear-gradient(90deg, #007AFF 0%, #34C759 100%);
+          transition: width 0.3s ease;
+        }
+      }
+      
+      .progress-text {
+        font-size: $ios-font-size-xs;
+        color: $ios-text-secondary;
+        font-weight: $ios-font-weight-medium;
+      }
+    }
+  }
+
+  // 标签页导航
+  .tab-navigation {
+    display: flex;
+    background: $ios-background-secondary;
+    border-radius: 8px;
+    padding: 2px;
+    margin-bottom: $ios-spacing-md;
+    
+    .tab-button {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: $ios-spacing-xs;
+      padding: $ios-spacing-sm $ios-spacing-xs;
+      border: none;
+      background: transparent;
+      color: $ios-text-secondary;
+      font-size: $ios-font-size-xs;
+      font-weight: $ios-font-weight-medium;
+      border-radius: 6px;
+      transition: all 0.2s ease;
+      cursor: pointer;
+      
+      svg {
+        width: 14px;
+        height: 14px;
+      }
+      
+      &:hover {
+        color: $ios-text-primary;
+        background: rgba(0, 122, 255, 0.1);
+      }
+      
+      &.active {
+        background: white;
+        color: $ios-primary;
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+      }
+    }
+  }
+
+  // 标签页内容
+  .tab-content {
+    min-height: 300px;
+  }
+
+  // 素材解析部分
+  .materials-section {
+    .upload-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-lg;
+      
+      .upload-item {
+        h5 {
+          margin: 0 0 $ios-spacing-sm 0;
+          font-size: $ios-font-size-xs;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+        }
+        
+        textarea {
+          width: 100%;
+          padding: $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: 6px;
+          font-size: $ios-font-size-xs;
+          resize: vertical;
+          margin-bottom: $ios-spacing-sm;
+          
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+            box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
+          }
+        }
+        
+        .file-upload-zone {
+          border: 2px dashed $ios-border;
+          border-radius: 8px;
+          padding: $ios-spacing-lg;
+          text-align: center;
+          cursor: pointer;
+          transition: all 0.2s ease;
+          
+          &:hover {
+            border-color: $ios-primary;
+            background: rgba(0, 122, 255, 0.02);
+          }
+          
+          svg {
+            color: $ios-text-secondary;
+            margin-bottom: $ios-spacing-sm;
+          }
+          
+          p {
+            margin: 0 0 $ios-spacing-xs 0;
+            font-size: $ios-font-size-xs;
+            color: $ios-text-primary;
+            font-weight: $ios-font-weight-medium;
+          }
+          
+          .hint {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+          }
+        }
+      }
+    }
+    
+    .materials-list {
+      h5 {
+        margin: 0 0 $ios-spacing-sm 0;
+        font-size: $ios-font-size-xs;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .material-cards {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+        gap: $ios-spacing-sm;
+        
+        .material-card {
+          border: 1px solid $ios-border;
+          border-radius: 6px;
+          padding: $ios-spacing-sm;
+          background: white;
+          
+          &.material-text {
+            border-left: 3px solid #34C759;
+          }
+          
+          &.material-image {
+            border-left: 3px solid #FF9500;
+          }
+          
+          &.material-cad {
+            border-left: 3px solid #007AFF;
+          }
+          
+          .material-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: $ios-spacing-xs;
+            
+            .material-type {
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-medium;
+              color: $ios-text-secondary;
+              text-transform: uppercase;
+            }
+          }
+          
+          .material-name {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-primary;
+            margin-bottom: $ios-spacing-xs;
+            font-weight: $ios-font-weight-medium;
+          }
+          
+          .parsed-info {
+            .parsed-tags {
+              .tag-group {
+                margin-bottom: $ios-spacing-xs;
+                
+                .tag-label {
+                  font-size: $ios-font-size-xs;
+                  color: $ios-text-secondary;
+                  margin-right: $ios-spacing-xs;
+                }
+                
+                .tag {
+                  display: inline-block;
+                  background: $ios-background-secondary;
+                  color: $ios-text-primary;
+                  padding: 2px 6px;
+                  border-radius: 4px;
+                  font-size: $ios-font-size-xs;
+                  margin-right: 4px;
+                }
+              }
+            }
+            
+            .color-info {
+              .color-temp {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 需求映射部分
+  .mapping-section {
+    .consistency-warning {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-xs;
+      padding: $ios-spacing-sm;
+      background: rgba(255, 59, 48, 0.1);
+      border: 1px solid rgba(255, 59, 48, 0.2);
+      border-radius: 6px;
+      color: #FF3B30;
+      font-size: $ios-font-size-xs;
+      margin-bottom: $ios-spacing-md;
+      
+      svg {
+        width: 16px;
+        height: 16px;
+        flex-shrink: 0;
+      }
+    }
+    
+    .indicator-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+      gap: $ios-spacing-lg;
+      
+      .indicator-group {
+        h5 {
+          margin: 0 0 $ios-spacing-sm 0;
+          font-size: $ios-font-size-xs;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+          padding-bottom: $ios-spacing-xs;
+          border-bottom: 1px solid $ios-border;
+        }
+        
+        .indicator-item {
+          margin-bottom: $ios-spacing-md;
+          
+          label {
+            display: block;
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+            margin-bottom: $ios-spacing-xs;
+            font-weight: $ios-font-weight-medium;
+          }
+          
+          .slider-container {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+            
+            input[type="range"] {
+              flex: 1;
+              height: 4px;
+              background: $ios-border;
+              border-radius: 2px;
+              outline: none;
+              -webkit-appearance: none;
+              appearance: none;
+              
+              &::-webkit-slider-thumb {
+                  -webkit-appearance: none;
+                  appearance: none;
+                  width: 16px;
+                height: 16px;
+                background: #007AFF;
+                border-radius: 50%;
+                cursor: pointer;
+              }
+              
+              &::-moz-range-thumb {
+                width: 16px;
+                height: 16px;
+                background: #007AFF;
+                border-radius: 50%;
+                cursor: pointer;
+                border: none;
+              }
+            }
+            
+            .slider-value {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-primary;
+              font-weight: $ios-font-weight-medium;
+              min-width: 40px;
+              text-align: right;
+            }
+          }
+
+          // RGB控件样式
+          .rgb-controls {
+            display: flex;
+            flex-direction: column;
+            gap: $ios-spacing-xs;
+            
+            .rgb-slider {
+              display: flex;
+              align-items: center;
+              gap: $ios-spacing-xs;
+              
+              span:first-child {
+                font-size: $ios-font-size-xs;
+                font-weight: $ios-font-weight-semibold;
+                color: $ios-text-secondary;
+                width: 12px;
+              }
+              
+              input[type="range"] {
+                flex: 1;
+                height: 4px;
+                background: $ios-border;
+                border-radius: 2px;
+                outline: none;
+                -webkit-appearance: none;
+                appearance: none;
+                
+                &::-webkit-slider-thumb {
+                  -webkit-appearance: none;
+                  appearance: none;
+                  width: 14px;
+                  height: 14px;
+                  background: #007AFF;
+                  border-radius: 50%;
+                  cursor: pointer;
+                }
+              }
+              
+              span:last-child {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-primary;
+                font-weight: $ios-font-weight-medium;
+                min-width: 30px;
+                text-align: right;
+              }
+            }
+            
+            .color-preview {
+              width: 100%;
+              height: 30px;
+              border-radius: 6px;
+              border: 1px solid $ios-border;
+              margin-top: $ios-spacing-xs;
+            }
+          }
+        }
+      }
+    }
+
+    // 预设氛围样式
+    .atmosphere-presets {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: $ios-spacing-sm;
+      
+      .preset-card {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+        padding: $ios-spacing-sm;
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        background: white;
+        cursor: pointer;
+        transition: all 0.2s ease;
+        
+        &:hover {
+          border-color: #007AFF;
+          background: rgba(0, 122, 255, 0.02);
+        }
+        
+        .preset-color {
+          width: 40px;
+          height: 40px;
+          border-radius: 6px;
+          border: 1px solid $ios-border;
+          flex-shrink: 0;
+        }
+        
+        .preset-info {
+          flex: 1;
+          
+          .preset-name {
+            font-size: $ios-font-size-xs;
+            font-weight: $ios-font-weight-semibold;
+            color: $ios-text-primary;
+            margin-bottom: 2px;
+          }
+          
+          .preset-details {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+            margin-bottom: $ios-spacing-xs;
+          }
+          
+          .preset-materials {
+            display: flex;
+            gap: 4px;
+            flex-wrap: wrap;
+            
+            .material-tag {
+              display: inline-block;
+              background: $ios-background-secondary;
+              color: $ios-text-secondary;
+              padding: 2px 6px;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 进度管理样式
+  .progress-section {
+    .progress-overview {
+      display: flex;
+      gap: 2rem;
+      margin-bottom: 2rem;
+      
+      .progress-stats {
+        flex: 1;
+        display: grid;
+        grid-template-columns: repeat(4, 1fr);
+        gap: 1rem;
+        
+        .stat-item {
+          text-align: center;
+          padding: 1rem;
+          background: #f8f9fa;
+          border-radius: 8px;
+          
+          .stat-number {
+            font-size: 2rem;
+            font-weight: 600;
+            color: #007AFF;
+            margin-bottom: 0.5rem;
+          }
+          
+          .stat-label {
+            font-size: 0.875rem;
+            color: #666;
+          }
+        }
+      }
+      
+      .progress-chart {
+        .chart-container {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          
+          .progress-circle {
+            position: relative;
+            
+            .progress-text {
+              position: absolute;
+              top: 50%;
+              left: 50%;
+              transform: translate(-50%, -50%);
+              text-align: center;
+              
+              .progress-percentage {
+                font-size: 1.5rem;
+                font-weight: 600;
+                color: #34C759;
+              }
+              
+              .progress-label {
+                font-size: 0.75rem;
+                color: #666;
+                margin-top: 0.25rem;
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    .history-section {
+      margin-bottom: 2rem;
+      
+      .section-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 1rem;
+        
+        h5 {
+          margin: 0;
+          color: #333;
+        }
+        
+        .history-controls {
+          display: flex;
+          gap: 1rem;
+          align-items: center;
+          
+          .history-select {
+            padding: 0.5rem;
+            border: 1px solid #ddd;
+            border-radius: 4px;
+            background: white;
+            min-width: 200px;
+          }
+        }
+      }
+      
+      .history-timeline {
+        .timeline-item {
+          display: flex;
+          align-items: flex-start;
+          margin-bottom: 1rem;
+          cursor: pointer;
+          padding: 0.5rem;
+          border-radius: 4px;
+          transition: background-color 0.2s;
+          
+          &:hover {
+            background: #f8f9fa;
+          }
+          
+          .timeline-marker {
+            width: 12px;
+            height: 12px;
+            border-radius: 50%;
+            background: #007AFF;
+            margin-right: 1rem;
+            margin-top: 0.25rem;
+            flex-shrink: 0;
+          }
+          
+          .timeline-content {
+            flex: 1;
+            
+            .timeline-header {
+              display: flex;
+              justify-content: space-between;
+              margin-bottom: 0.25rem;
+              
+              .timeline-time {
+                font-weight: 500;
+                color: #333;
+              }
+              
+              .timeline-author {
+                color: #666;
+                font-size: 0.875rem;
+              }
+            }
+            
+            .timeline-summary {
+              color: #666;
+              font-size: 0.875rem;
+            }
+          }
+        }
+      }
+      
+      .empty-history {
+        text-align: center;
+        padding: 2rem;
+        color: #666;
+        
+        p {
+          margin-bottom: 1rem;
+        }
+      }
+    }
+    
+    .status-distribution {
+      h5 {
+        margin-bottom: 1rem;
+        color: #333;
+      }
+      
+      .status-bars {
+        .status-bar {
+          margin-bottom: 1rem;
+          
+          .status-info {
+            display: flex;
+            justify-content: space-between;
+            margin-bottom: 0.5rem;
+            
+            .status-name {
+              font-weight: 500;
+              color: #333;
+            }
+            
+            .status-count {
+              color: #666;
+            }
+          }
+          
+          .status-progress {
+            height: 8px;
+            background: #e5e5ea;
+            border-radius: 4px;
+            overflow: hidden;
+            
+            .progress-bar {
+              height: 100%;
+              transition: width 0.3s ease;
+              
+              &.confirmed {
+                background: #34C759;
+              }
+              
+              &.pending {
+                background: #FF9500;
+              }
+              
+              &.rejected {
+                background: #FF3B30;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 协作验证部分
+  .collaboration-section {
+    .list-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: $ios-spacing-md;
+      padding-bottom: $ios-spacing-sm;
+      border-bottom: 1px solid $ios-border;
+      
+      h5 {
+        margin: 0;
+        font-size: $ios-font-size-sm;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .list-controls {
+        display: flex;
+        gap: $ios-spacing-sm;
+      }
+    }
+    
+    .requirements-list {
+      .requirement-item {
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        background: white;
+        transition: all 0.2s ease;
+        
+        &.confirmed {
+          border-color: #34C759;
+          background: rgba(52, 199, 89, 0.02);
+        }
+        
+        &.rejected {
+          border-color: #FF3B30;
+          background: rgba(255, 59, 48, 0.02);
+        }
+        
+        &.pending {
+          border-color: #FF9500;
+          background: rgba(255, 149, 0, 0.02);
+        }
+        
+        .requirement-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-sm;
+          
+          .requirement-info {
+            flex: 1;
+            
+            h6 {
+              margin: 0 0 $ios-spacing-xs 0;
+              font-size: $ios-font-size-sm;
+              font-weight: $ios-font-weight-semibold;
+              color: $ios-text-primary;
+            }
+            
+            p {
+              margin: 0 0 $ios-spacing-xs 0;
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+              line-height: 1.4;
+            }
+            
+            .requirement-tags {
+              display: flex;
+              gap: $ios-spacing-xs;
+              flex-wrap: wrap;
+              
+              .tag {
+                display: inline-block;
+                background: $ios-background-secondary;
+                color: $ios-text-secondary;
+                padding: 2px 6px;
+                border-radius: 4px;
+                font-size: $ios-font-size-xs;
+              }
+            }
+          }
+          
+          .requirement-meta {
+            display: flex;
+            flex-direction: column;
+            gap: $ios-spacing-xs;
+            align-items: flex-end;
+            
+            .priority-select {
+              padding: 4px 8px;
+              border: 1px solid $ios-border;
+              border-radius: 4px;
+              font-size: $ios-font-size-xs;
+              background: white;
+              
+              &:focus {
+                outline: none;
+                border-color: #007AFF;
+              }
+            }
+            
+            .status-badge {
+              padding: 2px 8px;
+              border-radius: 12px;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-medium;
+              
+              &.confirmed {
+                background: #34C759;
+                color: white;
+              }
+              
+              &.rejected {
+                background: #FF3B30;
+                color: white;
+              }
+              
+              &.pending {
+                background: #FF9500;
+                color: white;
+              }
+            }
+          }
+        }
+        
+        .requirement-actions {
+          display: flex;
+          gap: $ios-spacing-sm;
+          align-items: center;
+          
+          .unread-indicator {
+            display: inline-block;
+            width: 6px;
+            height: 6px;
+            background: #FF3B30;
+            border-radius: 50%;
+            margin-left: 4px;
+          }
+        }
+        
+        .comments-section {
+          margin-top: $ios-spacing-md;
+          padding-top: $ios-spacing-md;
+          border-top: 1px solid $ios-border;
+          
+          .comments-list {
+            margin-bottom: $ios-spacing-md;
+            
+            .comment-item {
+              padding: $ios-spacing-sm;
+              border: 1px solid $ios-border;
+              border-radius: 6px;
+              margin-bottom: $ios-spacing-sm;
+              background: $ios-background-secondary;
+              
+              &.resolved {
+                opacity: 0.6;
+                background: rgba(52, 199, 89, 0.05);
+              }
+              
+              .comment-header {
+                display: flex;
+                align-items: center;
+                gap: $ios-spacing-sm;
+                margin-bottom: $ios-spacing-xs;
+                
+                .comment-author {
+                  font-size: $ios-font-size-xs;
+                  font-weight: $ios-font-weight-semibold;
+                  color: $ios-text-primary;
+                }
+                
+                .comment-role {
+                  font-size: $ios-font-size-xs;
+                  color: $ios-text-secondary;
+                  background: white;
+                  padding: 1px 4px;
+                  border-radius: 3px;
+                }
+                
+                .comment-time {
+                  font-size: $ios-font-size-xs;
+                  color: $ios-text-tertiary;
+                  margin-left: auto;
+                }
+              }
+              
+              .comment-content {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-primary;
+                line-height: 1.4;
+              }
+            }
+          }
+          
+          .add-comment {
+            display: flex;
+            gap: $ios-spacing-sm;
+            align-items: flex-end;
+            
+            textarea {
+              flex: 1;
+              padding: $ios-spacing-xs;
+              border: 1px solid $ios-border;
+              border-radius: 6px;
+              font-size: $ios-font-size-xs;
+              resize: vertical;
+              min-height: 60px;
+              
+              &:focus {
+                outline: none;
+                border-color: #007AFF;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 进度管理部分
+  .progress-section {
+    .progress-overview {
+      display: grid;
+      grid-template-columns: 1fr auto;
+      gap: $ios-spacing-lg;
+      margin-bottom: $ios-spacing-lg;
+      
+      .progress-stats {
+        display: flex;
+        gap: $ios-spacing-lg;
+        
+        .stat-item {
+          text-align: center;
+          
+          .stat-number {
+            font-size: 24px;
+            font-weight: $ios-font-weight-bold;
+            color: $ios-primary;
+            margin-bottom: $ios-spacing-xs;
+          }
+          
+          .stat-label {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+          }
+        }
+      }
+      
+      .progress-chart {
+        .chart-container {
+          position: relative;
+          
+          .progress-circle {
+            position: relative;
+            
+            .progress-text-center {
+              position: absolute;
+              top: 50%;
+              left: 50%;
+              transform: translate(-50%, -50%);
+              text-align: center;
+              
+              .progress-percentage {
+                font-size: 18px;
+                font-weight: $ios-font-weight-bold;
+                color: $ios-primary;
+              }
+              
+              .progress-label {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    .timeline {
+      h5 {
+        margin: 0 0 $ios-spacing-sm 0;
+        font-size: $ios-font-size-xs;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+      }
+      
+      .timeline-list {
+        .timeline-item {
+          display: flex;
+          align-items: center;
+          gap: $ios-spacing-sm;
+          padding: $ios-spacing-sm 0;
+          border-bottom: 1px dashed $ios-border;
+          
+          &:last-child {
+            border-bottom: none;
+          }
+          
+          .timeline-marker {
+            width: 8px;
+            height: 8px;
+            border-radius: 50%;
+            background: $ios-border;
+            flex-shrink: 0;
+          }
+          
+          &.status-confirmed .timeline-marker {
+            background: #34C759;
+          }
+          
+          &.status-rejected .timeline-marker {
+            background: #FF3B30;
+          }
+          
+          &.status-pending .timeline-marker {
+            background: #007AFF;
+          }
+          
+          .timeline-content {
+            .timeline-title {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-primary;
+              font-weight: $ios-font-weight-medium;
+            }
+            
+            .timeline-status {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 通用按钮样式
+  .btn-primary, .btn-success, .btn-ghost {
+    border: none;
+    border-radius: 6px;
+    font-size: $ios-font-size-xs;
+    font-weight: $ios-font-weight-medium;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    
+    &.btn-sm {
+      padding: 6px 12px;
+    }
+    
+    &.btn-xs {
+      padding: 4px 8px;
+    }
+    
+    &:disabled {
+      opacity: 0.5;
+      cursor: not-allowed;
+    }
+  }
+  
+  .btn-primary {
+    background: $ios-primary;
+    color: white;
+    
+    &:hover:not(:disabled) {
+      background: darken($ios-primary, 10%);
+    }
+  }
+  
+  .btn-success {
+    background: #34C759;
+    color: white;
+    
+    &:hover:not(:disabled) {
+      background: darken(#34C759, 10%);
+    }
+  }
+  
+  .btn-ghost {
+    background: transparent;
+    color: $ios-text-secondary;
+    border: 1px solid $ios-border;
+    
+    &:hover:not(:disabled) {
+      background: $ios-background-secondary;
+      color: $ios-text-primary;
+    }
+  }
+}

+ 799 - 0
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.ts

@@ -0,0 +1,799 @@
+import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+// 素材文件接口
+interface MaterialFile {
+  id: string;
+  name: string;
+  type: 'text' | 'image' | 'cad';
+  url?: string;
+  content?: string;
+  size?: string;
+  uploadTime: Date;
+  analysis?: MaterialAnalysis;
+}
+
+// 素材解析结果接口
+interface MaterialAnalysis {
+  // 文本类解析
+  atmosphere?: { description: string; rgb?: string; colorTemp?: string };
+  residents?: { type: string; details: string };
+  scenes?: { preference: string; requirements: string };
+  keywords?: { positive: string[]; negative: string[] };
+  
+  // 图片类解析
+  mainColors?: { rgb: string; percentage: number }[];
+  colorTemperature?: string;
+  materialRatio?: { material: string; percentage: number }[];
+  spaceRatio?: number;
+  
+  // CAD类解析
+  structuralElements?: { type: string; position: string; changeable: boolean }[];
+  spaceMetrics?: { room: string; ratio: string; width: string }[];
+  flowMetrics?: { area: string; width: string; compliance: boolean }[];
+}
+
+// 需求指标接口
+interface RequirementMetric {
+  id: string;
+  category: 'color' | 'space' | 'material';
+  name: string;
+  value: any;
+  unit?: string;
+  range?: { min: number; max: number };
+  description?: string;
+}
+
+// 协作评论接口
+interface CollaborationComment {
+  id: string;
+  author: string;
+  role: 'customer-service' | 'designer' | 'client';
+  content: string;
+  timestamp: Date;
+  requirementId?: string;
+  status: 'pending' | 'resolved';
+}
+
+// 需求项接口
+interface RequirementItem {
+  id: string;
+  title: string;
+  description: string;
+  priority: 'high' | 'medium' | 'low';
+  status: 'pending' | 'confirmed' | 'rejected';
+  tags?: string[];
+  metrics?: RequirementMetric[];
+  comments?: CollaborationComment[];
+  lastUpdated: Date;
+  showComments?: boolean;
+}
+
+@Component({
+  selector: 'app-requirements-confirm-card',
+  standalone: true,
+  imports: [CommonModule, FormsModule, ReactiveFormsModule],
+  templateUrl: './requirements-confirm-card.html',
+  styleUrls: ['./requirements-confirm-card.scss']
+})
+export class RequirementsConfirmCardComponent implements OnInit {
+  @Input() projectId?: string;
+  @Output() requirementConfirmed = new EventEmitter<RequirementItem>();
+  @Output() progressUpdated = new EventEmitter<number>();
+
+  // 表单
+  materialForm!: FormGroup;
+  commentForm!: FormGroup;
+  materialUploadForm!: FormGroup;
+
+  // 数据
+  materialFiles: MaterialFile[] = [];
+  materials: MaterialFile[] = [];
+  requirementMetrics: RequirementMetric[] = [];
+  collaborationComments: CollaborationComment[] = [];
+  requirementItems: RequirementItem[] = [];
+
+  // 状态
+  activeTab: 'materials' | 'mapping' | 'collaboration' | 'progress' = 'materials';
+  isUploading = false;
+  isAnalyzing = false;
+  dragOver = false;
+  showConsistencyWarning = false;
+  warningMessage = '';
+
+  // 需求指标
+  colorIndicators = {
+    mainColor: { r: 255, g: 230, b: 180 },
+    colorTemperature: 2700,
+    colorRange: '暖色调',
+    saturation: 70,
+    brightness: 80,
+    contrast: 60
+  };
+
+  spaceIndicators = {
+    lineRatio: 60,
+    blankRatio: 30,
+    flowWidth: 0.9,
+    aspectRatio: 1.6,
+    ceilingHeight: 2.8,
+    lightingLevel: 300
+  };
+
+  materialIndicators = {
+    fabricRatio: 50,
+    woodRatio: 30,
+    metalRatio: 20,
+    smoothness: 7,
+    glossiness: 4,
+    texture: 6
+  };
+
+  // 指标范围配置
+  indicatorRanges = {
+    color: {
+      r: { min: 0, max: 255 },
+      g: { min: 0, max: 255 },
+      b: { min: 0, max: 255 },
+      temperature: { min: 2000, max: 6500 },
+      saturation: { min: 0, max: 100 },
+      brightness: { min: 0, max: 100 },
+      contrast: { min: 0, max: 100 }
+    },
+    space: {
+      lineRatio: { min: 0, max: 100 },
+      blankRatio: { min: 10, max: 80 },
+      flowWidth: { min: 0.6, max: 1.5 },
+      aspectRatio: { min: 1.0, max: 3.0 },
+      ceilingHeight: { min: 2.4, max: 4.0 },
+      lightingLevel: { min: 100, max: 800 }
+    },
+    material: {
+      fabricRatio: { min: 0, max: 100 },
+      woodRatio: { min: 0, max: 100 },
+      metalRatio: { min: 0, max: 100 },
+      smoothness: { min: 1, max: 10 },
+      glossiness: { min: 1, max: 10 },
+      texture: { min: 1, max: 10 }
+    }
+  };
+
+  // 预设数据
+  presetAtmospheres = [
+    { name: '温馨暖调', rgb: '255,230,180', colorTemp: '2700-3000K', materials: ['木质', '布艺'] },
+    { name: '现代冷调', rgb: '200,220,240', colorTemp: '5000-6000K', materials: ['金属', '玻璃'] },
+    { name: '自然清新', rgb: '220,240,220', colorTemp: '4000-4500K', materials: ['竹木', '石材'] }
+  ];
+
+  // 一致性检查
+  consistencyWarnings: string[] = [];
+  historyStates: any[] = [];
+
+  constructor(private fb: FormBuilder) {}
+
+  ngOnInit() {
+    this.initializeForms();
+    this.initializeRequirements();
+    this.loadPresetMetrics();
+  }
+
+  private initializeForms() {
+    this.materialForm = this.fb.group({
+      textContent: ['', Validators.required],
+      atmosphereDescription: [''],
+      residentInfo: [''],
+      scenePreferences: [''],
+      title: [''],
+      content: ['']
+    });
+
+    this.commentForm = this.fb.group({
+      content: ['', Validators.required],
+      requirementId: ['']
+    });
+
+    this.materialUploadForm = this.fb.group({
+      file: [''],
+      textContent: ['', Validators.required]
+    });
+  }
+
+  private initializeRequirements() {
+    this.requirementItems = [
+      {
+        id: 'color-atmosphere',
+        title: '色彩氛围',
+        description: '整体色调和氛围营造',
+        priority: 'high',
+        status: 'pending',
+        lastUpdated: new Date()
+      },
+      {
+        id: 'space-layout',
+        title: '空间布局',
+        description: '功能分区和动线设计',
+        priority: 'high',
+        status: 'pending',
+        lastUpdated: new Date()
+      },
+      {
+        id: 'material-selection',
+        title: '材质选择',
+        description: '主要材质和质感要求',
+        priority: 'medium',
+        status: 'pending',
+        lastUpdated: new Date()
+      }
+    ];
+  }
+
+  private loadPresetMetrics() {
+    this.requirementMetrics = [
+      {
+        id: 'main-color',
+        category: 'color',
+        name: '主色调',
+        value: { r: 255, g: 230, b: 180 },
+        description: '空间主要色彩'
+      },
+      {
+        id: 'color-temp',
+        category: 'color',
+        name: '色温',
+        value: 3000,
+        unit: 'K',
+        range: { min: 2700, max: 6500 },
+        description: '照明色温范围'
+      },
+      {
+        id: 'wood-ratio',
+        category: 'material',
+        name: '木质占比',
+        value: 60,
+        unit: '%',
+        range: { min: 0, max: 100 },
+        description: '木质材料使用比例'
+      }
+    ];
+  }
+
+  // 素材上传功能
+  onFileSelected(event: Event, type: 'text' | 'image' | 'cad') {
+    const input = event.target as HTMLInputElement;
+    if (input.files) {
+      Array.from(input.files).forEach(file => {
+        this.uploadFile(file, type);
+      });
+    }
+  }
+
+  onFileDrop(event: DragEvent, type: 'text' | 'image' | 'cad') {
+    event.preventDefault();
+    this.dragOver = false;
+    
+    if (event.dataTransfer?.files) {
+      Array.from(event.dataTransfer.files).forEach(file => {
+        this.uploadFile(file, type);
+      });
+    }
+  }
+
+  onDragOver(event: DragEvent) {
+    event.preventDefault();
+    this.dragOver = true;
+  }
+
+  onDragLeave(event: DragEvent) {
+    event.preventDefault();
+    this.dragOver = false;
+  }
+
+  private uploadFile(file: File, type: 'text' | 'image' | 'cad') {
+    this.isUploading = true;
+    
+    // 模拟文件上传
+    setTimeout(() => {
+      const materialFile: MaterialFile = {
+        id: this.generateId(),
+        name: file.name,
+        type: type,
+        url: URL.createObjectURL(file),
+        size: this.formatFileSize(file.size),
+        uploadTime: new Date()
+      };
+
+      this.materialFiles.push(materialFile);
+      this.materials.push(materialFile);
+      this.isUploading = false;
+      
+      // 自动解析
+      this.analyzeMaterial(materialFile);
+    }, 1000);
+  }
+
+  // 文本提交
+  onTextSubmit(): void {
+    if (this.materialUploadForm.valid) {
+      const formValue = this.materialUploadForm.value;
+      const textMaterial: MaterialFile = {
+        id: this.generateId(),
+        name: '文本需求',
+        type: 'text',
+        content: formValue.textContent,
+        uploadTime: new Date(),
+        size: this.formatFileSize(formValue.textContent?.length || 0)
+      };
+      
+      this.materialFiles.push(textMaterial);
+      this.materials.push(textMaterial);
+      this.analyzeMaterial(textMaterial);
+      this.materialUploadForm.reset();
+    }
+  }
+
+  // 素材解析功能
+  private analyzeMaterial(material: MaterialFile) {
+    this.isAnalyzing = true;
+    
+    // 模拟解析过程
+    setTimeout(() => {
+      let analysis: MaterialAnalysis = {};
+      
+      switch (material.type) {
+        case 'text':
+          analysis = this.analyzeTextMaterial(material);
+          break;
+        case 'image':
+          analysis = this.analyzeImageMaterial(material);
+          break;
+        case 'cad':
+          analysis = this.analyzeCADMaterial(material);
+          break;
+      }
+      
+      material.analysis = analysis;
+      this.isAnalyzing = false;
+      this.updateRequirementsFromAnalysis(analysis);
+    }, 2000);
+  }
+
+  private analyzeTextMaterial(material: MaterialFile): MaterialAnalysis {
+    return {
+      atmosphere: {
+        description: '温馨暖调',
+        rgb: '255,230,180',
+        colorTemp: '2700-3000K'
+      },
+      residents: {
+        type: '亲子家庭',
+        details: '孩子年龄3-6岁'
+      },
+      scenes: {
+        preference: '瑜伽爱好者',
+        requirements: '需要瑜伽区收纳'
+      },
+      keywords: {
+        positive: ['科技感', '温馨', '实用'],
+        negative: ['复杂线条', '冷色调']
+      }
+    };
+  }
+
+  private analyzeImageMaterial(material: MaterialFile): MaterialAnalysis {
+    return {
+      mainColors: [
+        { rgb: '255,230,180', percentage: 45 },
+        { rgb: '200,180,160', percentage: 30 },
+        { rgb: '180,160,140', percentage: 25 }
+      ],
+      colorTemperature: '3000K',
+      materialRatio: [
+        { material: '木质', percentage: 60 },
+        { material: '布艺', percentage: 25 },
+        { material: '金属', percentage: 15 }
+      ],
+      spaceRatio: 35
+    };
+  }
+
+  private analyzeCADMaterial(material: MaterialFile): MaterialAnalysis {
+    return {
+      structuralElements: [
+        { type: '承重柱', position: '客厅中央', changeable: false },
+        { type: '门窗', position: '南墙', changeable: false }
+      ],
+      spaceMetrics: [
+        { room: '客厅', ratio: '16:9', width: '4.2m' },
+        { room: '卧室', ratio: '4:3', width: '3.6m' }
+      ],
+      flowMetrics: [
+        { area: '主通道', width: '1.2m', compliance: true },
+        { area: '次通道', width: '0.8m', compliance: false }
+      ]
+    };
+  }
+
+  private updateRequirementsFromAnalysis(analysis: MaterialAnalysis) {
+    if (analysis.atmosphere?.rgb) {
+      const colorMetric = this.requirementMetrics.find(m => m.id === 'main-color');
+      if (colorMetric) {
+        const [r, g, b] = analysis.atmosphere.rgb.split(',').map(Number);
+        colorMetric.value = { r, g, b };
+      }
+    }
+    
+    if (analysis.materialRatio) {
+      const woodRatio = analysis.materialRatio.find(m => m.material === '木质');
+      if (woodRatio) {
+        const woodMetric = this.requirementMetrics.find(m => m.id === 'wood-ratio');
+        if (woodMetric) {
+          woodMetric.value = woodRatio.percentage;
+        }
+      }
+    }
+  }
+
+  // 需求确认功能
+  confirmRequirement(requirementId: string) {
+    const requirement = this.requirementItems.find(r => r.id === requirementId);
+    if (requirement) {
+      requirement.status = 'confirmed';
+      requirement.lastUpdated = new Date();
+      this.requirementConfirmed.emit(requirement);
+      this.updateProgress();
+    }
+  }
+
+  rejectRequirement(requirementId: string, reason?: string) {
+    const requirement = this.requirementItems.find(r => r.id === requirementId);
+    if (requirement) {
+      requirement.status = 'rejected';
+      requirement.lastUpdated = new Date();
+      
+      if (reason) {
+        this.addCommentToRequirement(requirementId, reason, 'system');
+      }
+      
+      this.updateProgress();
+    }
+  }
+
+  // 协作功能
+  addComment() {
+    if (this.commentForm.valid) {
+      const comment: CollaborationComment = {
+        id: this.generateId(),
+        author: '当前用户',
+        role: 'designer',
+        content: this.commentForm.value.content,
+        timestamp: new Date(),
+        requirementId: this.commentForm.value.requirementId,
+        status: 'pending'
+      };
+      
+      this.collaborationComments.push(comment);
+      this.commentForm.reset();
+    }
+  }
+
+  resolveComment(commentId: string) {
+    const comment = this.collaborationComments.find(c => c.id === commentId);
+    if (comment) {
+      comment.status = 'resolved';
+    }
+  }
+
+  // 进度管理
+  private updateProgress() {
+    const totalRequirements = this.requirementItems.length;
+    const confirmedRequirements = this.requirementItems.filter(r => r.status === 'confirmed').length;
+    const progress = totalRequirements > 0 ? (confirmedRequirements / totalRequirements) * 100 : 0;
+    this.progressUpdated.emit(progress);
+  }
+
+  getProgressPercentage(): number {
+    if (this.requirementItems.length === 0) return 0;
+    const confirmedCount = this.requirementItems.filter(r => r.status === 'confirmed').length;
+    return Math.round((confirmedCount / this.requirementItems.length) * 100);
+  }
+
+  // 处理优先级更新
+  onPriorityChange(requirementId: string, event: Event): void {
+    const target = event.target as HTMLSelectElement;
+    this.updateRequirementPriority(requirementId, target.value as 'high' | 'medium' | 'low');
+  }
+
+  // 检查需求是否有待处理评论
+  hasUnreadComments(requirementId: string): boolean {
+    return this.getCommentsForRequirement(requirementId).some(c => c.status === 'pending');
+  }
+
+  // 处理历史状态选择
+  onHistoryStateChange(event: Event): void {
+    const target = event.target as HTMLSelectElement;
+    const index = +target.value;
+    if (index >= 0) {
+      this.restoreHistoryState(index);
+    }
+  }
+
+  // 获取指定状态的需求数量
+  getRequirementCountByStatus(status: 'confirmed' | 'pending' | 'rejected'): number {
+    return (this.requirementItems || []).filter(r => r.status === status).length;
+  }
+
+  // 获取状态百分比
+  getStatusPercentage(status: 'confirmed' | 'pending' | 'rejected'): number {
+    const total = this.requirementItems.length;
+    if (total === 0) return 0;
+    
+    const statusCount = this.getRequirementCountByStatus(status);
+    return Math.round((statusCount / total) * 100);
+  }
+
+  // 指标映射功能
+  updateMetricValue(metricId: string, value: any) {
+    const metric = this.requirementMetrics.find(m => m.id === metricId);
+    if (metric) {
+      metric.value = value;
+      this.checkConsistency();
+    }
+  }
+
+  // 指标更新方法
+  updateColorIndicator(type: string, value?: any): void {
+    switch (type) {
+      case 'mainColor':
+        if (value && typeof value === 'object' && 'r' in value) {
+          this.colorIndicators.mainColor = value;
+        }
+        break;
+      case 'colorTemperature':
+        // 色温更新时不需要改变颜色值,只是触发一致性检查
+        break;
+      case 'saturation':
+        // 饱和度更新时不需要改变颜色值,只是触发一致性检查
+        break;
+    }
+    
+    console.log(`更新颜色指示器 ${type}:`, value);
+    this.checkConsistency();
+    this.updatePreview();
+  }
+
+  updateSpaceIndicator(key: string, value: number) {
+    if (key in this.spaceIndicators) {
+      (this.spaceIndicators as any)[key] = value;
+      this.checkConsistency();
+    }
+  }
+
+  updateMaterialIndicator(key: string, value: number) {
+    if (key in this.materialIndicators) {
+      (this.materialIndicators as any)[key] = value;
+      this.checkConsistency();
+      this.normalizeMaterialRatios();
+    }
+  }
+
+  // 材质比例归一化
+  private normalizeMaterialRatios() {
+    const total = this.materialIndicators.fabricRatio + 
+                  this.materialIndicators.woodRatio + 
+                  this.materialIndicators.metalRatio;
+    
+    if (total > 100) {
+      const scale = 100 / total;
+      this.materialIndicators.fabricRatio = Math.round(this.materialIndicators.fabricRatio * scale);
+      this.materialIndicators.woodRatio = Math.round(this.materialIndicators.woodRatio * scale);
+      this.materialIndicators.metalRatio = 100 - this.materialIndicators.fabricRatio - this.materialIndicators.woodRatio;
+    }
+  }
+
+  // 预览更新
+  private updatePreview() {
+    const { r, g, b } = this.colorIndicators.mainColor;
+    const previewElement = document.querySelector('.color-preview') as HTMLElement;
+    if (previewElement) {
+      previewElement.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
+    }
+  }
+
+  // 获取RGB字符串
+  getRgbString(): string {
+    const { r, g, b } = this.colorIndicators.mainColor;
+    return `rgb(${r}, ${g}, ${b})`;
+  }
+
+  // 获取色温描述
+  getColorTemperatureDescription(): string {
+    const temp = this.colorIndicators.colorTemperature;
+    if (temp < 3000) return '暖色调';
+    if (temp < 4000) return '中性色调';
+    if (temp < 5000) return '自然色调';
+    return '冷色调';
+  }
+
+  applyPresetAtmosphere(preset: any) {
+    const rgbMatch = preset.rgb.match(/(\d+),(\d+),(\d+)/);
+    if (rgbMatch) {
+      this.colorIndicators.mainColor = {
+        r: parseInt(rgbMatch[1]),
+        g: parseInt(rgbMatch[2]),
+        b: parseInt(rgbMatch[3])
+      };
+    }
+
+    const tempMatch = preset.colorTemp.match(/(\d+)/);
+    if (tempMatch) {
+      this.colorIndicators.colorTemperature = parseInt(tempMatch[1]);
+    }
+
+    if (preset.materials.includes('木质')) {
+      this.materialIndicators.woodRatio = 60;
+      this.materialIndicators.fabricRatio = 30;
+      this.materialIndicators.metalRatio = 10;
+    } else if (preset.materials.includes('金属')) {
+      this.materialIndicators.metalRatio = 50;
+      this.materialIndicators.woodRatio = 20;
+      this.materialIndicators.fabricRatio = 30;
+    }
+
+    this.updatePreview();
+    this.checkConsistency();
+  }
+
+  // 一致性检查
+  private checkConsistency() {
+    this.consistencyWarnings = [];
+    
+    if (this.colorIndicators.colorTemperature < 3000 && 
+        this.colorIndicators.mainColor.r > 200 && 
+        this.colorIndicators.mainColor.g < 150) {
+      this.consistencyWarnings.push('暖调氛围与冷色调RGB值存在矛盾');
+    }
+    
+    if (this.materialIndicators.fabricRatio > 70 && this.colorIndicators.colorTemperature > 5000) {
+      this.consistencyWarnings.push('高布艺占比与冷色调可能不协调');
+    }
+    
+    if (this.spaceIndicators.lineRatio > 80 && this.materialIndicators.woodRatio > 60) {
+      this.consistencyWarnings.push('高直线条占比与高木质占比可能过于刚硬');
+    }
+  }
+
+  // 协作批注功能
+  addCommentToRequirement(requirementId: string, content: string, author: string = '当前用户'): void {
+    const comment: CollaborationComment = {
+      id: this.generateId(),
+      author,
+      role: 'designer',
+      content,
+      timestamp: new Date(),
+      requirementId,
+      status: 'pending'
+    };
+    
+    this.collaborationComments.push(comment);
+    this.updateProgress();
+  }
+
+  // 优先级排序功能
+  updateRequirementPriority(requirementId: string, priority: 'high' | 'medium' | 'low'): void {
+    const requirement = this.requirementItems.find(r => r.id === requirementId);
+    if (requirement) {
+      requirement.priority = priority;
+      requirement.lastUpdated = new Date();
+      this.sortRequirementsByPriority();
+    }
+  }
+
+  sortRequirementsByPriority(): void {
+    const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 };
+    this.requirementItems.sort((a, b) => {
+      return priorityOrder[b.priority] - priorityOrder[a.priority];
+    });
+  }
+
+  // 历史记录功能
+  saveCurrentState(): void {
+    const currentState = {
+      timestamp: new Date(),
+      colorIndicators: { ...this.colorIndicators },
+      spaceIndicators: { ...this.spaceIndicators },
+      materialIndicators: { ...this.materialIndicators },
+      requirementItems: this.requirementItems.map((r: RequirementItem) => ({ ...r })),
+      author: '当前用户'
+    };
+    
+    this.historyStates.push(currentState);
+    
+    if (this.historyStates.length > 10) {
+      this.historyStates.shift();
+    }
+  }
+
+  restoreHistoryState(index: number): void {
+    if (index >= 0 && index < this.historyStates.length) {
+      const state = this.historyStates[index];
+      this.colorIndicators = { ...state.colorIndicators };
+      this.spaceIndicators = { ...state.spaceIndicators };
+      this.materialIndicators = { ...state.materialIndicators };
+      this.requirementItems = state.requirementItems.map((r: RequirementItem) => ({ ...r }));
+      
+      this.updatePreview();
+      this.checkConsistency();
+    }
+  }
+
+  // 获取未解决的评论数量
+  getUnresolvedCommentsCount(): number {
+    return this.collaborationComments.filter(c => c.status === 'pending').length;
+  }
+
+  // 获取需求的评论
+  getCommentsForRequirement(requirementId: string): CollaborationComment[] {
+    return this.collaborationComments.filter(comment => comment.requirementId === requirementId) || [];
+  }
+
+  // 获取状态样式类
+  getStatusClass(status: string): string {
+    return status;
+  }
+
+  // 工具方法
+  private generateId(): string {
+    return Math.random().toString(36).substr(2, 9);
+  }
+
+  private formatFileSize(bytes: number): string {
+    if (bytes === 0) return '0 Bytes';
+    const k = 1024;
+    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
+    const i = Math.floor(Math.log(bytes) / Math.log(k));
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+  }
+
+  // 删除素材
+  removeMaterial(materialId: string) {
+    const index = this.materialFiles.findIndex(m => m.id === materialId);
+    if (index > -1) {
+      const material = this.materialFiles[index];
+      if (material.url) {
+        URL.revokeObjectURL(material.url);
+      }
+      this.materialFiles.splice(index, 1);
+    }
+    
+    const materialsIndex = this.materials.findIndex(m => m.id === materialId);
+    if (materialsIndex > -1) {
+      this.materials.splice(materialsIndex, 1);
+    }
+  }
+
+  // 切换标签页
+  switchTab(tab: 'materials' | 'mapping' | 'collaboration' | 'progress') {
+    this.activeTab = tab;
+  }
+
+  // 获取需求状态文本
+  getRequirementStatusText(status: string): string {
+    const statusMap: { [key: string]: string } = {
+      'pending': '待确认',
+      'confirmed': '已确认',
+      'rejected': '已拒绝'
+    };
+    return statusMap[status] || status;
+  }
+
+  // 获取优先级文本
+  getPriorityText(priority: string): string {
+    const priorityMap: { [key: string]: string } = {
+      'high': '高',
+      'medium': '中',
+      'low': '低'
+    };
+    return priorityMap[priority] || priority;
+  }
+}

+ 12 - 0
src/app/shared/components/requirements-talk-card/requirements-talk-card.html

@@ -0,0 +1,12 @@
+<div class="info-card requirements-talk-card">
+  <h4>需求沟通清单</h4>
+  @if (checklist && checklist.length > 0) {
+    <ul class="checklist">
+      @for (item of checklist; track item) {
+        <li>{{ item }}</li>
+      }
+    </ul>
+  } @else {
+    <div class="empty">暂无清单</div>
+  }
+</div>

+ 11 - 0
src/app/shared/components/requirements-talk-card/requirements-talk-card.scss

@@ -0,0 +1,11 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { display: block; height: 100%; }
+
+.requirements-talk-card {
+  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
+  .checklist { margin: 0; padding-left: 0; list-style: none; }
+  .checklist li { padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; color: $ios-text-primary; }
+  .checklist li:last-child { border-bottom: none; }
+  .empty { color: $ios-text-secondary; font-size: $ios-font-size-sm; padding: $ios-spacing-sm 0; }
+}

+ 13 - 0
src/app/shared/components/requirements-talk-card/requirements-talk-card.ts

@@ -0,0 +1,13 @@
+import { Component, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+@Component({
+  selector: 'app-requirements-talk-card',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './requirements-talk-card.html',
+  styleUrls: ['./requirements-talk-card.scss']
+})
+export class RequirementsTalkCardComponent {
+  @Input() checklist: string[] = [];
+}

+ 17 - 0
src/app/shared/components/settlement-card/settlement-card.html

@@ -0,0 +1,17 @@
+<div class="info-card settlement-card">
+  <h4>尾款结算</h4>
+  @if (settlements && settlements.length > 0) {
+    <ul class="items">
+      @for (s of settlements; track s.id) {
+        <li class="item">
+          <span class="name">{{ s.projectName || '结算项' }}</span>
+          <span class="amount">¥{{ s.amount | number:'1.0-2' }}</span>
+          <span class="status">{{ s.status }}</span>
+          <span class="date" *ngIf="s.settledAt as t">{{ t | date:'yyyy-MM-dd' }}</span>
+        </li>
+      }
+    </ul>
+  } @else {
+    <div class="empty">暂无结算信息</div>
+  }
+</div>

+ 14 - 0
src/app/shared/components/settlement-card/settlement-card.scss

@@ -0,0 +1,14 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { display: block; height: 100%; }
+
+.settlement-card {
+  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
+  .items { margin: 0; padding-left: 0; list-style: none; }
+  .item { display: grid; grid-template-columns: 1fr auto auto auto; gap: $ios-spacing-xs; padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; }
+  .item:last-child { border-bottom: none; }
+  .name { font-weight: $ios-font-weight-medium; color: $ios-text-primary; }
+  .amount { color: $ios-primary; }
+  .status { font-size: $ios-font-size-xs; color: $ios-text-secondary; }
+  .date { font-size: $ios-font-size-xs; color: $ios-text-secondary; }
+}

+ 14 - 0
src/app/shared/components/settlement-card/settlement-card.ts

@@ -0,0 +1,14 @@
+import { Component, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Settlement } from '../../../models/project.model';
+
+@Component({
+  selector: 'app-settlement-card',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './settlement-card.html',
+  styleUrls: ['./settlement-card.scss']
+})
+export class SettlementCardComponent {
+  @Input() settlements: Settlement[] = [];
+}